Introduce component for caching missing supertypes

Profiling has shown, that supertype hierarchy scan for all calls has considerable
performance cost. However, missing supertypes may be calculated only once per
descriptor which would help avoiding multiple supertype hierarchy scans for
resolved calls from the same class. New memoizer is injected into call completers
and checker contexts and then used for retrieving missing super classifiers.

#KT-19234 Fixed
This commit is contained in:
Pavel Kirpichenkov
2019-11-13 18:27:06 +03:00
parent 4b405c6c0f
commit 3e8c15c62a
13 changed files with 150 additions and 70 deletions
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.typeUtil.supertypes
class MissingSupertypesResolver(
storageManager: StorageManager,
private val moduleDescriptor: ModuleDescriptor
) {
fun getMissingSuperClassifiers(descriptor: ClassifierDescriptor) = missingClassifiers(descriptor)
private val missingClassifiers = storageManager.createMemoizedFunction { classifier: ClassifierDescriptor ->
doGetMissingClassifiers(classifier)
}
private fun doGetMissingClassifiers(descriptor: ClassifierDescriptor): Set<ClassifierDescriptor> {
val missingSuperClassifiers = mutableSetOf<ClassifierDescriptor>()
val type = descriptor.defaultType
for (supertype in type.supertypes()) {
val supertypeDeclaration = supertype.constructor.declarationDescriptor
/*
* TODO: expects are not checked, because findClassAcrossModuleDependencies does not work with actualization via type alias
* Type parameters are skipped here in favor to explicit checks for bounds, local declarations are ignored for optimization
*/
if (supertypeDeclaration !is ClassDescriptor || supertypeDeclaration.isExpect) continue
if (supertypeDeclaration.visibility == Visibilities.LOCAL) continue
val superTypeClassId = supertypeDeclaration.classId ?: continue
val dependency = moduleDescriptor.findClassAcrossModuleDependencies(superTypeClassId)
if (dependency == null || dependency is NotFoundClasses.MockClassDescriptor) {
missingSuperClassifiers.add(supertypeDeclaration)
}
}
return missingSuperClassifiers.toSet()
}
}
@@ -266,7 +266,8 @@ public class ModifiersChecker {
public void runDeclarationCheckers(@NotNull KtDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
DeclarationCheckerContext context = new DeclarationCheckerContext(
trace, languageVersionSettings, deprecationResolver, moduleDescriptor, expectActualTracker
trace, languageVersionSettings, deprecationResolver, moduleDescriptor, expectActualTracker,
missingSupertypesResolver
);
for (DeclarationChecker checker : declarationCheckers) {
checker.check(declaration, descriptor, context);
@@ -291,6 +292,7 @@ public class ModifiersChecker {
private final ExpectActualTracker expectActualTracker;
private final DeprecationResolver deprecationResolver;
private final ModuleDescriptor moduleDescriptor;
private final MissingSupertypesResolver missingSupertypesResolver;
public ModifiersChecker(
@NotNull AnnotationChecker annotationChecker,
@@ -298,7 +300,8 @@ public class ModifiersChecker {
@NotNull LanguageVersionSettings languageVersionSettings,
@NotNull ExpectActualTracker expectActualTracker,
@NotNull DeprecationResolver deprecationResolver,
@NotNull ModuleDescriptor moduleDescriptor
@NotNull ModuleDescriptor moduleDescriptor,
@NotNull MissingSupertypesResolver missingSupertypesResolver
) {
this.annotationChecker = annotationChecker;
this.declarationCheckers = declarationCheckers;
@@ -306,6 +309,7 @@ public class ModifiersChecker {
this.expectActualTracker = expectActualTracker;
this.deprecationResolver = deprecationResolver;
this.moduleDescriptor = moduleDescriptor;
this.missingSupertypesResolver = missingSupertypesResolver;
}
@NotNull
@@ -59,7 +59,8 @@ class CallCompleter(
private val moduleDescriptor: ModuleDescriptor,
private val deprecationResolver: DeprecationResolver,
private val effectSystem: EffectSystem,
private val dataFlowValueFactory: DataFlowValueFactory
private val dataFlowValueFactory: DataFlowValueFactory,
private val missingSupertypesResolver: MissingSupertypesResolver
) {
fun <D : CallableDescriptor> completeCall(
context: BasicCallResolutionContext,
@@ -78,7 +79,7 @@ class CallCompleter(
if (context.trace.wantsDiagnostics()) {
if (resolvedCall == null) {
checkMissingSupertypes(context, moduleDescriptor)
checkMissingSupertypes(context, missingSupertypesResolver)
} else {
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
resolvedCall.variableCall.call.calleeExpression
@@ -88,7 +89,7 @@ class CallCompleter(
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
else resolvedCall.call.callElement
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor)
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor, missingSupertypesResolver)
for (callChecker in callCheckers) {
callChecker.check(resolvedCall, reportOn, callCheckerContext)
@@ -105,12 +106,15 @@ class CallCompleter(
return results
}
private fun checkMissingSupertypes(context: BasicCallResolutionContext, moduleDescriptor: ModuleDescriptor) {
private fun checkMissingSupertypes(
context: BasicCallResolutionContext,
missingSupertypesResolver: MissingSupertypesResolver
) {
val call = context.call
val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>()
?: return
val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>() ?: return
MissingDependencySupertypeChecker.checkSupertypes(
explicitReceiver.type, call.callElement, context.trace, moduleDescriptor
explicitReceiver.type, call.callElement, context.trace, missingSupertypesResolver
)
}
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -42,6 +43,7 @@ class CallCheckerContext @JvmOverloads constructor(
val resolutionContext: ResolutionContext<*>,
override val deprecationResolver: DeprecationResolver,
override val moduleDescriptor: ModuleDescriptor,
val missingSupertypesResolver: MissingSupertypesResolver,
override val trace: BindingTrace = resolutionContext.trace
) : CheckerContext {
val scope: LexicalScope
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.components.CompletedCallInfo
@@ -49,7 +50,8 @@ class CoroutineInferenceSession(
private val doubleColonExpressionResolver: DoubleColonExpressionResolver,
private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor,
private val typeApproximator: TypeApproximator
private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver
) : ManyCandidatesResolver<CallableDescriptor>(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns
) {
@@ -222,7 +224,7 @@ class CoroutineInferenceSession(
return ResolvedAtomCompleter(
resultSubstitutor, context, kotlinToResolvedCallTransformer,
expressionTypingServices, argumentTypeResolver, doubleColonExpressionResolver, builtIns,
deprecationResolver, moduleDescriptor, context.dataFlowValueFactory, typeApproximator
deprecationResolver, moduleDescriptor, context.dataFlowValueFactory, typeApproximator, missingSupertypesResolver
)
}
}
@@ -38,8 +38,6 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
@@ -75,7 +73,8 @@ class KotlinResolutionCallbacksImpl(
val doubleColonExpressionResolver: DoubleColonExpressionResolver,
val deprecationResolver: DeprecationResolver,
val moduleDescriptor: ModuleDescriptor,
val topLevelCallContext: BasicCallResolutionContext?
val topLevelCallContext: BasicCallResolutionContext?,
val missingSupertypesResolver: MissingSupertypesResolver
) : KotlinResolutionCallbacks {
class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) {
val returnStatements = ArrayList<Pair<KtReturnExpression, LambdaContextInfo?>>()
@@ -161,7 +160,8 @@ class KotlinResolutionCallbacksImpl(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter,
callComponents, builtIns, topLevelCallContext, stubsForPostponedVariables, trace,
kotlinToResolvedCallTransformer, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator,
missingSupertypesResolver
)
} else {
null
@@ -72,7 +72,8 @@ class KotlinToResolvedCallTransformer(
private val builtIns: KotlinBuiltIns,
private val typeSystemContext: TypeSystemInferenceExtensionContextDelegate,
private val smartCastManager: SmartCastManager,
private val typeApproximator: TypeApproximator
private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver
) {
companion object {
private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC
@@ -134,7 +135,7 @@ class KotlinToResolvedCallTransformer(
val ktPrimitiveCompleter = ResolvedAtomCompleter(
resultSubstitutor, context, this, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, builtIns, deprecationResolver, moduleDescriptor, dataFlowValueFactory,
typeApproximator
typeApproximator, missingSupertypesResolver
)
if (!ErrorUtils.isError(candidate.candidateDescriptor)) {
@@ -69,7 +69,8 @@ class PSICallResolver(
private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor,
private val callableReferenceResolver: CallableReferenceResolver,
private val candidateInterceptor: CandidateInterceptor
private val candidateInterceptor: CandidateInterceptor,
private val missingSupertypesResolver: MissingSupertypesResolver
) {
private val givenCandidatesName = Name.special("<given candidates>")
@@ -182,7 +183,7 @@ class PSICallResolver(
argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer,
dataFlowValueFactory, inferenceSession, constantExpressionEvaluator, typeResolver,
this, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents,
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, context
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, context, missingSupertypesResolver
)
private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? {
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
@@ -49,9 +50,12 @@ class ResolvedAtomCompleter(
private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor,
private val dataFlowValueFactory: DataFlowValueFactory,
private val typeApproximator: TypeApproximator
private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver
) {
private val topLevelCallCheckerContext = CallCheckerContext(topLevelCallContext, deprecationResolver, moduleDescriptor)
private val topLevelCallCheckerContext = CallCheckerContext(
topLevelCallContext, deprecationResolver, moduleDescriptor, missingSupertypesResolver
)
private val topLevelTrace = topLevelCallCheckerContext.trace
private fun complete(resolvedAtom: ResolvedAtom) {
@@ -90,7 +94,7 @@ class ResolvedAtomCompleter(
val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall
if (ErrorUtils.isError(resolvedCall.candidateDescriptor)) {
kotlinToResolvedCallTransformer.runArgumentsChecks(topLevelCallContext, topLevelTrace, lastCall as NewResolvedCallImpl<*>)
checkMissingReceiverSupertypes(resolvedCall, moduleDescriptor, topLevelTrace)
checkMissingReceiverSupertypes(resolvedCall, missingSupertypesResolver, topLevelTrace)
return resolvedCall
}
@@ -101,7 +105,8 @@ class ResolvedAtomCompleter(
CallCheckerContext(
resolutionContextForPartialCall.replaceBindingTrace(topLevelTrace),
deprecationResolver,
moduleDescriptor
moduleDescriptor,
missingSupertypesResolver
)
else
topLevelCallCheckerContext
@@ -119,7 +124,7 @@ class ResolvedAtomCompleter(
private fun checkMissingReceiverSupertypes(
resolvedCall: ResolvedCall<CallableDescriptor>,
moduleDescriptor: ModuleDescriptor,
missingSupertypesResolver: MissingSupertypesResolver,
trace: BindingTrace
) {
val receiverValue = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver
@@ -128,7 +133,7 @@ class ResolvedAtomCompleter(
receiverType,
resolvedCall.call.callElement,
trace,
moduleDescriptor
missingSupertypesResolver
)
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
interface DeclarationChecker {
@@ -33,5 +34,6 @@ class DeclarationCheckerContext(
override val languageVersionSettings: LanguageVersionSettings,
override val deprecationResolver: DeprecationResolver,
override val moduleDescriptor: ModuleDescriptor,
val expectActualTracker: ExpectActualTracker
val expectActualTracker: ExpectActualTracker,
val missingSupertypesResolver: MissingSupertypesResolver
) : CheckerContext
@@ -12,29 +12,27 @@ import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
object MissingDependencySupertypeChecker {
object ForDeclarations : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val trace = context.trace
val module = context.moduleDescriptor
if (descriptor is ClassDescriptor) {
checkSupertypes(descriptor.defaultType, declaration, trace, module)
checkSupertypes(descriptor, declaration, trace, context.missingSupertypesResolver)
}
if (declaration is KtTypeParameterListOwner) {
for (ktTypeParameter in declaration.typeParameters) {
val typeParameterDescriptor = trace.bindingContext.get(BindingContext.TYPE_PARAMETER, ktTypeParameter) ?: continue
for (upperBound in typeParameterDescriptor.upperBounds) {
checkSupertypes(upperBound, ktTypeParameter, trace, module)
checkSupertypes(upperBound, ktTypeParameter, trace, context.missingSupertypesResolver)
}
}
}
@@ -45,57 +43,54 @@ object MissingDependencySupertypeChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.resultingDescriptor
// Constructor call leads to resolution of supertypes of enclosing class if it's an inner class constructor
checkHierarchy(descriptor.dispatchReceiverParameter?.declaration, reportOn, context)
// The constructed class' own supertypes are not resolved after constructor call,
// so its containing declaration should not be checked.
if (descriptor !is ConstructorDescriptor) {
checkHierarchy(descriptor.containingDeclaration, reportOn, context)
checkHierarchy(descriptor.extensionReceiverParameter?.declaration, reportOn, context)
val errorReported = checkSupertypes(
descriptor.dispatchReceiverParameter?.declaration, reportOn,
context.trace, context.missingSupertypesResolver
)
if (descriptor !is ConstructorDescriptor && !errorReported) {
// The constructed class' own supertypes are not resolved after constructor call,
// so its containing declaration should not be checked.
// Dispatch receiver is checked before for case of inner class constructor call.
checkSupertypes(descriptor.containingDeclaration, reportOn, context.trace, context.missingSupertypesResolver)
checkSupertypes(
descriptor.extensionReceiverParameter?.declaration, reportOn,
context.trace, context.missingSupertypesResolver
)
}
}
private val ReceiverParameterDescriptor.declaration
get() = value.type.constructor.declarationDescriptor
private fun checkHierarchy(declaration: DeclarationDescriptor?, reportOn: PsiElement, context: CallCheckerContext) {
if (declaration !is ClassifierDescriptor) return
checkSupertypes(declaration.defaultType, reportOn, context.trace, context.moduleDescriptor)
}
}
// true for reported error
fun checkSupertypes(
classifierType: KotlinType,
reportOn: PsiElement,
trace: BindingTrace,
moduleDescriptor: ModuleDescriptor
) {
val classifierDescriptor = classifierType.constructor.declarationDescriptor ?: return
missingSupertypesResolver: MissingSupertypesResolver
) = checkSupertypes(classifierType.constructor.declarationDescriptor, reportOn, trace, missingSupertypesResolver)
for (supertype in classifierType.supertypes()) {
val supertypeDeclaration = supertype.constructor.declarationDescriptor
// true for reported error
fun checkSupertypes(
declaration: DeclarationDescriptor?,
reportOn: PsiElement,
trace: BindingTrace,
missingSupertypesResolver: MissingSupertypesResolver
): Boolean {
if (declaration !is ClassifierDescriptor)
return false
/*
* TODO: expects are not checked, because findClassAcrossModuleDependencies does not work with actualization via type alias
* Type parameters are skipped here, bounds of type parameters are checked in declaration checker separately
* Local declarations are ignored for optimization
*/
if (supertypeDeclaration !is ClassDescriptor || supertypeDeclaration.isExpect) continue
if (supertypeDeclaration.visibility == Visibilities.LOCAL) continue
val superTypeClassId = supertypeDeclaration.classId ?: continue
val dependency = moduleDescriptor.findClassAcrossModuleDependencies(superTypeClassId)
if (dependency == null || dependency is NotFoundClasses.MockClassDescriptor) {
trace.report(
Errors.MISSING_DEPENDENCY_SUPERCLASS.on(
reportOn,
supertypeDeclaration.fqNameSafe,
classifierDescriptor.fqNameSafe
)
val missingSupertypes = missingSupertypesResolver.getMissingSuperClassifiers(declaration)
for (missingClassifier in missingSupertypes) {
trace.report(
Errors.MISSING_DEPENDENCY_SUPERCLASS.on(
reportOn,
missingClassifier.fqNameSafe,
declaration.fqNameSafe
)
}
)
}
return missingSupertypes.isNotEmpty()
}
}
@@ -961,7 +961,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
// Call must be validated with the actual, not temporary trace in order to report operator diagnostic
// Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace
CallCheckerContext callCheckerContext =
new CallCheckerContext(context, components.deprecationResolver, components.moduleDescriptor, trace);
new CallCheckerContext(
context,
components.deprecationResolver,
components.moduleDescriptor,
components.missingSupertypesResolver,
trace
);
for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, expression, callCheckerContext);
}
@@ -1038,7 +1044,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@NotNull
private CallCheckerContext createCallCheckerContext(@NotNull ExpressionTypingContext context) {
return new CallCheckerContext(context, components.deprecationResolver, components.moduleDescriptor);
return new CallCheckerContext(
context,
components.deprecationResolver,
components.moduleDescriptor,
components.missingSupertypesResolver
);
}
@Override
@@ -66,6 +66,7 @@ public class ExpressionTypingComponents {
/*package*/ DataFlowValueFactory dataFlowValueFactory;
/*package*/ NewKotlinTypeChecker kotlinTypeChecker;
/*package*/ TypeResolutionInterceptor typeResolutionInterceptor;
/*package*/ MissingSupertypesResolver missingSupertypesResolver;
@Inject
@@ -252,4 +253,9 @@ public class ExpressionTypingComponents {
public void setTypeResolutionInterceptor(@NotNull TypeResolutionInterceptor typeResolutionInterceptor) {
this.typeResolutionInterceptor = typeResolutionInterceptor;
}
@Inject
public void setMissingSupertypesResolver(@NotNull MissingSupertypesResolver missingSupertypesResolver) {
this.missingSupertypesResolver = missingSupertypesResolver;
}
}