diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index a1a6b696a1a..794ed6d384f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -161,7 +161,7 @@ public interface BindingContext { /** * Has type of current expression has been already resolved */ - WritableSlice PROCESSED = Slices.createSimpleSetSlice(); + WritableSlice PROCESSED = Slices.createSimpleSlice(); WritableSlice USED_AS_EXPRESSION = Slices.createSimpleSetSlice(); WritableSlice USED_AS_RESULT_OF_LAMBDA = Slices.createSimpleSetSlice(); WritableSlice UNREACHABLE_CODE = Slices.createSimpleSetSlice(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java index 6f32873f870..60ee2626c83 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java @@ -181,7 +181,7 @@ public class BindingContextUtils { @Nullable public static KotlinTypeInfo getRecordedTypeInfo(@NotNull KtExpression expression, @NotNull BindingContext context) { // noinspection ConstantConditions - if (!context.get(BindingContext.PROCESSED, expression)) return null; + if (context.get(BindingContext.PROCESSED, expression) != Boolean.TRUE) return null; // NB: should never return null if expression is already processed KotlinTypeInfo result = context.get(BindingContext.EXPRESSION_TYPE_INFO, expression); return result != null ? result : TypeInfoFactoryKt.noTypeInfo(DataFlowInfoFactory.EMPTY); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java index 5949ca001dd..95f1a3dbb07 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ExpressionTypingVisitorDispatcher.java @@ -182,7 +182,7 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor, bodyResolveMode: BodyResolveMode): BindingContext fun analyzeFullyAndGetResult(elements: Collection): AnalysisResult diff --git a/idea/ide-common/src/org/jetbrains/kotlin/resolve/lazy/PartialBodyResolveFilter.kt b/idea/ide-common/src/org/jetbrains/kotlin/resolve/lazy/PartialBodyResolveFilter.kt index fc760d7208b..f56af420c6e 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/resolve/lazy/PartialBodyResolveFilter.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/resolve/lazy/PartialBodyResolveFilter.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.resolve.lazy import com.intellij.psi.PsiElement -import com.intellij.util.SmartList import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* @@ -26,14 +25,12 @@ import org.jetbrains.kotlin.resolve.StatementFilter import org.jetbrains.kotlin.util.isProbablyNothing import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.swap -import java.util.ArrayList -import java.util.HashMap -import java.util.HashSet +import java.util.* //TODO: do resolve anonymous object's body class PartialBodyResolveFilter( - elementToResolve: KtElement, + elementsToResolve: Collection, private val declaration: KtDeclaration, probablyNothingCallableNames: ProbablyNothingCallableNames, forCompletion: Boolean @@ -50,7 +47,7 @@ class PartialBodyResolveFilter( get() = statementMarks.allMarkedStatements() init { - assert(declaration.isAncestor(elementToResolve)) + elementsToResolve.forEach { assert(declaration.isAncestor(it)) } assert(!KtPsiUtil.isLocal(declaration)) { "Should never be invoked on local declaration otherwise we may miss some local declarations with type Nothing" } declaration.forEachDescendantOfType { declaration -> @@ -67,7 +64,9 @@ class PartialBodyResolveFilter( } } - statementMarks.mark(elementToResolve, if (forCompletion) MarkLevel.NEED_COMPLETION else MarkLevel.NEED_REFERENCE_RESOLVE) + elementsToResolve.forEach { + statementMarks.mark(it, if (forCompletion) MarkLevel.NEED_COMPLETION else MarkLevel.NEED_REFERENCE_RESOLVE) + } declaration.forTopLevelBlocksInside { processBlock(it) } } 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 index 32300a73cb3..8bfaf21d727 100644 --- 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 @@ -48,7 +48,7 @@ class CodeFragmentAnalyzer( val codeFragmentElement = codeFragment.getContentElement() val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) { - resolveElementCache!!.resolveToElement(it, bodyResolveMode) + resolveElementCache!!.resolveToElements(listOf(it), bodyResolveMode) } ?: return when (codeFragmentElement) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolutionFacadeImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolutionFacadeImpl.kt index 0a2a768b5a3..26429111be2 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolutionFacadeImpl.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ResolutionFacadeImpl.kt @@ -46,8 +46,12 @@ internal class ResolutionFacadeImpl( fun findModuleDescriptor(ideaModuleInfo: IdeaModuleInfo) = projectFacade.findModuleDescriptor(ideaModuleInfo) override fun analyze(element: KtElement, bodyResolveMode: BodyResolveMode): BindingContext { - val resolveElementCache = getFrontendService(element, ResolveElementCache::class.java) - return resolveElementCache.resolveToElement(element, bodyResolveMode) + return analyze(listOf(element), bodyResolveMode) + } + + override fun analyze(elements: Collection, bodyResolveMode: BodyResolveMode): BindingContext { + val resolveElementCache = getFrontendService(elements.first(), ResolveElementCache::class.java) + return resolveElementCache.resolveToElements(elements, bodyResolveMode) } override fun analyzeFullyAndGetResult(elements: Collection): AnalysisResult diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaLocalDescriptorResolver.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaLocalDescriptorResolver.kt index 903e627a8db..37b9f5cd0a7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaLocalDescriptorResolver.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/IdeaLocalDescriptorResolver.kt @@ -31,7 +31,7 @@ class IdeaLocalDescriptorResolver( private val absentDescriptorHandler: AbsentDescriptorHandler ) : LocalDescriptorResolver { override fun resolveLocalDeclaration(declaration: KtDeclaration): DeclarationDescriptor { - val context = resolveElementCache.resolveToElement(declaration, BodyResolveMode.FULL) + val context = resolveElementCache.resolveToElements(listOf(declaration), BodyResolveMode.FULL) return context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) ?: absentDescriptorHandler.diagnoseDescriptorNotFound(declaration) } 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 3e456d29ff1..03dff0c43b4 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 @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo @@ -45,7 +46,8 @@ import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor import org.jetbrains.kotlin.resolve.scopes.LexicalScope -import org.jetbrains.kotlin.utils.addToStdlib.check +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* class ResolveElementCache( private val resolveSession: ResolveSession, @@ -112,13 +114,18 @@ class ResolveElementCache( } override fun resolveFunctionBody(function: KtNamedFunction) - = getElementAdditionalResolve(function, function, BodyResolveMode.FULL) + = getElementsAdditionalResolve(function, emptyList(), BodyResolveMode.FULL) fun resolvePrimaryConstructorParametersDefaultValues(ktClass: KtClass): BindingContext { return constructorAdditionalResolve(resolveSession, ktClass, ktClass.getContainingKtFile()).bindingContext } + @Deprecated("Use getElementsAdditionalResolve") fun getElementAdditionalResolve(resolveElement: KtElement, contextElement: KtElement, bodyResolveMode: BodyResolveMode): BindingContext { + return getElementsAdditionalResolve(resolveElement, listOf(contextElement), bodyResolveMode) + } + + fun getElementsAdditionalResolve(resolveElement: KtElement, contextElements: Collection, bodyResolveMode: BodyResolveMode): BindingContext { // check if full additional resolve already performed and is up-to-date val fullResolveMap = fullResolveCache.value val cachedFullResolve = fullResolveMap[resolveElement] @@ -133,23 +140,25 @@ class ResolveElementCache( when (bodyResolveMode) { BodyResolveMode.FULL -> { - val bindingContext = performElementAdditionalResolve(resolveElement, resolveElement, BodyResolveMode.FULL).first + val bindingContext = performElementAdditionalResolve(resolveElement, emptyList(), BodyResolveMode.FULL).first fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement) return bindingContext } BodyResolveMode.PARTIAL -> { if (resolveElement !is KtDeclaration) { - return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL) + return getElementsAdditionalResolve(resolveElement, emptyList(), BodyResolveMode.FULL) } val file = resolveElement.getContainingKtFile() - val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement) + val statementsToResolve = contextElements.map { PartialBodyResolveFilter.findStatementToResolve(it, resolveElement) }.distinct() val partialResolveMap = partialBodyResolveCache.value - partialResolveMap[statementToResolve ?: resolveElement] - ?.check { it.isUpToDate(file) } - ?.let { return it.bindingContext } // partial resolve is already cached for this statement - val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.PARTIAL) + val cachedResults = statementsToResolve.map { partialResolveMap[it ?: resolveElement] } + if (cachedResults.all { it != null && it.isUpToDate(file) }) { // partial resolve is already cached for these statements + return CompositeBindingContext.create(cachedResults.map { it!!.bindingContext }.distinct()) + } + + val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElements, BodyResolveMode.PARTIAL) if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead fullResolveMap[resolveElement] = CachedFullResolve(bindingContext, resolveElement) @@ -172,35 +181,48 @@ class ResolveElementCache( BodyResolveMode.PARTIAL_FOR_COMPLETION -> { if (resolveElement !is KtDeclaration) { - return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL) + return getElementsAdditionalResolve(resolveElement, emptyList(), BodyResolveMode.FULL) } // not cached - return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode).first + return performElementAdditionalResolve(resolveElement, contextElements, bodyResolveMode).first } } } - fun resolveToElement(element: KtElement, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext { - var contextElement = element + fun resolveToElements(elements: Collection, bodyResolveMode: BodyResolveMode = BodyResolveMode.FULL): BindingContext { + val elementsByAdditionalResolveElement: Map> = elements.groupBy { findElementOfAdditionalResolve(it) } - val elementOfAdditionalResolve = findElementOfAdditionalResolve(contextElement) - - if (elementOfAdditionalResolve is KtParameter) { - // Parameters for function literal could be met inside other parameters. We can't make resolveToDescriptors for internal elements. - contextElement = elementOfAdditionalResolve - } - else if (elementOfAdditionalResolve != null) { - return getElementAdditionalResolve(elementOfAdditionalResolve, contextElement, bodyResolveMode) + val bindingContexts = ArrayList() + val declarationsToResolve = ArrayList() + var addResolveSessionBindingContext = false + for ((elementOfAdditionalResolve, contextElements) in elementsByAdditionalResolveElement) { + if (elementOfAdditionalResolve != null) { + if (elementOfAdditionalResolve !is KtParameter) { + val bindingContext = getElementsAdditionalResolve(elementOfAdditionalResolve, contextElements, bodyResolveMode) + bindingContexts.add(bindingContext) + } + else { + // Parameters for function literal could be met inside other parameters. We can't make resolveToDescriptors for internal elements. + declarationsToResolve.addIfNotNull(elementOfAdditionalResolve.getNonStrictParentOfType()) + addResolveSessionBindingContext = true + } + } + else { + contextElements + .map { it.getNonStrictParentOfType() } + .filterNotNull() + .filterTo(declarationsToResolve) { it !is KtAnonymousInitializer } + addResolveSessionBindingContext = true + } } - val declaration = contextElement.getParentOfType(false) - if (declaration != null && declaration !is KtAnonymousInitializer) { - // Activate descriptor resolution - resolveSession.resolveToDescriptor(declaration) + declarationsToResolve.forEach { resolveSession.resolveToDescriptor(it) } + if (addResolveSessionBindingContext) { + bindingContexts.add(resolveSession.bindingContext) } - return resolveSession.bindingContext + return CompositeBindingContext.create(bindingContexts) } private fun findElementOfAdditionalResolve(element: KtElement): KtElement? { @@ -258,7 +280,7 @@ class ResolveElementCache( } } - private fun performElementAdditionalResolve(resolveElement: KtElement, contextElement: KtElement, + private fun performElementAdditionalResolve(resolveElement: KtElement, contextElements: Collection, bodyResolveMode: BodyResolveMode): Pair { val file = resolveElement.getContainingKtFile() @@ -268,7 +290,7 @@ class ResolveElementCache( assert(resolveElement is KtDeclaration) if (bodyResolveMode != BodyResolveMode.FULL) { statementFilterUsed = PartialBodyResolveFilter( - contextElement, + contextElements, resolveElement as KtDeclaration, probablyNothingCallableNames(), bodyResolveMode == BodyResolveMode.PARTIAL_FOR_COMPLETION) diff --git a/idea/tests/org/jetbrains/kotlin/idea/ResolveElementCacheTest.kt b/idea/tests/org/jetbrains/kotlin/idea/ResolveElementCacheTest.kt index 87bb9dc0393..7a1d1a1ad12 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/ResolveElementCacheTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/ResolveElementCacheTest.kt @@ -17,9 +17,11 @@ package org.jetbrains.kotlin.idea import com.intellij.psi.PsiDocumentManager +import junit.framework.TestCase import org.intellij.lang.annotations.Language import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor @@ -48,6 +50,7 @@ class C(param1: String = "", param2: Int = 0) { } fun c() { + x(2) } } """ @@ -361,4 +364,61 @@ class C(param1: String = "", param2: Int = 0) { val annotationArguments = ((file.declarations[0]) as KtClass).getPrimaryConstructor()!!.annotationEntries[0].valueArgumentList!! annotationArguments.analyzeFullyAndGetResult() } + + fun testFullResolveMultiple() { + doTest { + val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression + val statement1InFunA = aBody.statements[0] + val statement2InFunA = aBody.statements[1] + val statementInFunB = ((members[1] as KtFunction).bodyExpression as KtBlockExpression).statements[0] + val statementInFunC = ((members[2] as KtFunction).bodyExpression as KtBlockExpression).statements[0] + + val bindingContext = checkResolveMultiple(BodyResolveMode.FULL, statement1InFunA, statementInFunB) + + TestCase.assertEquals(true, bindingContext[BindingContext.PROCESSED, statement2InFunA]) + TestCase.assertEquals(null, bindingContext[BindingContext.PROCESSED, statementInFunC]) + } + } + + fun testPartialResolveMultiple() { + doTest { + val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression + val statement1InFunA = aBody.statements[0] + val statement2InFunA = aBody.statements[1] + val statementInFunB = ((members[1] as KtFunction).bodyExpression as KtBlockExpression).statements[0] + val constructorParameterDefault = klass.getPrimaryConstructor()!!.valueParameters[1].defaultValue!! + val funC = members[2] + + checkResolveMultiple(BodyResolveMode.PARTIAL, statement1InFunA, statement2InFunA, statementInFunB, constructorParameterDefault, funC) + } + } + + fun testPartialResolveMultipleInOneFunction() { + doTest { + val aBody = (members[0] as KtFunction).bodyExpression as KtBlockExpression + val statement1InFunA = aBody.statements[0] + val statement2InFunA = aBody.statements[1] + + val bindingContext = checkResolveMultiple(BodyResolveMode.PARTIAL, statement1InFunA, statement2InFunA) + + val bindingContext1 = statement1InFunA.analyze(BodyResolveMode.PARTIAL) + assert(bindingContext1 === bindingContext) + } + } + + private fun checkResolveMultiple(mode: BodyResolveMode, vararg expressions: KtExpression): BindingContext { + val resolutionFacade = expressions.first().getResolutionFacade() + val bindingContext = resolutionFacade.analyze(expressions.asList(), mode) + + expressions.forEach { + if (it !is KtDeclaration) { + TestCase.assertEquals(true, bindingContext[BindingContext.PROCESSED, it]) + } + else { + TestCase.assertNotNull(bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]) + } + } + + return bindingContext + } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java index 179b2e860c7..b5d0f8c76cc 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractOutOfBlockModificationTest.java @@ -108,7 +108,7 @@ public abstract class AbstractOutOfBlockModificationTest extends KotlinLightCode @SuppressWarnings("ConstantConditions") boolean expressionProcessed = context.get( BindingContext.PROCESSED, - ktExpression instanceof KtFunctionLiteral ? (KtLambdaExpression) ktExpression.getParent() : ktExpression); + ktExpression instanceof KtFunctionLiteral ? (KtLambdaExpression) ktExpression.getParent() : ktExpression) == Boolean.TRUE; assertEquals("Expected out-of-block should result expression analyzed and vise versa", expectedOutOfBlock, expressionProcessed); diff --git a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractAdditionalResolveDescriptorRendererTest.kt b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractAdditionalResolveDescriptorRendererTest.kt index 43001ec78b1..6a3402d56b9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractAdditionalResolveDescriptorRendererTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractAdditionalResolveDescriptorRendererTest.kt @@ -34,7 +34,7 @@ abstract class AbstractAdditionalResolveDescriptorRendererTest : AbstractDescrip override fun getDescriptor(declaration: KtDeclaration, container: ComponentProvider): DeclarationDescriptor { if (declaration is KtAnonymousInitializer || KtPsiUtil.isLocal(declaration)) { return container.get() - .resolveToElement(declaration, BodyResolveMode.FULL) + .resolveToElements(listOf(declaration), BodyResolveMode.FULL) .get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)!! } return container.get().resolveToDescriptor(declaration)