Move ClassifierUsageChecker invocation lower in LazyTopDownAnalyzer
Invoking checkers right after resolution of qualified expressions is too early: DeprecatedClassifierUsageChecker tries to load the annotations of a referenced classifier to report deprecation, and that may result in building light classes for not yet analyzed declarations #KT-13954 Fixed
This commit is contained in:
@@ -17,8 +17,54 @@
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
|
||||
|
||||
interface ClassifierUsageChecker {
|
||||
fun check(targetDescriptor: ClassifierDescriptor, trace: BindingTrace, element: PsiElement)
|
||||
|
||||
companion object {
|
||||
fun check(declarations: Collection<PsiElement>, trace: BindingTrace, checkers: Iterable<ClassifierUsageChecker>) {
|
||||
val visitor = object : KtTreeVisitorVoid() {
|
||||
override fun visitReferenceExpression(expression: KtReferenceExpression) {
|
||||
super.visitReferenceExpression(expression)
|
||||
|
||||
if (expression is KtNameReferenceExpression && expression.getReferencedNameElementType() == KtTokens.SUPER_KEYWORD) {
|
||||
// Do not run checkers here because super expressions is not a proper expression. For example,
|
||||
// it doesn't make sense to report deprecation on "super" in "super.foo()" if the super class is deprecated.
|
||||
return
|
||||
}
|
||||
|
||||
val target = trace.bindingContext.get(BindingContext.REFERENCE_TARGET, expression) as? ClassifierDescriptor ?: return
|
||||
|
||||
for (checker in checkers) {
|
||||
checker.check(target, trace, expression)
|
||||
}
|
||||
|
||||
if (isReferenceToCompanionViaOuterClass(expression, target)) {
|
||||
val outerClass = target.containingDeclaration as ClassDescriptor
|
||||
for (checker in checkers) {
|
||||
checker.check(outerClass, trace, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff [expression] references the companion of a class Foo via the name Foo, e.g. in `Foo.bar()`
|
||||
*/
|
||||
private fun isReferenceToCompanionViaOuterClass(expression: KtReferenceExpression, target: ClassifierDescriptor?): Boolean {
|
||||
return DescriptorUtils.isCompanionObject(target) &&
|
||||
trace.bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, expression] != null
|
||||
}
|
||||
}
|
||||
|
||||
for (declaration in declarations) {
|
||||
declaration.accept(visitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ class LazyTopDownAnalyzer(
|
||||
private val declarationScopeProvider: DeclarationScopeProvider,
|
||||
private val qualifiedExpressionResolver: QualifiedExpressionResolver,
|
||||
private val identifierChecker: IdentifierChecker,
|
||||
private val languageVersionSettings: LanguageVersionSettings
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
private val classifierUsageCheckers: Iterable<ClassifierUsageChecker>
|
||||
) {
|
||||
fun analyzeDeclarations(topDownAnalysisMode: TopDownAnalysisMode, declarations: Collection<PsiElement>, outerDataFlowInfo: DataFlowInfo): TopDownAnalysisContext {
|
||||
|
||||
@@ -204,6 +205,8 @@ class LazyTopDownAnalyzer(
|
||||
|
||||
bodyResolver.resolveBodies(c)
|
||||
|
||||
ClassifierUsageChecker.check(declarations, trace, classifierUsageCheckers)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
+5
-14
@@ -25,12 +25,9 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
|
||||
fun resolveQualifierAsReceiverInExpression(
|
||||
qualifier: Qualifier,
|
||||
selector: DeclarationDescriptor?,
|
||||
context: ExpressionTypingContext,
|
||||
classifierUsageCheckers: Iterable<ClassifierUsageChecker>
|
||||
qualifier: Qualifier, selector: DeclarationDescriptor?, context: ExpressionTypingContext
|
||||
): DeclarationDescriptor {
|
||||
val referenceTarget = resolveQualifierReferenceTarget(qualifier, selector, context, classifierUsageCheckers)
|
||||
val referenceTarget = resolveQualifierReferenceTarget(qualifier, selector, context)
|
||||
|
||||
if (referenceTarget is TypeParameterDescriptor) {
|
||||
context.trace.report(Errors.TYPE_PARAMETER_ON_LHS_OF_DOT.on(qualifier.referenceExpression, referenceTarget))
|
||||
@@ -40,11 +37,9 @@ fun resolveQualifierAsReceiverInExpression(
|
||||
}
|
||||
|
||||
fun resolveQualifierAsStandaloneExpression(
|
||||
qualifier: Qualifier,
|
||||
context: ExpressionTypingContext,
|
||||
classifierUsageCheckers: Iterable<ClassifierUsageChecker>
|
||||
qualifier: Qualifier, context: ExpressionTypingContext
|
||||
): DeclarationDescriptor {
|
||||
val referenceTarget = resolveQualifierReferenceTarget(qualifier, null, context, classifierUsageCheckers)
|
||||
val referenceTarget = resolveQualifierReferenceTarget(qualifier, null, context)
|
||||
|
||||
when (referenceTarget) {
|
||||
is TypeAliasDescriptor -> {
|
||||
@@ -73,8 +68,7 @@ fun resolveQualifierAsStandaloneExpression(
|
||||
private fun resolveQualifierReferenceTarget(
|
||||
qualifier: Qualifier,
|
||||
selector: DeclarationDescriptor?,
|
||||
context: ExpressionTypingContext,
|
||||
classifierUsageCheckers: Iterable<ClassifierUsageChecker>
|
||||
context: ExpressionTypingContext
|
||||
): DeclarationDescriptor {
|
||||
if (qualifier is TypeParameterQualifier) {
|
||||
return qualifier.descriptor
|
||||
@@ -110,9 +104,6 @@ private fun resolveQualifierReferenceTarget(
|
||||
context.trace.recordType(qualifier.expression, classValueTypeDescriptor.defaultType)
|
||||
if (classifier.hasCompanionObject) {
|
||||
context.trace.record(BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, qualifier.referenceExpression, classifier)
|
||||
for (checker in classifierUsageCheckers) {
|
||||
checker.check(classValueDescriptor, context.trace, qualifier.referenceExpression)
|
||||
}
|
||||
}
|
||||
return classValueTypeDescriptor
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.types.expressions.isWithoutValueArguments
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
|
||||
class QualifiedExpressionResolver(val classifierUsageCheckers: Iterable<ClassifierUsageChecker>) {
|
||||
class QualifiedExpressionResolver {
|
||||
fun resolvePackageHeader(
|
||||
packageDirective: KtPackageDirective,
|
||||
module: ModuleDescriptor,
|
||||
@@ -376,9 +376,7 @@ class QualifiedExpressionResolver(val classifierUsageCheckers: Iterable<Classifi
|
||||
}
|
||||
}
|
||||
|
||||
val classifierDescriptor = scopeForFirstPart?.let {
|
||||
it.findClassifier(firstPart.name, firstPart.location)
|
||||
}
|
||||
val classifierDescriptor = scopeForFirstPart?.findClassifier(firstPart.name, firstPart.location)
|
||||
|
||||
if (classifierDescriptor != null) {
|
||||
storeResult(trace, firstPart.expression, classifierDescriptor, shouldBeVisibleFrom, position)
|
||||
@@ -626,12 +624,6 @@ class QualifiedExpressionResolver(val classifierUsageCheckers: Iterable<Classifi
|
||||
|
||||
trace.record(BindingContext.REFERENCE_TARGET, referenceExpression, descriptor)
|
||||
|
||||
if (descriptor is ClassifierDescriptor) {
|
||||
for (checker in classifierUsageCheckers) {
|
||||
checker.check(descriptor, trace, referenceExpression)
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor is DeclarationDescriptorWithVisibility) {
|
||||
val fromToCheck =
|
||||
if (shouldBeVisibleFrom is PackageFragmentDescriptor && shouldBeVisibleFrom.source == SourceElement.NO_SOURCE && referenceExpression.containingFile !is DummyHolder) {
|
||||
|
||||
@@ -70,8 +70,7 @@ class CallExpressionResolver(
|
||||
private val argumentTypeResolver: ArgumentTypeResolver,
|
||||
private val dataFlowAnalyzer: DataFlowAnalyzer,
|
||||
private val builtIns: KotlinBuiltIns,
|
||||
private val qualifiedExpressionResolver: QualifiedExpressionResolver,
|
||||
private val classifierUsageCheckers: Iterable<ClassifierUsageChecker>
|
||||
private val qualifiedExpressionResolver: QualifiedExpressionResolver
|
||||
) {
|
||||
private lateinit var expressionTypingServices: ExpressionTypingServices
|
||||
|
||||
@@ -162,7 +161,7 @@ class CallExpressionResolver(
|
||||
val temporaryForQualifier = TemporaryTraceAndCache.create(context, "trace to resolve as qualifier", nameExpression)
|
||||
val contextForQualifier = context.replaceTraceAndCache(temporaryForQualifier)
|
||||
qualifiedExpressionResolver.resolveNameExpressionAsQualifierForDiagnostics(nameExpression, receiver, contextForQualifier)?.let {
|
||||
resolveQualifierAsStandaloneExpression(it, contextForQualifier, classifierUsageCheckers)
|
||||
resolveQualifierAsStandaloneExpression(it, contextForQualifier)
|
||||
temporaryForQualifier.commit()
|
||||
} ?: temporaryForVariable.commit()
|
||||
return noTypeInfo(context)
|
||||
@@ -225,7 +224,7 @@ class CallExpressionResolver(
|
||||
val type = functionDescriptor.returnType
|
||||
// Extracting jump out possible and jump point flow info from arguments, if any
|
||||
val arguments = callExpression.valueArguments
|
||||
val resultFlowInfo = resolvedCall!!.dataFlowInfoForArguments.resultInfo
|
||||
val resultFlowInfo = resolvedCall.dataFlowInfoForArguments.resultInfo
|
||||
var jumpFlowInfo = resultFlowInfo
|
||||
var jumpOutPossible = false
|
||||
for (argument in arguments) {
|
||||
@@ -449,7 +448,7 @@ class CallExpressionResolver(
|
||||
context.trace.get(BindingContext.REFERENCE_TARGET, it)
|
||||
}
|
||||
|
||||
resolveQualifierAsReceiverInExpression(qualifier, selectorDescriptor, context, classifierUsageCheckers)
|
||||
resolveQualifierAsReceiverInExpression(qualifier, selectorDescriptor, context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+4
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.createDeprecationDiagnostic
|
||||
import org.jetbrains.kotlin.resolve.getDeprecation
|
||||
|
||||
@@ -38,6 +39,9 @@ object DeprecatedCallChecker : CallChecker {
|
||||
}
|
||||
|
||||
private fun check(targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) {
|
||||
// Objects will be checked by DeprecatedClassifierUsageChecker
|
||||
if (targetDescriptor is FakeCallableDescriptorForObject) return
|
||||
|
||||
val deprecation = targetDescriptor.getDeprecation()
|
||||
|
||||
// avoid duplicating diagnostic when deprecation for property effectively deprecates setter
|
||||
|
||||
Reference in New Issue
Block a user