From 680297374347555fd7e7302af080dfffae9e9072 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Thu, 30 Jul 2015 17:49:32 +0300 Subject: [PATCH] Refactor: merge duplicated code for code fragment analysis Introduce CodeFragmentAnalyzer --- .../jetbrains/kotlin/analyzer/AnalyzerUtil.kt | 10 -- .../resolve/util/codeFragmentResolveUtils.kt | 86 ------------- .../caches/resolve/CodeFragmentAnalyzer.kt | 120 ++++++++++++++++++ .../idea/caches/resolve/KotlinResolveCache.kt | 25 +--- .../idea/project/ResolveElementCache.kt | 25 ++-- 5 files changed, 134 insertions(+), 132 deletions(-) delete mode 100644 idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerUtil.kt index 506233982e5..cda434dded7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerUtil.kt @@ -54,16 +54,6 @@ public fun JetExpression.analyzeInContext( return trace.getBindingContext() } -public fun JetExpression.computeTypeInContext( - scope: JetScope, - trace: BindingTrace = BindingTraceContext(), - dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY, - expectedType: JetType = TypeUtils.NO_EXPECTED_TYPE, - module: ModuleDescriptor = scope.getModule() -): JetType { - return computeTypeInfoInContext(scope, trace, dataFlowInfo, expectedType, module).type.safeType(this) -} - public fun JetType?.safeType(expression: JetExpression): JetType { if (this != null) return this diff --git a/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt deleted file mode 100644 index 9dc91eb9da8..00000000000 --- a/idea/ide-common/src/org/jetbrains/kotlin/resolve/util/codeFragmentResolveUtils.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.resolve.util - -import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.parents -import org.jetbrains.kotlin.psi.psiUtil.siblings -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer -import org.jetbrains.kotlin.resolve.scopes.ChainedScope -import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull - -//TODO: this code should be moved into debugger which should set correct context for its code fragment -private fun JetExpression.correctContextForExpression(): JetExpression { - return when (this) { - is JetProperty -> this.getDelegateExpressionOrInitializer() - is JetFunctionLiteral -> this.getBodyExpression()?.getStatements()?.lastOrNull() - is JetDeclarationWithBody -> this.getBodyExpression() - is JetBlockExpression -> this.getStatements().lastOrNull() - else -> { - this.siblings(forward = false, withItself = false).firstIsInstanceOrNull() - ?: this.parents.firstIsInstanceOrNull() - } - } ?: this -} - -public fun JetCodeFragment.getScopeAndDataFlowForAnalyzeFragment( - resolveSession: KotlinCodeAnalyzer, - resolveToElement: (JetElement) -> BindingContext -): Pair? { - val context = getContext() - if (context !is JetExpression) return null - - val scopeForContextElement: JetScope? - val dataFlowInfo: DataFlowInfo - - when (context) { - is JetClassOrObject -> { - val descriptor = resolveSession.getClassDescriptor(context) as ClassDescriptorWithResolutionScopes - - scopeForContextElement = descriptor.getScopeForMemberDeclarationResolution() - dataFlowInfo = DataFlowInfo.EMPTY - } - is JetExpression -> { - val correctedContext = context.correctContextForExpression() - - val contextForElement = resolveToElement(correctedContext) - - scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext] - dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext) - } - is JetFile -> { - scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context) - dataFlowInfo = DataFlowInfo.EMPTY - } - else -> return null - } - - if (scopeForContextElement == null) return null - - val codeFragmentScope = resolveSession.getFileScopeProvider().getFileScope(this) - val chainedScope = ChainedScope( - scopeForContextElement.getContainingDeclaration(), - "Scope for resolve code fragment", - scopeForContextElement, codeFragmentScope) - - return chainedScope to dataFlowInfo -} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt new file mode 100644 index 00000000000..3c0a79b5a99 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/CodeFragmentAnalyzer.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.caches.resolve + +import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes +import org.jetbrains.kotlin.idea.project.ResolveElementCache +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.lazy.ResolveSession +import org.jetbrains.kotlin.resolve.scopes.ChainedScope +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import javax.inject.Inject +import kotlin.properties.Delegates + +public class CodeFragmentAnalyzer( + private val resolveSession: ResolveSession, + private val expressionTypingServices: ExpressionTypingServices +) { + + // component dependency cycle + public var resolveElementCache: ResolveElementCache? = null + @Inject set + + public fun analyzeCodeFragment(codeFragment: JetCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) { + val codeFragmentExpression = codeFragment.getContentElement() + if (codeFragmentExpression !is JetExpression) return + + val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) { + resolveElementCache!!.resolveToElement(it, bodyResolveMode) + } ?: return + + expressionTypingServices.getTypeInfo( + scopeForContextElement, + codeFragmentExpression, + TypeUtils.NO_EXPECTED_TYPE, + dataFlowInfo, + trace + ) + } + + //TODO: this code should be moved into debugger which should set correct context for its code fragment + private fun JetExpression.correctContextForExpression(): JetExpression { + return when (this) { + is JetProperty -> this.getDelegateExpressionOrInitializer() + is JetFunctionLiteral -> this.getBodyExpression()?.getStatements()?.lastOrNull() + is JetDeclarationWithBody -> this.getBodyExpression() + is JetBlockExpression -> this.getStatements().lastOrNull() + else -> { + this.siblings(forward = false, withItself = false).firstIsInstanceOrNull() + ?: this.parents.firstIsInstanceOrNull() + } + } ?: this + } + + private fun getScopeAndDataFlowForAnalyzeFragment( + codeFragment: JetCodeFragment, + resolveToElement: (JetElement) -> BindingContext + ): Pair? { + val context = codeFragment.getContext() + if (context !is JetExpression) return null + + val scopeForContextElement: JetScope? + val dataFlowInfo: DataFlowInfo + + when (context) { + is JetClassOrObject -> { + val descriptor = resolveSession.getClassDescriptor(context) as ClassDescriptorWithResolutionScopes + + scopeForContextElement = descriptor.getScopeForMemberDeclarationResolution() + dataFlowInfo = DataFlowInfo.EMPTY + } + is JetExpression -> { + val correctedContext = context.correctContextForExpression() + + val contextForElement = resolveToElement(correctedContext) + + scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext] + dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext) + } + is JetFile -> { + scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context) + dataFlowInfo = DataFlowInfo.EMPTY + } + else -> return null + } + + if (scopeForContextElement == null) return null + + val codeFragmentScope = resolveSession.getFileScopeProvider().getFileScope(codeFragment) + val chainedScope = ChainedScope( + scopeForContextElement.getContainingDeclaration(), + "Scope for resolve code fragment", + scopeForContextElement, codeFragmentScope) + + return chainedScope to dataFlowInfo + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinResolveCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinResolveCache.kt index 3414a300c9f..ed2b8bc9877 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinResolveCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinResolveCache.kt @@ -27,7 +27,6 @@ import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.containers.SLRUCache import org.jetbrains.kotlin.analyzer.AnalysisResult -import org.jetbrains.kotlin.analyzer.analyzeInContext import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.container.ComponentProvider import org.jetbrains.kotlin.container.get @@ -37,15 +36,12 @@ import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve -import org.jetbrains.kotlin.idea.project.ResolveElementCache import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.ResolveSession -import org.jetbrains.kotlin.resolve.util.getScopeAndDataFlowForAnalyzeFragment -import org.jetbrains.kotlin.types.TypeUtils import java.util.HashMap public interface CacheExtension { @@ -283,21 +279,12 @@ private object KotlinResolveDataProvider { } private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: JetCodeFragment): BindingContext { - val codeFragmentExpression = codeFragment.getContentElement() - if (codeFragmentExpression !is JetExpression) return BindingContext.EMPTY - val resolveSession = componentProvider.get() - - val (scopeForContextElement, dataFlowInfo) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) { - componentProvider.get().resolveToElement(it, BodyResolveMode.PARTIAL_FOR_COMPLETION) //TODO: discuss it - } ?: return BindingContext.EMPTY - - - return codeFragmentExpression.analyzeInContext( - scopeForContextElement, - BindingTraceContext(), - dataFlowInfo, - TypeUtils.NO_EXPECTED_TYPE, - resolveSession.getModuleDescriptor() + val trace = BindingTraceContext() + componentProvider.get().analyzeCodeFragment( + codeFragment, + trace, + BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it ) + return trace.bindingContext } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt index c5e3971c2f5..c71462dfb9b 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/ResolveElementCache.kt @@ -22,7 +22,6 @@ import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import com.intellij.util.containers.ContainerUtil -import org.jetbrains.kotlin.analyzer.computeTypeInContext import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener import org.jetbrains.kotlin.cfg.JetFlowInformationProvider import org.jetbrains.kotlin.container.get @@ -32,6 +31,7 @@ import org.jetbrains.kotlin.context.withProject import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve +import org.jetbrains.kotlin.idea.caches.resolve.CodeFragmentAnalyzer import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex import org.jetbrains.kotlin.name.FqName @@ -45,15 +45,14 @@ import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.resolve.util.getScopeAndDataFlowForAnalyzeFragment -import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull public class ResolveElementCache( private val resolveSession: ResolveSession, private val project: Project, - private val targetPlatform: TargetPlatform + private val targetPlatform: TargetPlatform, + private val codeFragmentAnalyzer: CodeFragmentAnalyzer ) : BodyResolveCache { private class CachedFullResolve(val bindingContext: BindingContext, resolveElement: JetElement) { private val modificationStamp: Long? = modificationStamp(resolveElement) @@ -343,19 +342,11 @@ public class ResolveElementCache( private fun codeFragmentAdditionalResolve(resolveSession: ResolveSession, codeFragment: JetCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace { val trace = createDelegatingTrace(codeFragment) - val codeFragmentExpression = codeFragment.getContentElement() as? JetExpression ?: return trace - - val (scopeForContextElement, dataFlowInfoForContextElement) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) { - val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL) - BodyResolveMode.PARTIAL_FOR_COMPLETION - else - bodyResolveMode - - resolveToElement(it, contextResolveMode) - } ?: return trace - - codeFragmentExpression.computeTypeInContext(scopeForContextElement, trace, dataFlowInfoForContextElement, - TypeUtils.NO_EXPECTED_TYPE, resolveSession.getModuleDescriptor()) + val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL) + BodyResolveMode.PARTIAL_FOR_COMPLETION + else + bodyResolveMode + codeFragmentAnalyzer.analyzeCodeFragment(codeFragment, trace, contextResolveMode) return trace }