[FIR] Fix annotations invalid references diagnostics
This commit is contained in:
committed by
TeamCityServer
parent
261098a122
commit
ae63ad0b6d
Vendored
+1
-1
@@ -5,7 +5,7 @@ interface I {
|
||||
}
|
||||
|
||||
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<!>
|
||||
{
|
||||
|
||||
+2
-1
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
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.ImplicitReceiverStack
|
||||
import org.jetbrains.kotlin.fir.resolve.SessionHolder
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
|
||||
@@ -23,7 +24,7 @@ abstract class CheckerContext {
|
||||
// Context
|
||||
abstract val implicitReceiverStack: ImplicitReceiverStack
|
||||
abstract val containingDeclarations: List<FirDeclaration>
|
||||
abstract val qualifiedAccesses: List<FirQualifiedAccess>
|
||||
abstract val qualifiedAccessOrAnnotationCalls: List<FirStatement>
|
||||
abstract val getClassCalls: List<FirGetClassCall>
|
||||
|
||||
// Suppress
|
||||
|
||||
+8
-8
@@ -11,7 +11,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
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.SessionHolder
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
||||
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.name.Name
|
||||
class PersistentCheckerContext private constructor(
|
||||
override val implicitReceiverStack: PersistentImplicitReceiverStack,
|
||||
override val containingDeclarations: PersistentList<FirDeclaration>,
|
||||
override val qualifiedAccesses: PersistentList<FirQualifiedAccess>,
|
||||
override val qualifiedAccessOrAnnotationCalls: PersistentList<FirStatement>,
|
||||
override val getClassCalls: PersistentList<FirGetClassCall>,
|
||||
override val sessionHolder: SessionHolder,
|
||||
override val returnTypeCalculator: ReturnTypeCalculator,
|
||||
@@ -47,7 +47,7 @@ class PersistentCheckerContext private constructor(
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack.add(name, value),
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
qualifiedAccessOrAnnotationCalls,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
@@ -62,7 +62,7 @@ class PersistentCheckerContext private constructor(
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations.add(declaration),
|
||||
qualifiedAccesses,
|
||||
qualifiedAccessOrAnnotationCalls,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
@@ -73,11 +73,11 @@ class PersistentCheckerContext private constructor(
|
||||
)
|
||||
}
|
||||
|
||||
fun addQualifiedAccess(qualifiedAccess: FirQualifiedAccess): PersistentCheckerContext {
|
||||
fun addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement): PersistentCheckerContext {
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses.add(qualifiedAccess),
|
||||
this.qualifiedAccessOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall),
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
@@ -92,7 +92,7 @@ class PersistentCheckerContext private constructor(
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
qualifiedAccessOrAnnotationCalls,
|
||||
getClassCalls.add(getClassCall),
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
@@ -113,7 +113,7 @@ class PersistentCheckerContext private constructor(
|
||||
return PersistentCheckerContext(
|
||||
implicitReceiverStack,
|
||||
containingDeclarations,
|
||||
qualifiedAccesses,
|
||||
qualifiedAccessOrAnnotationCalls,
|
||||
getClassCalls,
|
||||
sessionHolder,
|
||||
returnTypeCalculator,
|
||||
|
||||
+2
-2
@@ -184,8 +184,8 @@ object FirInlineDeclarationChecker : FirMemberDeclarationChecker() {
|
||||
|
||||
private fun FirQualifiedAccess.partOfCall(context: CheckerContext): Boolean {
|
||||
if (this !is FirExpression) return false
|
||||
val containingQualifiedAccess = context.qualifiedAccesses.getOrNull(context.qualifiedAccesses.size - 2) ?: return false
|
||||
if (this == containingQualifiedAccess.explicitReceiver) return true
|
||||
val containingQualifiedAccess = context.qualifiedAccessOrAnnotationCalls.getOrNull(context.qualifiedAccessOrAnnotationCalls.size - 2) ?: return false
|
||||
if (this == (containingQualifiedAccess as? FirQualifiedAccess)?.explicitReceiver) return true
|
||||
val call = containingQualifiedAccess as? FirCall ?: return false
|
||||
return call.arguments.any { it.unwrapArgument() == this }
|
||||
}
|
||||
|
||||
+2
-1
@@ -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.FirErrors
|
||||
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.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.isUnit
|
||||
|
||||
object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
|
||||
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
|
||||
val lastGetClass = context.getClassCalls.lastOrNull()
|
||||
if (lastGetClass?.argument === expression) return
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ object FirTypeParameterInQualifiedAccessChecker : FirQualifiedAccessChecker() {
|
||||
// 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
|
||||
// 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 source = expression.source ?: return
|
||||
|
||||
+12
-8
@@ -175,15 +175,19 @@ abstract class AbstractDiagnosticCollectorVisitor(
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall, data: Nothing?) {
|
||||
visitWithQualifiedAccess(functionCall)
|
||||
visitWithQualifiedAccessOrAnnotationCall(functionCall)
|
||||
}
|
||||
|
||||
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?) {
|
||||
visitWithQualifiedAccess(variableAssignment)
|
||||
visitWithQualifiedAccessOrAnnotationCall(variableAssignment)
|
||||
}
|
||||
|
||||
override fun visitGetClassCall(getClassCall: FirGetClassCall, data: Nothing?) {
|
||||
@@ -215,9 +219,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitWithQualifiedAccess(qualifiedAccess: FirQualifiedAccess) {
|
||||
return withQualifiedAccess(qualifiedAccess) {
|
||||
visitElement(qualifiedAccess, null)
|
||||
private fun visitWithQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement) {
|
||||
return withQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall) {
|
||||
visitElement(qualifiedAccessOrAnnotationCall, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +232,9 @@ abstract class AbstractDiagnosticCollectorVisitor(
|
||||
}
|
||||
|
||||
@OptIn(PrivateForInline::class)
|
||||
inline fun <R> withQualifiedAccess(qualifiedAccess: FirQualifiedAccess, block: () -> R): R {
|
||||
inline fun <R> withQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement, block: () -> R): R {
|
||||
val existingContext = context
|
||||
context = context.addQualifiedAccess(qualifiedAccess)
|
||||
context = context.addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall)
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
|
||||
+13
-7
@@ -46,9 +46,13 @@ class ErrorNodeDiagnosticCollectorComponent(
|
||||
|
||||
override fun visitErrorNamedReference(errorNamedReference: FirErrorNamedReference, data: CheckerContext) {
|
||||
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.
|
||||
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)
|
||||
if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && errorNamedReference.diagnostic is ConeUnresolvedNameError) return
|
||||
@@ -58,12 +62,14 @@ class ErrorNodeDiagnosticCollectorComponent(
|
||||
) return
|
||||
|
||||
// If the receiver cannot be resolved, we skip reporting any further problems for this call.
|
||||
if (qualifiedAccess?.dispatchReceiver.hasUnresolvedNameError() ||
|
||||
qualifiedAccess?.extensionReceiver.hasUnresolvedNameError() ||
|
||||
qualifiedAccess?.explicitReceiver.hasUnresolvedNameError()
|
||||
) return
|
||||
if (qualifiedAccessOrAnnotationCall is FirQualifiedAccess) {
|
||||
if (qualifiedAccessOrAnnotationCall.dispatchReceiver.hasUnresolvedNameError() ||
|
||||
qualifiedAccessOrAnnotationCall.extensionReceiver.hasUnresolvedNameError() ||
|
||||
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 {
|
||||
|
||||
+6
-1
@@ -336,7 +336,12 @@ class DeclarationsConverter(
|
||||
useSiteTarget = annotationUseSiteTarget ?: defaultAnnotationUseSiteTarget
|
||||
annotationTypeRef = constructorCalleePair.first
|
||||
calleeReference = buildSimpleNamedReference {
|
||||
source = this@buildAnnotationCall.source
|
||||
source = unescapedAnnotation
|
||||
.getChildNodeByType(CONSTRUCTOR_CALLEE)
|
||||
?.getChildNodeByType(TYPE_REFERENCE)
|
||||
?.getChildNodeByType(USER_TYPE)
|
||||
?.getChildNodeByType(REFERENCE_EXPRESSION)
|
||||
?.toFirSourceElement()
|
||||
this.name = name
|
||||
}
|
||||
extractArgumentsFrom(constructorCalleePair.second, stubMode)
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
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.builder.buildResolvedReifiedParameterReference
|
||||
import org.jetbrains.kotlin.fir.references.*
|
||||
@@ -410,7 +411,8 @@ class FirCallResolver(
|
||||
buildErrorReference(
|
||||
callInfo,
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
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() {}
|
||||
@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
|
||||
|
||||
annotation class annotation
|
||||
@@ -17,4 +16,4 @@ annotation class My
|
||||
|
||||
<!NOT_AN_ANNOTATION_CLASS!>@test.annotation<!> class Your
|
||||
|
||||
@My class Our
|
||||
@My class Our
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
class Foo
|
||||
|
||||
@<!NOT_AN_ANNOTATION_CLASS!>Foo<!> class Bar
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
class Foo
|
||||
|
||||
<!NOT_AN_ANNOTATION_CLASS!>@Foo<!> class Bar
|
||||
<!NOT_AN_ANNOTATION_CLASS!>@Foo<!> class Bar
|
||||
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
// 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
-1
@@ -1,7 +1,7 @@
|
||||
// !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 fun <T> listOf(t: T): List<T> = TODO()
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@
|
||||
|
||||
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() {}
|
||||
|
||||
Reference in New Issue
Block a user