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) { public void runDeclarationCheckers(@NotNull KtDeclaration declaration, @NotNull DeclarationDescriptor descriptor) {
DeclarationCheckerContext context = new DeclarationCheckerContext( DeclarationCheckerContext context = new DeclarationCheckerContext(
trace, languageVersionSettings, deprecationResolver, moduleDescriptor, expectActualTracker trace, languageVersionSettings, deprecationResolver, moduleDescriptor, expectActualTracker,
missingSupertypesResolver
); );
for (DeclarationChecker checker : declarationCheckers) { for (DeclarationChecker checker : declarationCheckers) {
checker.check(declaration, descriptor, context); checker.check(declaration, descriptor, context);
@@ -291,6 +292,7 @@ public class ModifiersChecker {
private final ExpectActualTracker expectActualTracker; private final ExpectActualTracker expectActualTracker;
private final DeprecationResolver deprecationResolver; private final DeprecationResolver deprecationResolver;
private final ModuleDescriptor moduleDescriptor; private final ModuleDescriptor moduleDescriptor;
private final MissingSupertypesResolver missingSupertypesResolver;
public ModifiersChecker( public ModifiersChecker(
@NotNull AnnotationChecker annotationChecker, @NotNull AnnotationChecker annotationChecker,
@@ -298,7 +300,8 @@ public class ModifiersChecker {
@NotNull LanguageVersionSettings languageVersionSettings, @NotNull LanguageVersionSettings languageVersionSettings,
@NotNull ExpectActualTracker expectActualTracker, @NotNull ExpectActualTracker expectActualTracker,
@NotNull DeprecationResolver deprecationResolver, @NotNull DeprecationResolver deprecationResolver,
@NotNull ModuleDescriptor moduleDescriptor @NotNull ModuleDescriptor moduleDescriptor,
@NotNull MissingSupertypesResolver missingSupertypesResolver
) { ) {
this.annotationChecker = annotationChecker; this.annotationChecker = annotationChecker;
this.declarationCheckers = declarationCheckers; this.declarationCheckers = declarationCheckers;
@@ -306,6 +309,7 @@ public class ModifiersChecker {
this.expectActualTracker = expectActualTracker; this.expectActualTracker = expectActualTracker;
this.deprecationResolver = deprecationResolver; this.deprecationResolver = deprecationResolver;
this.moduleDescriptor = moduleDescriptor; this.moduleDescriptor = moduleDescriptor;
this.missingSupertypesResolver = missingSupertypesResolver;
} }
@NotNull @NotNull
@@ -59,7 +59,8 @@ class CallCompleter(
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val effectSystem: EffectSystem, private val effectSystem: EffectSystem,
private val dataFlowValueFactory: DataFlowValueFactory private val dataFlowValueFactory: DataFlowValueFactory,
private val missingSupertypesResolver: MissingSupertypesResolver
) { ) {
fun <D : CallableDescriptor> completeCall( fun <D : CallableDescriptor> completeCall(
context: BasicCallResolutionContext, context: BasicCallResolutionContext,
@@ -78,7 +79,7 @@ class CallCompleter(
if (context.trace.wantsDiagnostics()) { if (context.trace.wantsDiagnostics()) {
if (resolvedCall == null) { if (resolvedCall == null) {
checkMissingSupertypes(context, moduleDescriptor) checkMissingSupertypes(context, missingSupertypesResolver)
} else { } else {
val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall) val calleeExpression = if (resolvedCall is VariableAsFunctionResolvedCall)
resolvedCall.variableCall.call.calleeExpression resolvedCall.variableCall.call.calleeExpression
@@ -88,7 +89,7 @@ class CallCompleter(
if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression if (calleeExpression != null && !calleeExpression.isFakeElement) calleeExpression
else resolvedCall.call.callElement else resolvedCall.call.callElement
val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor) val callCheckerContext = CallCheckerContext(context, deprecationResolver, moduleDescriptor, missingSupertypesResolver)
for (callChecker in callCheckers) { for (callChecker in callCheckers) {
callChecker.check(resolvedCall, reportOn, callCheckerContext) callChecker.check(resolvedCall, reportOn, callCheckerContext)
@@ -105,12 +106,15 @@ class CallCompleter(
return results return results
} }
private fun checkMissingSupertypes(context: BasicCallResolutionContext, moduleDescriptor: ModuleDescriptor) { private fun checkMissingSupertypes(
context: BasicCallResolutionContext,
missingSupertypesResolver: MissingSupertypesResolver
) {
val call = context.call val call = context.call
val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>() val explicitReceiver = call.explicitReceiver.safeAs<ReceiverValue>() ?: return
?: return
MissingDependencySupertypeChecker.checkSupertypes( 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.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingTrace 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.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -42,6 +43,7 @@ class CallCheckerContext @JvmOverloads constructor(
val resolutionContext: ResolutionContext<*>, val resolutionContext: ResolutionContext<*>,
override val deprecationResolver: DeprecationResolver, override val deprecationResolver: DeprecationResolver,
override val moduleDescriptor: ModuleDescriptor, override val moduleDescriptor: ModuleDescriptor,
val missingSupertypesResolver: MissingSupertypesResolver,
override val trace: BindingTrace = resolutionContext.trace override val trace: BindingTrace = resolutionContext.trace
) : CheckerContext { ) : CheckerContext {
val scope: LexicalScope val scope: LexicalScope
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.components.CompletedCallInfo import org.jetbrains.kotlin.resolve.calls.components.CompletedCallInfo
@@ -49,7 +50,8 @@ class CoroutineInferenceSession(
private val doubleColonExpressionResolver: DoubleColonExpressionResolver, private val doubleColonExpressionResolver: DoubleColonExpressionResolver,
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val typeApproximator: TypeApproximator private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver
) : ManyCandidatesResolver<CallableDescriptor>( ) : ManyCandidatesResolver<CallableDescriptor>(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns
) { ) {
@@ -222,7 +224,7 @@ class CoroutineInferenceSession(
return ResolvedAtomCompleter( return ResolvedAtomCompleter(
resultSubstitutor, context, kotlinToResolvedCallTransformer, resultSubstitutor, context, kotlinToResolvedCallTransformer,
expressionTypingServices, argumentTypeResolver, doubleColonExpressionResolver, builtIns, 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.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.LexicalScope 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.*
import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver import org.jetbrains.kotlin.types.expressions.DoubleColonExpressionResolver
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
@@ -75,7 +73,8 @@ class KotlinResolutionCallbacksImpl(
val doubleColonExpressionResolver: DoubleColonExpressionResolver, val doubleColonExpressionResolver: DoubleColonExpressionResolver,
val deprecationResolver: DeprecationResolver, val deprecationResolver: DeprecationResolver,
val moduleDescriptor: ModuleDescriptor, val moduleDescriptor: ModuleDescriptor,
val topLevelCallContext: BasicCallResolutionContext? val topLevelCallContext: BasicCallResolutionContext?,
val missingSupertypesResolver: MissingSupertypesResolver
) : KotlinResolutionCallbacks { ) : KotlinResolutionCallbacks {
class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) { class LambdaInfo(val expectedType: UnwrappedType, val contextDependency: ContextDependency) {
val returnStatements = ArrayList<Pair<KtReturnExpression, LambdaContextInfo?>>() val returnStatements = ArrayList<Pair<KtReturnExpression, LambdaContextInfo?>>()
@@ -161,7 +160,8 @@ class KotlinResolutionCallbacksImpl(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter,
callComponents, builtIns, topLevelCallContext, stubsForPostponedVariables, trace, callComponents, builtIns, topLevelCallContext, stubsForPostponedVariables, trace,
kotlinToResolvedCallTransformer, expressionTypingServices, argumentTypeResolver, kotlinToResolvedCallTransformer, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, typeApproximator,
missingSupertypesResolver
) )
} else { } else {
null null
@@ -72,7 +72,8 @@ class KotlinToResolvedCallTransformer(
private val builtIns: KotlinBuiltIns, private val builtIns: KotlinBuiltIns,
private val typeSystemContext: TypeSystemInferenceExtensionContextDelegate, private val typeSystemContext: TypeSystemInferenceExtensionContextDelegate,
private val smartCastManager: SmartCastManager, private val smartCastManager: SmartCastManager,
private val typeApproximator: TypeApproximator private val typeApproximator: TypeApproximator,
private val missingSupertypesResolver: MissingSupertypesResolver
) { ) {
companion object { companion object {
private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC private val REPORT_MISSING_NEW_INFERENCE_DIAGNOSTIC
@@ -134,7 +135,7 @@ class KotlinToResolvedCallTransformer(
val ktPrimitiveCompleter = ResolvedAtomCompleter( val ktPrimitiveCompleter = ResolvedAtomCompleter(
resultSubstitutor, context, this, expressionTypingServices, argumentTypeResolver, resultSubstitutor, context, this, expressionTypingServices, argumentTypeResolver,
doubleColonExpressionResolver, builtIns, deprecationResolver, moduleDescriptor, dataFlowValueFactory, doubleColonExpressionResolver, builtIns, deprecationResolver, moduleDescriptor, dataFlowValueFactory,
typeApproximator typeApproximator, missingSupertypesResolver
) )
if (!ErrorUtils.isError(candidate.candidateDescriptor)) { if (!ErrorUtils.isError(candidate.candidateDescriptor)) {
@@ -69,7 +69,8 @@ class PSICallResolver(
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val callableReferenceResolver: CallableReferenceResolver, private val callableReferenceResolver: CallableReferenceResolver,
private val candidateInterceptor: CandidateInterceptor private val candidateInterceptor: CandidateInterceptor,
private val missingSupertypesResolver: MissingSupertypesResolver
) { ) {
private val givenCandidatesName = Name.special("<given candidates>") private val givenCandidatesName = Name.special("<given candidates>")
@@ -182,7 +183,7 @@ class PSICallResolver(
argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer, argumentTypeResolver, languageVersionSettings, kotlinToResolvedCallTransformer,
dataFlowValueFactory, inferenceSession, constantExpressionEvaluator, typeResolver, dataFlowValueFactory, inferenceSession, constantExpressionEvaluator, typeResolver,
this, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, this, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents,
doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, context doubleColonExpressionResolver, deprecationResolver, moduleDescriptor, context, missingSupertypesResolver
) )
private fun calculateExpectedType(context: BasicCallResolutionContext): UnwrappedType? { 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.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
@@ -49,9 +50,12 @@ class ResolvedAtomCompleter(
private val deprecationResolver: DeprecationResolver, private val deprecationResolver: DeprecationResolver,
private val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
private val dataFlowValueFactory: DataFlowValueFactory, 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 val topLevelTrace = topLevelCallCheckerContext.trace
private fun complete(resolvedAtom: ResolvedAtom) { private fun complete(resolvedAtom: ResolvedAtom) {
@@ -90,7 +94,7 @@ class ResolvedAtomCompleter(
val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall val lastCall = if (resolvedCall is VariableAsFunctionResolvedCall) resolvedCall.functionCall else resolvedCall
if (ErrorUtils.isError(resolvedCall.candidateDescriptor)) { if (ErrorUtils.isError(resolvedCall.candidateDescriptor)) {
kotlinToResolvedCallTransformer.runArgumentsChecks(topLevelCallContext, topLevelTrace, lastCall as NewResolvedCallImpl<*>) kotlinToResolvedCallTransformer.runArgumentsChecks(topLevelCallContext, topLevelTrace, lastCall as NewResolvedCallImpl<*>)
checkMissingReceiverSupertypes(resolvedCall, moduleDescriptor, topLevelTrace) checkMissingReceiverSupertypes(resolvedCall, missingSupertypesResolver, topLevelTrace)
return resolvedCall return resolvedCall
} }
@@ -101,7 +105,8 @@ class ResolvedAtomCompleter(
CallCheckerContext( CallCheckerContext(
resolutionContextForPartialCall.replaceBindingTrace(topLevelTrace), resolutionContextForPartialCall.replaceBindingTrace(topLevelTrace),
deprecationResolver, deprecationResolver,
moduleDescriptor moduleDescriptor,
missingSupertypesResolver
) )
else else
topLevelCallCheckerContext topLevelCallCheckerContext
@@ -119,7 +124,7 @@ class ResolvedAtomCompleter(
private fun checkMissingReceiverSupertypes( private fun checkMissingReceiverSupertypes(
resolvedCall: ResolvedCall<CallableDescriptor>, resolvedCall: ResolvedCall<CallableDescriptor>,
moduleDescriptor: ModuleDescriptor, missingSupertypesResolver: MissingSupertypesResolver,
trace: BindingTrace trace: BindingTrace
) { ) {
val receiverValue = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver val receiverValue = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver
@@ -128,7 +133,7 @@ class ResolvedAtomCompleter(
receiverType, receiverType,
resolvedCall.call.callElement, resolvedCall.call.callElement,
trace, trace,
moduleDescriptor missingSupertypesResolver
) )
} }
} }
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
interface DeclarationChecker { interface DeclarationChecker {
@@ -33,5 +34,6 @@ class DeclarationCheckerContext(
override val languageVersionSettings: LanguageVersionSettings, override val languageVersionSettings: LanguageVersionSettings,
override val deprecationResolver: DeprecationResolver, override val deprecationResolver: DeprecationResolver,
override val moduleDescriptor: ModuleDescriptor, override val moduleDescriptor: ModuleDescriptor,
val expectActualTracker: ExpectActualTracker val expectActualTracker: ExpectActualTracker,
val missingSupertypesResolver: MissingSupertypesResolver
) : CheckerContext ) : CheckerContext
@@ -12,29 +12,27 @@ import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace 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.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall 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.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
object MissingDependencySupertypeChecker { object MissingDependencySupertypeChecker {
object ForDeclarations : DeclarationChecker { object ForDeclarations : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val trace = context.trace val trace = context.trace
val module = context.moduleDescriptor
if (descriptor is ClassDescriptor) { if (descriptor is ClassDescriptor) {
checkSupertypes(descriptor.defaultType, declaration, trace, module) checkSupertypes(descriptor, declaration, trace, context.missingSupertypesResolver)
} }
if (declaration is KtTypeParameterListOwner) { if (declaration is KtTypeParameterListOwner) {
for (ktTypeParameter in declaration.typeParameters) { for (ktTypeParameter in declaration.typeParameters) {
val typeParameterDescriptor = trace.bindingContext.get(BindingContext.TYPE_PARAMETER, ktTypeParameter) ?: continue val typeParameterDescriptor = trace.bindingContext.get(BindingContext.TYPE_PARAMETER, ktTypeParameter) ?: continue
for (upperBound in typeParameterDescriptor.upperBounds) { 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) { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
// Constructor call leads to resolution of supertypes of enclosing class if it's an inner class constructor val errorReported = checkSupertypes(
checkHierarchy(descriptor.dispatchReceiverParameter?.declaration, reportOn, context) descriptor.dispatchReceiverParameter?.declaration, reportOn,
// The constructed class' own supertypes are not resolved after constructor call, context.trace, context.missingSupertypesResolver
// so its containing declaration should not be checked. )
if (descriptor !is ConstructorDescriptor) { if (descriptor !is ConstructorDescriptor && !errorReported) {
checkHierarchy(descriptor.containingDeclaration, reportOn, context) // The constructed class' own supertypes are not resolved after constructor call,
checkHierarchy(descriptor.extensionReceiverParameter?.declaration, reportOn, context) // 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 private val ReceiverParameterDescriptor.declaration
get() = value.type.constructor.declarationDescriptor 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( fun checkSupertypes(
classifierType: KotlinType, classifierType: KotlinType,
reportOn: PsiElement, reportOn: PsiElement,
trace: BindingTrace, trace: BindingTrace,
moduleDescriptor: ModuleDescriptor missingSupertypesResolver: MissingSupertypesResolver
) { ) = checkSupertypes(classifierType.constructor.declarationDescriptor, reportOn, trace, missingSupertypesResolver)
val classifierDescriptor = classifierType.constructor.declarationDescriptor ?: return
for (supertype in classifierType.supertypes()) { // true for reported error
val supertypeDeclaration = supertype.constructor.declarationDescriptor fun checkSupertypes(
declaration: DeclarationDescriptor?,
reportOn: PsiElement,
trace: BindingTrace,
missingSupertypesResolver: MissingSupertypesResolver
): Boolean {
if (declaration !is ClassifierDescriptor)
return false
/* val missingSupertypes = missingSupertypesResolver.getMissingSuperClassifiers(declaration)
* TODO: expects are not checked, because findClassAcrossModuleDependencies does not work with actualization via type alias for (missingClassifier in missingSupertypes) {
* Type parameters are skipped here, bounds of type parameters are checked in declaration checker separately trace.report(
* Local declarations are ignored for optimization Errors.MISSING_DEPENDENCY_SUPERCLASS.on(
*/ reportOn,
if (supertypeDeclaration !is ClassDescriptor || supertypeDeclaration.isExpect) continue missingClassifier.fqNameSafe,
if (supertypeDeclaration.visibility == Visibilities.LOCAL) continue declaration.fqNameSafe
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
)
) )
} )
} }
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 // 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 // Only unary assignment expressions (++, --) and +=/... must be checked, normal assignments have the proper trace
CallCheckerContext callCheckerContext = 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) { for (CallChecker checker : components.callCheckers) {
checker.check(resolvedCall, expression, callCheckerContext); checker.check(resolvedCall, expression, callCheckerContext);
} }
@@ -1038,7 +1044,12 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@NotNull @NotNull
private CallCheckerContext createCallCheckerContext(@NotNull ExpressionTypingContext context) { 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 @Override
@@ -66,6 +66,7 @@ public class ExpressionTypingComponents {
/*package*/ DataFlowValueFactory dataFlowValueFactory; /*package*/ DataFlowValueFactory dataFlowValueFactory;
/*package*/ NewKotlinTypeChecker kotlinTypeChecker; /*package*/ NewKotlinTypeChecker kotlinTypeChecker;
/*package*/ TypeResolutionInterceptor typeResolutionInterceptor; /*package*/ TypeResolutionInterceptor typeResolutionInterceptor;
/*package*/ MissingSupertypesResolver missingSupertypesResolver;
@Inject @Inject
@@ -252,4 +253,9 @@ public class ExpressionTypingComponents {
public void setTypeResolutionInterceptor(@NotNull TypeResolutionInterceptor typeResolutionInterceptor) { public void setTypeResolutionInterceptor(@NotNull TypeResolutionInterceptor typeResolutionInterceptor) {
this.typeResolutionInterceptor = typeResolutionInterceptor; this.typeResolutionInterceptor = typeResolutionInterceptor;
} }
@Inject
public void setMissingSupertypesResolver(@NotNull MissingSupertypesResolver missingSupertypesResolver) {
this.missingSupertypesResolver = missingSupertypesResolver;
}
} }