Perform and cache full resolve when partial is not supported
This commit is contained in:
@@ -110,19 +110,28 @@ public abstract class ElementResolver protected constructor(
|
|||||||
protected fun performElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): Pair<BindingContext, StatementFilter> {
|
protected fun performElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): Pair<BindingContext, StatementFilter> {
|
||||||
val file = resolveElement.getContainingJetFile()
|
val file = resolveElement.getContainingJetFile()
|
||||||
|
|
||||||
val statementFilter = if (bodyResolveMode != BodyResolveMode.FULL && resolveElement is JetDeclaration)
|
var statementFilterUsed = StatementFilter.NONE
|
||||||
PartialBodyResolveFilter(contextElement, resolveElement, probablyNothingCallableNames(), bodyResolveMode == BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
|
||||||
else
|
fun createStatementFilter(): StatementFilter {
|
||||||
StatementFilter.NONE
|
assert(resolveElement is JetDeclaration)
|
||||||
|
if (bodyResolveMode != BodyResolveMode.FULL) {
|
||||||
|
statementFilterUsed = PartialBodyResolveFilter(
|
||||||
|
contextElement,
|
||||||
|
resolveElement as JetDeclaration,
|
||||||
|
probablyNothingCallableNames(),
|
||||||
|
bodyResolveMode == BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
||||||
|
}
|
||||||
|
return statementFilterUsed
|
||||||
|
}
|
||||||
|
|
||||||
val trace : BindingTrace = when (resolveElement) {
|
val trace : BindingTrace = when (resolveElement) {
|
||||||
is JetNamedFunction -> functionAdditionalResolve(resolveSession, resolveElement, file, statementFilter)
|
is JetNamedFunction -> functionAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter())
|
||||||
|
|
||||||
is JetClassInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, statementFilter)
|
is JetClassInitializer -> initializerAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter())
|
||||||
|
|
||||||
is JetSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, statementFilter)
|
is JetSecondaryConstructor -> secondaryConstructorAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter())
|
||||||
|
|
||||||
is JetProperty -> propertyAdditionalResolve(resolveSession, resolveElement, file, statementFilter)
|
is JetProperty -> propertyAdditionalResolve(resolveSession, resolveElement, file, createStatementFilter())
|
||||||
|
|
||||||
is JetDelegationSpecifierList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as JetClassOrObject, file)
|
is JetDelegationSpecifierList -> delegationSpecifierAdditionalResolve(resolveSession, resolveElement, resolveElement.getParent() as JetClassOrObject, file)
|
||||||
|
|
||||||
@@ -136,7 +145,7 @@ public abstract class ElementResolver protected constructor(
|
|||||||
|
|
||||||
is JetAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement)
|
is JetAnnotationEntry -> annotationAdditionalResolve(resolveSession, resolveElement)
|
||||||
|
|
||||||
is JetClass -> constructorAdditionalResolve(resolveSession, resolveElement, file, statementFilter)
|
is JetClass -> constructorAdditionalResolve(resolveSession, resolveElement, file)
|
||||||
|
|
||||||
is JetTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
|
is JetTypeParameter -> typeParameterAdditionalResolve(resolveSession, resolveElement)
|
||||||
|
|
||||||
@@ -158,7 +167,7 @@ public abstract class ElementResolver protected constructor(
|
|||||||
JetFlowInformationProvider(resolveElement, controlFlowTrace).checkDeclaration()
|
JetFlowInformationProvider(resolveElement, controlFlowTrace).checkDeclaration()
|
||||||
controlFlowTrace.addOwnDataTo(trace, null, false)
|
controlFlowTrace.addOwnDataTo(trace, null, false)
|
||||||
|
|
||||||
return Pair(trace.getBindingContext(), statementFilter)
|
return Pair(trace.getBindingContext(), statementFilterUsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun packageRefAdditionalResolve(resolveSession: ResolveSession, jetElement: JetElement): BindingTrace {
|
private fun packageRefAdditionalResolve(resolveSession: ResolveSession, jetElement: JetElement): BindingTrace {
|
||||||
@@ -339,7 +348,7 @@ public abstract class ElementResolver protected constructor(
|
|||||||
return trace
|
return trace
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun constructorAdditionalResolve(resolveSession: ResolveSession, klass: JetClass, file: JetFile, statementFilter: StatementFilter): BindingTrace {
|
private fun constructorAdditionalResolve(resolveSession: ResolveSession, klass: JetClass, file: JetFile): BindingTrace {
|
||||||
val trace = createDelegatingTrace(klass)
|
val trace = createDelegatingTrace(klass)
|
||||||
val scope = resolveSession.getDeclarationScopeProvider().getResolutionScopeForDeclaration(klass)
|
val scope = resolveSession.getDeclarationScopeProvider().getResolutionScopeForDeclaration(klass)
|
||||||
|
|
||||||
@@ -347,7 +356,7 @@ public abstract class ElementResolver protected constructor(
|
|||||||
val constructorDescriptor = classDescriptor.getUnsubstitutedPrimaryConstructor()
|
val constructorDescriptor = classDescriptor.getUnsubstitutedPrimaryConstructor()
|
||||||
?: error("Can't get primary constructor for descriptor '$classDescriptor' in from class '${klass.getElementTextWithContext()}'")
|
?: error("Can't get primary constructor for descriptor '$classDescriptor' in from class '${klass.getElementTextWithContext()}'")
|
||||||
|
|
||||||
val bodyResolver = createBodyResolver(resolveSession, trace, file, statementFilter)
|
val bodyResolver = createBodyResolver(resolveSession, trace, file, StatementFilter.NONE)
|
||||||
bodyResolver.resolveConstructorParameterDefaultValuesAndAnnotations(DataFlowInfo.EMPTY, trace, klass, constructorDescriptor, scope)
|
bodyResolver.resolveConstructorParameterDefaultValuesAndAnnotations(DataFlowInfo.EMPTY, trace, klass, constructorDescriptor, scope)
|
||||||
|
|
||||||
return trace
|
return trace
|
||||||
|
|||||||
@@ -28,19 +28,16 @@ import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIn
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.BodyResolveCache
|
import org.jetbrains.kotlin.resolve.BodyResolveCache
|
||||||
|
import org.jetbrains.kotlin.resolve.StatementFilter
|
||||||
import org.jetbrains.kotlin.resolve.lazy.*
|
import org.jetbrains.kotlin.resolve.lazy.*
|
||||||
import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull
|
import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull
|
||||||
|
|
||||||
public class ResolveElementCache(resolveSession: ResolveSession, private val project: Project) : ElementResolver(resolveSession), BodyResolveCache {
|
public class ResolveElementCache(resolveSession: ResolveSession, private val project: Project) : ElementResolver(resolveSession), BodyResolveCache {
|
||||||
// Recreate internal cache after change of modification count
|
// Recreate internal cache after change of modification count
|
||||||
private val additionalResolveCache: CachedValue<MemoizedFunctionToNotNull<JetElement, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue(
|
private val fullResolveCache: CachedValue<MutableMap<JetElement, BindingContext>> = CachedValuesManager.getManager(project).createCachedValue(
|
||||||
object : CachedValueProvider<MemoizedFunctionToNotNull<JetElement, BindingContext>> {
|
object : CachedValueProvider<MutableMap<JetElement, BindingContext>> {
|
||||||
override fun compute(): CachedValueProvider.Result<MemoizedFunctionToNotNull<JetElement, BindingContext>> {
|
override fun compute(): CachedValueProvider.Result<MutableMap<JetElement, BindingContext>> {
|
||||||
val manager = resolveSession.getStorageManager()
|
return CachedValueProvider.Result.create(ContainerUtil.createConcurrentSoftValueMap<JetElement, BindingContext>(),
|
||||||
val cacheFunction = manager.createSoftlyRetainedMemoizedFunction<JetElement, BindingContext> { element ->
|
|
||||||
performElementAdditionalResolve(element, element, BodyResolveMode.FULL).first
|
|
||||||
}
|
|
||||||
return CachedValueProvider.Result.create(cacheFunction,
|
|
||||||
PsiModificationTracker.MODIFICATION_COUNT,
|
PsiModificationTracker.MODIFICATION_COUNT,
|
||||||
resolveSession.getExceptionTracker())
|
resolveSession.getExceptionTracker())
|
||||||
}
|
}
|
||||||
@@ -58,36 +55,53 @@ public class ResolveElementCache(resolveSession: ResolveSession, private val pro
|
|||||||
false)
|
false)
|
||||||
|
|
||||||
override fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
|
override fun getElementAdditionalResolve(resolveElement: JetElement, contextElement: JetElement, bodyResolveMode: BodyResolveMode): BindingContext {
|
||||||
if (bodyResolveMode != BodyResolveMode.FULL && !hasElementAdditionalResolveCached(resolveElement) && resolveElement is JetDeclaration) {
|
val fullResolveMap = fullResolveCache.getValue()
|
||||||
if (bodyResolveMode == BodyResolveMode.PARTIAL) {
|
fullResolveMap[resolveElement]?.let { return it } // check if full additional resolve already performed
|
||||||
|
|
||||||
|
when (bodyResolveMode) {
|
||||||
|
BodyResolveMode.FULL -> {
|
||||||
|
val bindingContext = performElementAdditionalResolve(resolveElement, resolveElement, BodyResolveMode.FULL).first
|
||||||
|
fullResolveMap[resolveElement] = bindingContext
|
||||||
|
return bindingContext
|
||||||
|
}
|
||||||
|
|
||||||
|
BodyResolveMode.PARTIAL -> {
|
||||||
|
if (resolveElement !is JetDeclaration) {
|
||||||
|
return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL)
|
||||||
|
}
|
||||||
|
|
||||||
val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement)
|
val statementToResolve = PartialBodyResolveFilter.findStatementToResolve(contextElement, resolveElement)
|
||||||
val map = partialBodyResolveCache.getValue()
|
val partialResolveMap = partialBodyResolveCache.getValue()
|
||||||
map[statementToResolve ?: resolveElement]?.let { return it }
|
partialResolveMap[statementToResolve ?: resolveElement]?.let { return it } // partial resolve is already cached for this statement
|
||||||
|
|
||||||
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.PARTIAL)
|
val (bindingContext, statementFilter) = performElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.PARTIAL)
|
||||||
|
|
||||||
if (statementFilter is PartialBodyResolveFilter) {
|
if (statementFilter == StatementFilter.NONE) { // partial resolve is not supported for the given declaration - full resolve performed instead
|
||||||
for (statement in statementFilter.allStatementsToResolve) {
|
fullResolveMap[resolveElement] = bindingContext
|
||||||
if (!map.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) {
|
return bindingContext
|
||||||
map[statement] = bindingContext
|
}
|
||||||
}
|
|
||||||
|
for (statement in (statementFilter as PartialBodyResolveFilter).allStatementsToResolve) {
|
||||||
|
if (!partialResolveMap.containsKey(statement) && bindingContext[BindingContext.PROCESSED, statement] == true) {
|
||||||
|
partialResolveMap[statement] = bindingContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map[resolveElement] = bindingContext // we use the whole declaration key in the map to obtain resolve not inside any block (e.g. default parameter values)
|
partialResolveMap[resolveElement] = bindingContext // we use the whole declaration key in the map to obtain resolve not inside any block (e.g. default parameter values)
|
||||||
|
|
||||||
return bindingContext
|
return bindingContext
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
BodyResolveMode.PARTIAL_FOR_COMPLETION -> {
|
||||||
|
if (resolveElement !is JetDeclaration) {
|
||||||
|
return getElementAdditionalResolve(resolveElement, contextElement, BodyResolveMode.FULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// not cached
|
||||||
return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode).first
|
return performElementAdditionalResolve(resolveElement, contextElement, bodyResolveMode).first
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return additionalResolveCache.getValue().invoke(resolveElement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasElementAdditionalResolveCached(element: JetElement)
|
|
||||||
= additionalResolveCache.hasUpToDateValue() && additionalResolveCache.getValue().isComputed(element)
|
|
||||||
|
|
||||||
override fun createAdditionalCheckerProvider(file: JetFile, module: ModuleDescriptor)
|
override fun createAdditionalCheckerProvider(file: JetFile, module: ModuleDescriptor)
|
||||||
= TargetPlatformDetector.getPlatform(file).createAdditionalCheckerProvider(module)
|
= TargetPlatformDetector.getPlatform(file).createAdditionalCheckerProvider(module)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class ResolveElementCacheTest : JetLightCodeInsightFixtureTestCase() {
|
|||||||
|
|
||||||
private val FILE_TEXT =
|
private val FILE_TEXT =
|
||||||
"""
|
"""
|
||||||
class C {
|
class C(param1: String = "", param2: Int = 0) {
|
||||||
fun a(p: Int = 0) {
|
fun a(p: Int = 0) {
|
||||||
b(1, 2)
|
b(1, 2)
|
||||||
val x = c()
|
val x = c()
|
||||||
@@ -45,6 +45,7 @@ class C {
|
|||||||
|
|
||||||
private data class Data(
|
private data class Data(
|
||||||
val file: JetFile,
|
val file: JetFile,
|
||||||
|
val klass: JetClass,
|
||||||
val members: List<JetDeclaration>,
|
val members: List<JetDeclaration>,
|
||||||
val statements: List<JetExpression>,
|
val statements: List<JetExpression>,
|
||||||
val factory: JetPsiFactory
|
val factory: JetPsiFactory
|
||||||
@@ -57,7 +58,7 @@ class C {
|
|||||||
val function = members.first() as JetNamedFunction
|
val function = members.first() as JetNamedFunction
|
||||||
val statements = (function.getBodyExpression() as JetBlockExpression).getStatements()
|
val statements = (function.getBodyExpression() as JetBlockExpression).getStatements()
|
||||||
myFixture.getProject().executeWriteCommand("") {
|
myFixture.getProject().executeWriteCommand("") {
|
||||||
Data(file, members, statements, JetPsiFactory(getProject())).handler()
|
Data(file, klass, members, statements, JetPsiFactory(getProject())).handler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,4 +157,14 @@ class C {
|
|||||||
assert(bindingContext3 !== bindingContext2)
|
assert(bindingContext3 !== bindingContext2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun testFullResolvedCachedWhenPartialForConstructorInvoked() {
|
||||||
|
doTest {
|
||||||
|
val defaultValue1 = klass.getPrimaryConstructorParameters()[0].getDefaultValue()!!
|
||||||
|
val defaultValue2 = klass.getPrimaryConstructorParameters()[1].getDefaultValue()!!
|
||||||
|
val bindingContext1 = defaultValue1.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val bindingContext2 = defaultValue2.analyze(BodyResolveMode.FULL)
|
||||||
|
assert(bindingContext1 === bindingContext2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user