[FIR] Fix annotations invalid references diagnostics

This commit is contained in:
Igor Yakovlev
2021-04-30 17:12:58 +02:00
committed by TeamCityServer
parent 261098a122
commit ae63ad0b6d
18 changed files with 78 additions and 39 deletions
@@ -5,7 +5,7 @@ interface I {
} }
class A { class A {
fun too(): <!NOT_AN_ANNOTATION_CLASS!>@Annotation<!> Unit {} fun too(): @<!NOT_AN_ANNOTATION_CLASS!>Annotation<!> Unit {}
fun foo(): <!REDUNDANT_RETURN_UNIT_TYPE!>Unit<!> fun foo(): <!REDUNDANT_RETURN_UNIT_TYPE!>Unit<!>
{ {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack
import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
@@ -23,7 +24,7 @@ abstract class CheckerContext {
// Context // Context
abstract val implicitReceiverStack: ImplicitReceiverStack abstract val implicitReceiverStack: ImplicitReceiverStack
abstract val containingDeclarations: List<FirDeclaration> abstract val containingDeclarations: List<FirDeclaration>
abstract val qualifiedAccesses: List<FirQualifiedAccess> abstract val qualifiedAccessOrAnnotationCalls: List<FirStatement>
abstract val getClassCalls: List<FirGetClassCall> abstract val getClassCalls: List<FirGetClassCall>
// Suppress // Suppress
@@ -11,7 +11,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack import org.jetbrains.kotlin.fir.resolve.PersistentImplicitReceiverStack
import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.name.Name
class PersistentCheckerContext private constructor( class PersistentCheckerContext private constructor(
override val implicitReceiverStack: PersistentImplicitReceiverStack, override val implicitReceiverStack: PersistentImplicitReceiverStack,
override val containingDeclarations: PersistentList<FirDeclaration>, override val containingDeclarations: PersistentList<FirDeclaration>,
override val qualifiedAccesses: PersistentList<FirQualifiedAccess>, override val qualifiedAccessOrAnnotationCalls: PersistentList<FirStatement>,
override val getClassCalls: PersistentList<FirGetClassCall>, override val getClassCalls: PersistentList<FirGetClassCall>,
override val sessionHolder: SessionHolder, override val sessionHolder: SessionHolder,
override val returnTypeCalculator: ReturnTypeCalculator, override val returnTypeCalculator: ReturnTypeCalculator,
@@ -47,7 +47,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext( return PersistentCheckerContext(
implicitReceiverStack.add(name, value), implicitReceiverStack.add(name, value),
containingDeclarations, containingDeclarations,
qualifiedAccesses, qualifiedAccessOrAnnotationCalls,
getClassCalls, getClassCalls,
sessionHolder, sessionHolder,
returnTypeCalculator, returnTypeCalculator,
@@ -62,7 +62,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext( return PersistentCheckerContext(
implicitReceiverStack, implicitReceiverStack,
containingDeclarations.add(declaration), containingDeclarations.add(declaration),
qualifiedAccesses, qualifiedAccessOrAnnotationCalls,
getClassCalls, getClassCalls,
sessionHolder, sessionHolder,
returnTypeCalculator, returnTypeCalculator,
@@ -73,11 +73,11 @@ class PersistentCheckerContext private constructor(
) )
} }
fun addQualifiedAccess(qualifiedAccess: FirQualifiedAccess): PersistentCheckerContext { fun addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement): PersistentCheckerContext {
return PersistentCheckerContext( return PersistentCheckerContext(
implicitReceiverStack, implicitReceiverStack,
containingDeclarations, containingDeclarations,
qualifiedAccesses.add(qualifiedAccess), this.qualifiedAccessOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall),
getClassCalls, getClassCalls,
sessionHolder, sessionHolder,
returnTypeCalculator, returnTypeCalculator,
@@ -92,7 +92,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext( return PersistentCheckerContext(
implicitReceiverStack, implicitReceiverStack,
containingDeclarations, containingDeclarations,
qualifiedAccesses, qualifiedAccessOrAnnotationCalls,
getClassCalls.add(getClassCall), getClassCalls.add(getClassCall),
sessionHolder, sessionHolder,
returnTypeCalculator, returnTypeCalculator,
@@ -113,7 +113,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext( return PersistentCheckerContext(
implicitReceiverStack, implicitReceiverStack,
containingDeclarations, containingDeclarations,
qualifiedAccesses, qualifiedAccessOrAnnotationCalls,
getClassCalls, getClassCalls,
sessionHolder, sessionHolder,
returnTypeCalculator, returnTypeCalculator,
@@ -184,8 +184,8 @@ object FirInlineDeclarationChecker : FirMemberDeclarationChecker() {
private fun FirQualifiedAccess.partOfCall(context: CheckerContext): Boolean { private fun FirQualifiedAccess.partOfCall(context: CheckerContext): Boolean {
if (this !is FirExpression) return false if (this !is FirExpression) return false
val containingQualifiedAccess = context.qualifiedAccesses.getOrNull(context.qualifiedAccesses.size - 2) ?: return false val containingQualifiedAccess = context.qualifiedAccessOrAnnotationCalls.getOrNull(context.qualifiedAccessOrAnnotationCalls.size - 2) ?: return false
if (this == containingQualifiedAccess.explicitReceiver) return true if (this == (containingQualifiedAccess as? FirQualifiedAccess)?.explicitReceiver) return true
val call = containingQualifiedAccess as? FirCall ?: return false val call = containingQualifiedAccess as? FirCall ?: return false
return call.arguments.any { it.unwrapArgument() == this } return call.arguments.any { it.unwrapArgument() == this }
} }
@@ -10,13 +10,14 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.isUnit
object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() { object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
val lastQualifiedAccess = context.qualifiedAccesses.lastOrNull() val lastQualifiedAccess = context.qualifiedAccessOrAnnotationCalls.lastOrNull() as? FirQualifiedAccess
if (lastQualifiedAccess?.explicitReceiver === expression) return if (lastQualifiedAccess?.explicitReceiver === expression) return
val lastGetClass = context.getClassCalls.lastOrNull() val lastGetClass = context.getClassCalls.lastOrNull()
if (lastGetClass?.argument === expression) return if (lastGetClass?.argument === expression) return
@@ -30,7 +30,7 @@ object FirTypeParameterInQualifiedAccessChecker : FirQualifiedAccessChecker() {
// Make sure the current qualified access is not part of another qualified access or class literals. // Make sure the current qualified access is not part of another qualified access or class literals.
// E.g., for `T::toString`, which is a callable reference (a subtype of qualified access), type parameter T is checked once as an // E.g., for `T::toString`, which is a callable reference (a subtype of qualified access), type parameter T is checked once as an
// explicit receiver (or LHS). When we visit `T` (as a qualified access), we should not regard it as an expression here. // explicit receiver (or LHS). When we visit `T` (as a qualified access), we should not regard it as an expression here.
if (context.qualifiedAccesses.size > 1 || context.getClassCalls.isNotEmpty()) return if (context.qualifiedAccessOrAnnotationCalls.size > 1 || context.getClassCalls.isNotEmpty()) return
val diagnostic = expression.typeRef.coneTypeParameterInQualifiedAccess ?: return val diagnostic = expression.typeRef.coneTypeParameterInQualifiedAccess ?: return
val source = expression.source ?: return val source = expression.source ?: return
@@ -175,15 +175,19 @@ abstract class AbstractDiagnosticCollectorVisitor(
} }
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?) { override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?) {
visitWithQualifiedAccess(functionCall) visitWithQualifiedAccessOrAnnotationCall(functionCall)
} }
override fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: Nothing?) { override fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: Nothing?) {
visitWithQualifiedAccess(qualifiedAccessExpression) visitWithQualifiedAccessOrAnnotationCall(qualifiedAccessExpression)
}
override fun visitAnnotationCall(annotationCall: FirAnnotationCall, data: Nothing?) {
visitWithQualifiedAccessOrAnnotationCall(annotationCall)
} }
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: Nothing?) { override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: Nothing?) {
visitWithQualifiedAccess(variableAssignment) visitWithQualifiedAccessOrAnnotationCall(variableAssignment)
} }
override fun visitGetClassCall(getClassCall: FirGetClassCall, data: Nothing?) { override fun visitGetClassCall(getClassCall: FirGetClassCall, data: Nothing?) {
@@ -215,9 +219,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
} }
} }
private fun visitWithQualifiedAccess(qualifiedAccess: FirQualifiedAccess) { private fun visitWithQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement) {
return withQualifiedAccess(qualifiedAccess) { return withQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall) {
visitElement(qualifiedAccess, null) visitElement(qualifiedAccessOrAnnotationCall, null)
} }
} }
@@ -228,9 +232,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
} }
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
inline fun <R> withQualifiedAccess(qualifiedAccess: FirQualifiedAccess, block: () -> R): R { inline fun <R> withQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement, block: () -> R): R {
val existingContext = context val existingContext = context
context = context.addQualifiedAccess(qualifiedAccess) context = context.addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall)
try { try {
return block() return block()
} finally { } finally {
@@ -46,9 +46,13 @@ class ErrorNodeDiagnosticCollectorComponent(
override fun visitErrorNamedReference(errorNamedReference: FirErrorNamedReference, data: CheckerContext) { override fun visitErrorNamedReference(errorNamedReference: FirErrorNamedReference, data: CheckerContext) {
val source = errorNamedReference.source ?: return val source = errorNamedReference.source ?: return
val qualifiedAccess = data.qualifiedAccesses.lastOrNull()?.takeIf { val qualifiedAccessOrAnnotationCall = data.qualifiedAccessOrAnnotationCalls.lastOrNull()?.takeIf {
// Use the source of the enclosing FirQualifiedAccessExpression if it is exactly the call to the erroneous callee. // Use the source of the enclosing FirQualifiedAccessExpression if it is exactly the call to the erroneous callee.
it is FirQualifiedAccessExpression && it.calleeReference == errorNamedReference when (it) {
is FirQualifiedAccessExpression -> it.calleeReference == errorNamedReference
is FirAnnotationCall -> it.calleeReference == errorNamedReference
else -> false
}
} }
// Don't report duplicated unresolved reference on annotation entry (already reported on its type) // Don't report duplicated unresolved reference on annotation entry (already reported on its type)
if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && errorNamedReference.diagnostic is ConeUnresolvedNameError) return if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && errorNamedReference.diagnostic is ConeUnresolvedNameError) return
@@ -58,12 +62,14 @@ class ErrorNodeDiagnosticCollectorComponent(
) return ) return
// If the receiver cannot be resolved, we skip reporting any further problems for this call. // If the receiver cannot be resolved, we skip reporting any further problems for this call.
if (qualifiedAccess?.dispatchReceiver.hasUnresolvedNameError() || if (qualifiedAccessOrAnnotationCall is FirQualifiedAccess) {
qualifiedAccess?.extensionReceiver.hasUnresolvedNameError() || if (qualifiedAccessOrAnnotationCall.dispatchReceiver.hasUnresolvedNameError() ||
qualifiedAccess?.explicitReceiver.hasUnresolvedNameError() qualifiedAccessOrAnnotationCall.extensionReceiver.hasUnresolvedNameError() ||
) return qualifiedAccessOrAnnotationCall.explicitReceiver.hasUnresolvedNameError()
) return
}
reportFirDiagnostic(errorNamedReference.diagnostic, source, reporter, data, qualifiedAccess?.source) reportFirDiagnostic(errorNamedReference.diagnostic, source, reporter, data, qualifiedAccessOrAnnotationCall?.source)
} }
private fun FirExpression?.hasUnresolvedNameError(): Boolean { private fun FirExpression?.hasUnresolvedNameError(): Boolean {
@@ -336,7 +336,12 @@ class DeclarationsConverter(
useSiteTarget = annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget useSiteTarget = annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget
annotationTypeRef = constructorCalleePair.first annotationTypeRef = constructorCalleePair.first
calleeReference = buildSimpleNamedReference { calleeReference = buildSimpleNamedReference {
source = this@buildAnnotationCall.source source = unescapedAnnotation
.getChildNodeByType(CONSTRUCTOR_CALLEE)
?.getChildNodeByType(TYPE_REFERENCE)
?.getChildNodeByType(USER_TYPE)
?.getChildNodeByType(REFERENCE_EXPRESSION)
?.toFirSourceElement()
this.name = name this.name = name
} }
extractArgumentsFrom(constructorCalleePair.second, stubMode) extractArgumentsFrom(constructorCalleePair.second, stubMode)
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.ConeStubDiagnostic
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedReifiedParameterReference import org.jetbrains.kotlin.fir.expressions.builder.buildResolvedReifiedParameterReference
import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.*
@@ -410,7 +411,8 @@ class FirCallResolver(
buildErrorReference( buildErrorReference(
callInfo, callInfo,
if (annotationClassSymbol != null) ConeIllegalAnnotationError(reference.name) if (annotationClassSymbol != null) ConeIllegalAnnotationError(reference.name)
else ConeUnresolvedNameError(reference.name), //calleeReference and annotationTypeRef are both error nodes so we need to avoid doubling of the diagnostic report
else ConeStubDiagnostic(ConeUnresolvedNameError(reference.name)),
reference.source reference.source
) )
} }
@@ -1,6 +1,6 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
import java.util.ArrayList import java.util.ArrayList
<!NOT_AN_ANNOTATION_CLASS!>@ArrayList<Int>(1, 1)<!> fun b() {} @<!NOT_AN_ANNOTATION_CLASS!>ArrayList<!><Int>(1, 1) fun b() {}
@<!UNRESOLVED_REFERENCE!>Xoo<!>(<!UNRESOLVED_REFERENCE!>x<!>) fun c() {} @<!UNRESOLVED_REFERENCE!>Xoo<!>(<!UNRESOLVED_REFERENCE!>x<!>) fun c() {}
@java.lang.Deprecated(<!TOO_MANY_ARGUMENTS, UNRESOLVED_REFERENCE!>x<!>) fun a() {} @java.lang.Deprecated(<!TOO_MANY_ARGUMENTS, UNRESOLVED_REFERENCE!>x<!>) fun a() {}
@@ -0,0 +1,19 @@
// FILE: a.kt
annotation class annotation
// FILE: test/b.kt
package test
@test.<!NOT_AN_ANNOTATION_CLASS!>annotation<!> class annotation
// FILE: other/c.kt
package other
annotation class My
@test.<!NOT_AN_ANNOTATION_CLASS!>annotation<!> class Your
@My class Our
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// FILE: a.kt // FILE: a.kt
annotation class annotation annotation class annotation
@@ -0,0 +1,3 @@
class Foo
@<!NOT_AN_ANNOTATION_CLASS!>Foo<!> class Bar
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
class Foo class Foo
<!NOT_AN_ANNOTATION_CLASS!>@Foo<!> class Bar <!NOT_AN_ANNOTATION_CLASS!>@Foo<!> class Bar
@@ -1,2 +1,2 @@
// Class constructor parameter CAN be recursively annotated // Class constructor parameter CAN be recursively annotated
class RecursivelyAnnotated(<!NOT_AN_ANNOTATION_CLASS, NOT_AN_ANNOTATION_CLASS!>@RecursivelyAnnotated(1)<!> val x: Int) class RecursivelyAnnotated(@<!NOT_AN_ANNOTATION_CLASS, NOT_AN_ANNOTATION_CLASS!>RecursivelyAnnotated<!>(1) val x: Int)
@@ -1,7 +1,7 @@
// !CHECK_TYPE // !CHECK_TYPE
@<!INVISIBLE_REFERENCE!>kotlin.internal.InlineOnly<!> @kotlin.internal.<!INVISIBLE_REFERENCE!>InlineOnly<!>
public inline fun <C, R> C.ifEmpty(f: () -> R): R where C : Collection<*>, C : R = if (isEmpty()) f() else this public inline fun <C, R> C.ifEmpty(f: () -> R): R where C : Collection<*>, C : R = if (isEmpty()) f() else this
public fun <T> listOf(t: T): List<T> = TODO() public fun <T> listOf(t: T): List<T> = TODO()
@@ -2,5 +2,5 @@
package a.b package a.b
@<error descr="[INVISIBLE_REFERENCE] Symbol kotlin/internal/InlineOnly.InlineOnly is invisible">kotlin.internal.InlineOnly</error> @kotlin.internal.<error descr="[INVISIBLE_REFERENCE] Symbol kotlin/internal/InlineOnly.InlineOnly is invisible">InlineOnly</error>
inline fun foo() {} inline fun foo() {}