[FIR] Add diagnostic when annotation argument is resolved ambiguously
Annotation arguments that are resolved in COMPILER_REQUIRED_ANNOTATIONS phase are resolved again in ANNOTATION_ARGUMENTS phase. If they resolve to a different symbol, report an error. KT-56177
This commit is contained in:
committed by
Space Team
parent
9dda5e4fcd
commit
2139914061
+9
@@ -1007,6 +1007,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT) { firDiagnostic ->
|
||||||
|
AmbiguousAnnotationArgumentImpl(
|
||||||
|
firDiagnostic.a.map { firBasedSymbol ->
|
||||||
|
firSymbolBuilder.buildSymbol(firBasedSymbol)
|
||||||
|
},
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirJsErrors.WRONG_JS_QUALIFIER) { firDiagnostic ->
|
add(FirJsErrors.WRONG_JS_QUALIFIER) { firDiagnostic ->
|
||||||
WrongJsQualifierImpl(
|
WrongJsQualifierImpl(
|
||||||
firDiagnostic as KtPsiDiagnostic,
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
|||||||
+5
@@ -728,6 +728,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val typeFromTypesPhase: KtType
|
abstract val typeFromTypesPhase: KtType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class AmbiguousAnnotationArgument : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = AmbiguousAnnotationArgument::class
|
||||||
|
abstract val symbols: List<KtSymbol>
|
||||||
|
}
|
||||||
|
|
||||||
abstract class WrongJsQualifier : KtFirDiagnostic<KtElement>() {
|
abstract class WrongJsQualifier : KtFirDiagnostic<KtElement>() {
|
||||||
override val diagnosticClass get() = WrongJsQualifier::class
|
override val diagnosticClass get() = WrongJsQualifier::class
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -873,6 +873,12 @@ internal class PluginAnnotationAmbiguityImpl(
|
|||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
) : KtFirDiagnostic.PluginAnnotationAmbiguity(), KtAbstractFirDiagnostic<PsiElement>
|
) : KtFirDiagnostic.PluginAnnotationAmbiguity(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class AmbiguousAnnotationArgumentImpl(
|
||||||
|
override val symbols: List<KtSymbol>,
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.AmbiguousAnnotationArgument(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
internal class WrongJsQualifierImpl(
|
internal class WrongJsQualifierImpl(
|
||||||
override val firDiagnostic: KtPsiDiagnostic,
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
|
|||||||
+6
@@ -35874,6 +35874,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationTargetResolvedAmbiguously.kt")
|
||||||
|
public void testAnnotationTargetResolvedAmbiguously() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationTargetResolvedAmbiguously.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
||||||
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
||||||
|
|||||||
+6
@@ -35874,6 +35874,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationTargetResolvedAmbiguously.kt")
|
||||||
|
public void testAnnotationTargetResolvedAmbiguously() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationTargetResolvedAmbiguously.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
||||||
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
||||||
|
|||||||
+6
@@ -35970,6 +35970,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationTargetResolvedAmbiguously.kt")
|
||||||
|
public void testAnnotationTargetResolvedAmbiguously() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationTargetResolvedAmbiguously.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
||||||
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
||||||
|
|||||||
+5
-1
@@ -309,10 +309,14 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val WRONG_EXTENSION_FUNCTION_TYPE_WARNING by warning<KtAnnotationEntry>()
|
val WRONG_EXTENSION_FUNCTION_TYPE_WARNING by warning<KtAnnotationEntry>()
|
||||||
val ANNOTATION_IN_WHERE_CLAUSE_ERROR by error<KtAnnotationEntry>()
|
val ANNOTATION_IN_WHERE_CLAUSE_ERROR by error<KtAnnotationEntry>()
|
||||||
|
|
||||||
val PLUGIN_ANNOTATION_AMBIGUITY by error<PsiElement>() {
|
val PLUGIN_ANNOTATION_AMBIGUITY by error<PsiElement> {
|
||||||
parameter<ConeKotlinType>("typeFromCompilerPhase")
|
parameter<ConeKotlinType>("typeFromCompilerPhase")
|
||||||
parameter<ConeKotlinType>("typeFromTypesPhase")
|
parameter<ConeKotlinType>("typeFromTypesPhase")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val AMBIGUOUS_ANNOTATION_ARGUMENT by error<PsiElement> {
|
||||||
|
parameter<List<FirBasedSymbol<*>>>("symbols")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val OPT_IN by object : DiagnosticGroup("OptIn") {
|
val OPT_IN by object : DiagnosticGroup("OptIn") {
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ object FirErrors {
|
|||||||
val WRONG_EXTENSION_FUNCTION_TYPE_WARNING by warning0<KtAnnotationEntry>()
|
val WRONG_EXTENSION_FUNCTION_TYPE_WARNING by warning0<KtAnnotationEntry>()
|
||||||
val ANNOTATION_IN_WHERE_CLAUSE_ERROR by error0<KtAnnotationEntry>()
|
val ANNOTATION_IN_WHERE_CLAUSE_ERROR by error0<KtAnnotationEntry>()
|
||||||
val PLUGIN_ANNOTATION_AMBIGUITY by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
val PLUGIN_ANNOTATION_AMBIGUITY by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
||||||
|
val AMBIGUOUS_ANNOTATION_ARGUMENT by error1<PsiElement, List<FirBasedSymbol<*>>>()
|
||||||
|
|
||||||
// OptIn
|
// OptIn
|
||||||
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
val OPT_IN_USAGE by warning2<PsiElement, FqName, String>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||||
|
|||||||
+7
-1
@@ -58,6 +58,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_WITHOUT_EXPECT
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_WITHOUT_EXPECT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ACTUALS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ACTUALS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ALTERED_ASSIGN
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ALTERED_ASSIGN
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ANONYMOUS_TYPE_INFERRED
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ANONYMOUS_TYPE_INFERRED
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_EXPECTS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_EXPECTS
|
||||||
@@ -928,10 +929,15 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
|||||||
)
|
)
|
||||||
map.put(
|
map.put(
|
||||||
PLUGIN_ANNOTATION_AMBIGUITY,
|
PLUGIN_ANNOTATION_AMBIGUITY,
|
||||||
"Resolution of annotation type is ambigous between {1} and plugin annotation {0}. Please specify fully qualified name of annotation",
|
"Resolution of annotation type is ambiguous between {1} and plugin annotation {0}. Please specify fully qualified name of annotation",
|
||||||
RENDER_TYPE,
|
RENDER_TYPE,
|
||||||
RENDER_TYPE
|
RENDER_TYPE
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
AMBIGUOUS_ANNOTATION_ARGUMENT,
|
||||||
|
"Resolution of annotation argument is ambiguous between {0}. Please specify fully qualified name of argument",
|
||||||
|
SYMBOLS,
|
||||||
|
)
|
||||||
|
|
||||||
// Exposed visibility group // #
|
// Exposed visibility group // #
|
||||||
map.put(
|
map.put(
|
||||||
|
|||||||
+2
@@ -132,6 +132,8 @@ private fun ConeDiagnostic.toKtDiagnostic(
|
|||||||
is ConeAmbiguouslyResolvedAnnotationFromPlugin -> {
|
is ConeAmbiguouslyResolvedAnnotationFromPlugin -> {
|
||||||
FirErrors.PLUGIN_ANNOTATION_AMBIGUITY.createOn(source, typeFromCompilerPhase, typeFromTypesPhase)
|
FirErrors.PLUGIN_ANNOTATION_AMBIGUITY.createOn(source, typeFromCompilerPhase, typeFromTypesPhase)
|
||||||
}
|
}
|
||||||
|
is ConeAmbiguouslyResolvedAnnotationArgument ->
|
||||||
|
FirErrors.AMBIGUOUS_ANNOTATION_ARGUMENT.createOn(source, listOfNotNull(symbolFromCompilerPhase, symbolFromAnnotationArgumentsPhase))
|
||||||
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
|
is ConeAmbiguousFunctionTypeKinds -> FirErrors.AMBIGUOUS_FUNCTION_TYPE_KIND.createOn(source, kinds)
|
||||||
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
|
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ fun FirResolvedQualifier.continueQualifier(
|
|||||||
packageFqName = this@continueQualifier.packageFqName
|
packageFqName = this@continueQualifier.packageFqName
|
||||||
relativeClassFqName = this@continueQualifier.relativeClassFqName?.child(name)
|
relativeClassFqName = this@continueQualifier.relativeClassFqName?.child(name)
|
||||||
symbol = nestedClassSymbol as FirClassLikeSymbol<*>
|
symbol = nestedClassSymbol as FirClassLikeSymbol<*>
|
||||||
|
isFullyQualified = true
|
||||||
|
|
||||||
val outerTypeArguments = this.typeArguments.toList()
|
val outerTypeArguments = this.typeArguments.toList()
|
||||||
this.typeArguments.clear()
|
this.typeArguments.clear()
|
||||||
@@ -180,6 +181,7 @@ private fun FqName.continueQualifierInPackage(
|
|||||||
apiVersion
|
apiVersion
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
isFullyQualified = true
|
||||||
}.apply {
|
}.apply {
|
||||||
resultType = components.typeForQualifier(this)
|
resultType = components.typeForQualifier(this)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -139,6 +139,7 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer(
|
|||||||
relativeClassFqName = symbol.classId.relativeClassName
|
relativeClassFqName = symbol.classId.relativeClassName
|
||||||
typeRef = FirImplicitUnitTypeRef(receiver.typeRef.source)
|
typeRef = FirImplicitUnitTypeRef(receiver.typeRef.source)
|
||||||
this.symbol = symbol
|
this.symbol = symbol
|
||||||
|
isFullyQualified = segments.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve enum entry by name from the declarations of the receiver.
|
// Resolve enum entry by name from the declarations of the receiver.
|
||||||
|
|||||||
+80
-1
@@ -7,12 +7,26 @@ package org.jetbrains.kotlin.fir.resolve.transformers.plugin
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguouslyResolvedAnnotationArgument
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformerDispatcher
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirDeclarationsResolveTransformer
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirExpressionsResolveTransformer
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
|
||||||
open class FirAnnotationArgumentsResolveTransformer(
|
open class FirAnnotationArgumentsResolveTransformer(
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
@@ -252,9 +266,74 @@ abstract class AbstractFirExpressionsResolveTransformerForAnnotations(transforme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of enum class IDs that are resolved in COMPILER_REQUIRED_ANNOTATIONS phase that need to be rechecked here.
|
||||||
|
*/
|
||||||
|
private val classIdsToCheck: Set<ClassId> = setOf(StandardClassIds.DeprecationLevel, StandardClassIds.AnnotationTarget)
|
||||||
|
|
||||||
private class FirExpressionsResolveTransformerForSpecificAnnotations(transformer: FirAbstractBodyResolveTransformerDispatcher) :
|
private class FirExpressionsResolveTransformerForSpecificAnnotations(transformer: FirAbstractBodyResolveTransformerDispatcher) :
|
||||||
AbstractFirExpressionsResolveTransformerForAnnotations(transformer) {
|
AbstractFirExpressionsResolveTransformerForAnnotations(transformer) {
|
||||||
|
|
||||||
|
override fun transformQualifiedAccessExpression(
|
||||||
|
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||||
|
data: ResolutionMode
|
||||||
|
): FirStatement {
|
||||||
|
val calleeReference = qualifiedAccessExpression.calleeReference
|
||||||
|
if (calleeReference is FirResolvedNamedReference &&
|
||||||
|
calleeReference.resolvedSymbol.let { it is FirEnumEntrySymbol && it.containingClassLookupTag()?.classId in classIdsToCheck } &&
|
||||||
|
qualifiedAccessExpression is FirPropertyAccessExpression
|
||||||
|
) {
|
||||||
|
val symbolFromCompilerPhase = calleeReference.resolvedSymbol
|
||||||
|
|
||||||
|
(qualifiedAccessExpression.explicitReceiver as? FirResolvedQualifier)?.let {
|
||||||
|
qualifiedAccessExpression.replaceResolvedQualifierReceiver(it)
|
||||||
|
}
|
||||||
|
qualifiedAccessExpression.replaceDispatchReceiver(FirNoReceiverExpression)
|
||||||
|
qualifiedAccessExpression.replaceTypeRef(noExpectedType)
|
||||||
|
qualifiedAccessExpression.replaceCalleeReference(buildSimpleNamedReference {
|
||||||
|
source = calleeReference.source
|
||||||
|
name = calleeReference.name
|
||||||
|
})
|
||||||
|
|
||||||
|
val resolved = super.transformQualifiedAccessExpression(qualifiedAccessExpression, data)
|
||||||
|
|
||||||
|
if (resolved is FirQualifiedAccessExpression) {
|
||||||
|
// The initial resolution must have been to an enum entry. Report ambiguity if symbolFromArgumentsPhase is different to
|
||||||
|
// original symbol including null (meaning we would resolve to something other than an enum entry).
|
||||||
|
val symbolFromArgumentsPhase = resolved.calleeReference.toResolvedBaseSymbol()
|
||||||
|
if (symbolFromCompilerPhase != symbolFromArgumentsPhase) {
|
||||||
|
resolved.replaceCalleeReference(buildErrorNamedReference {
|
||||||
|
source = resolved.calleeReference.source
|
||||||
|
diagnostic = ConeAmbiguouslyResolvedAnnotationArgument(symbolFromCompilerPhase, symbolFromArgumentsPhase)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.transformQualifiedAccessExpression(qualifiedAccessExpression, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirQualifiedAccessExpression.replaceResolvedQualifierReceiver(receiver: FirResolvedQualifier) {
|
||||||
|
var lastReceiver = buildPropertyAccessExpression {
|
||||||
|
source = receiver.source
|
||||||
|
this.calleeReference = buildSimpleNamedReference {
|
||||||
|
val classId = receiver.classId ?: return
|
||||||
|
name = classId.relativeClassName.shortName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
replaceExplicitReceiver(lastReceiver)
|
||||||
|
|
||||||
|
if (receiver.isFullyQualified) {
|
||||||
|
for (segment in receiver.packageFqName.pathSegments().asReversed()) {
|
||||||
|
lastReceiver.replaceExplicitReceiver(buildPropertyAccessExpression {
|
||||||
|
this.calleeReference = buildSimpleNamedReference { name = segment }
|
||||||
|
}.also { lastReceiver = it })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun resolveQualifiedAccessAndSelectCandidate(
|
override fun resolveQualifiedAccessAndSelectCandidate(
|
||||||
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
qualifiedAccessExpression: FirQualifiedAccessExpression,
|
||||||
isUsedAsReceiver: Boolean,
|
isUsedAsReceiver: Boolean,
|
||||||
|
|||||||
+12
@@ -251,3 +251,15 @@ class ConeAmbiguouslyResolvedAnnotationFromPlugin(
|
|||||||
- types stage: $typeFromTypesPhase
|
- types stage: $typeFromTypesPhase
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConeAmbiguouslyResolvedAnnotationArgument(
|
||||||
|
val symbolFromCompilerPhase: FirBasedSymbol<*>,
|
||||||
|
val symbolFromAnnotationArgumentsPhase: FirBasedSymbol<*>?
|
||||||
|
) : ConeDiagnostic {
|
||||||
|
override val reason: String
|
||||||
|
get() = """
|
||||||
|
Annotation symbol resolved differently on compiler annotation and symbols stages:
|
||||||
|
- compiler annotations: $symbolFromCompilerPhase
|
||||||
|
- compiler arguments stage: $symbolFromAnnotationArgumentsPhase
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|||||||
+1
@@ -31,6 +31,7 @@ abstract class FirErrorResolvedQualifier : FirResolvedQualifier(), FirDiagnostic
|
|||||||
abstract override val symbol: FirClassLikeSymbol<*>?
|
abstract override val symbol: FirClassLikeSymbol<*>?
|
||||||
abstract override val isNullableLHSForCallableReference: Boolean
|
abstract override val isNullableLHSForCallableReference: Boolean
|
||||||
abstract override val resolvedToCompanionObject: Boolean
|
abstract override val resolvedToCompanionObject: Boolean
|
||||||
|
abstract override val isFullyQualified: Boolean
|
||||||
abstract override val nonFatalDiagnostics: List<ConeDiagnostic>
|
abstract override val nonFatalDiagnostics: List<ConeDiagnostic>
|
||||||
abstract override val typeArguments: List<FirTypeProjection>
|
abstract override val typeArguments: List<FirTypeProjection>
|
||||||
abstract override val diagnostic: ConeDiagnostic
|
abstract override val diagnostic: ConeDiagnostic
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ abstract class FirResolvedQualifier : FirExpression() {
|
|||||||
abstract val symbol: FirClassLikeSymbol<*>?
|
abstract val symbol: FirClassLikeSymbol<*>?
|
||||||
abstract val isNullableLHSForCallableReference: Boolean
|
abstract val isNullableLHSForCallableReference: Boolean
|
||||||
abstract val resolvedToCompanionObject: Boolean
|
abstract val resolvedToCompanionObject: Boolean
|
||||||
|
abstract val isFullyQualified: Boolean
|
||||||
abstract val nonFatalDiagnostics: List<ConeDiagnostic>
|
abstract val nonFatalDiagnostics: List<ConeDiagnostic>
|
||||||
abstract val typeArguments: List<FirTypeProjection>
|
abstract val typeArguments: List<FirTypeProjection>
|
||||||
|
|
||||||
|
|||||||
+1
@@ -35,6 +35,7 @@ interface FirAbstractResolvedQualifierBuilder {
|
|||||||
abstract var symbol: FirClassLikeSymbol<*>?
|
abstract var symbol: FirClassLikeSymbol<*>?
|
||||||
abstract var isNullableLHSForCallableReference: Boolean
|
abstract var isNullableLHSForCallableReference: Boolean
|
||||||
abstract var resolvedToCompanionObject: Boolean
|
abstract var resolvedToCompanionObject: Boolean
|
||||||
|
abstract var isFullyQualified: Boolean
|
||||||
abstract val nonFatalDiagnostics: MutableList<ConeDiagnostic>
|
abstract val nonFatalDiagnostics: MutableList<ConeDiagnostic>
|
||||||
abstract val typeArguments: MutableList<FirTypeProjection>
|
abstract val typeArguments: MutableList<FirTypeProjection>
|
||||||
|
|
||||||
|
|||||||
+2
@@ -39,6 +39,7 @@ class FirErrorResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, Fi
|
|||||||
override var relativeClassFqName: FqName? = null
|
override var relativeClassFqName: FqName? = null
|
||||||
override var symbol: FirClassLikeSymbol<*>? = null
|
override var symbol: FirClassLikeSymbol<*>? = null
|
||||||
override var isNullableLHSForCallableReference: Boolean = false
|
override var isNullableLHSForCallableReference: Boolean = false
|
||||||
|
override var isFullyQualified: Boolean = false
|
||||||
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
|
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
|
||||||
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
|
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
|
||||||
lateinit var diagnostic: ConeDiagnostic
|
lateinit var diagnostic: ConeDiagnostic
|
||||||
@@ -51,6 +52,7 @@ class FirErrorResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, Fi
|
|||||||
relativeClassFqName,
|
relativeClassFqName,
|
||||||
symbol,
|
symbol,
|
||||||
isNullableLHSForCallableReference,
|
isNullableLHSForCallableReference,
|
||||||
|
isFullyQualified,
|
||||||
nonFatalDiagnostics.toMutableOrEmpty(),
|
nonFatalDiagnostics.toMutableOrEmpty(),
|
||||||
typeArguments.toMutableOrEmpty(),
|
typeArguments.toMutableOrEmpty(),
|
||||||
diagnostic,
|
diagnostic,
|
||||||
|
|||||||
+2
@@ -41,6 +41,7 @@ class FirResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, FirAnno
|
|||||||
override var relativeClassFqName: FqName? = null
|
override var relativeClassFqName: FqName? = null
|
||||||
override var symbol: FirClassLikeSymbol<*>? = null
|
override var symbol: FirClassLikeSymbol<*>? = null
|
||||||
override var isNullableLHSForCallableReference: Boolean = false
|
override var isNullableLHSForCallableReference: Boolean = false
|
||||||
|
override var isFullyQualified: Boolean = false
|
||||||
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
|
override val nonFatalDiagnostics: MutableList<ConeDiagnostic> = mutableListOf()
|
||||||
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
|
override val typeArguments: MutableList<FirTypeProjection> = mutableListOf()
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ class FirResolvedQualifierBuilder : FirAbstractResolvedQualifierBuilder, FirAnno
|
|||||||
relativeClassFqName,
|
relativeClassFqName,
|
||||||
symbol,
|
symbol,
|
||||||
isNullableLHSForCallableReference,
|
isNullableLHSForCallableReference,
|
||||||
|
isFullyQualified,
|
||||||
nonFatalDiagnostics.toMutableOrEmpty(),
|
nonFatalDiagnostics.toMutableOrEmpty(),
|
||||||
typeArguments.toMutableOrEmpty(),
|
typeArguments.toMutableOrEmpty(),
|
||||||
)
|
)
|
||||||
|
|||||||
+1
@@ -33,6 +33,7 @@ internal class FirErrorResolvedQualifierImpl(
|
|||||||
override val relativeClassFqName: FqName?,
|
override val relativeClassFqName: FqName?,
|
||||||
override val symbol: FirClassLikeSymbol<*>?,
|
override val symbol: FirClassLikeSymbol<*>?,
|
||||||
override var isNullableLHSForCallableReference: Boolean,
|
override var isNullableLHSForCallableReference: Boolean,
|
||||||
|
override val isFullyQualified: Boolean,
|
||||||
override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>,
|
override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>,
|
||||||
override var typeArguments: MutableOrEmptyList<FirTypeProjection>,
|
override var typeArguments: MutableOrEmptyList<FirTypeProjection>,
|
||||||
override val diagnostic: ConeDiagnostic,
|
override val diagnostic: ConeDiagnostic,
|
||||||
|
|||||||
+1
@@ -34,6 +34,7 @@ internal class FirResolvedQualifierImpl(
|
|||||||
override var relativeClassFqName: FqName?,
|
override var relativeClassFqName: FqName?,
|
||||||
override val symbol: FirClassLikeSymbol<*>?,
|
override val symbol: FirClassLikeSymbol<*>?,
|
||||||
override var isNullableLHSForCallableReference: Boolean,
|
override var isNullableLHSForCallableReference: Boolean,
|
||||||
|
override val isFullyQualified: Boolean,
|
||||||
override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>,
|
override var nonFatalDiagnostics: MutableOrEmptyList<ConeDiagnostic>,
|
||||||
override var typeArguments: MutableOrEmptyList<FirTypeProjection>,
|
override var typeArguments: MutableOrEmptyList<FirTypeProjection>,
|
||||||
) : FirResolvedQualifier() {
|
) : FirResolvedQualifier() {
|
||||||
|
|||||||
+2
-2
@@ -354,12 +354,12 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
|
|||||||
|
|
||||||
builder(resolvedQualifier) {
|
builder(resolvedQualifier) {
|
||||||
parents += abstractResolvedQualifierBuilder
|
parents += abstractResolvedQualifierBuilder
|
||||||
defaultFalse("isNullableLHSForCallableReference")
|
defaultFalse("isNullableLHSForCallableReference", "isFullyQualified")
|
||||||
}
|
}
|
||||||
|
|
||||||
builder(errorResolvedQualifier) {
|
builder(errorResolvedQualifier) {
|
||||||
parents += abstractResolvedQualifierBuilder
|
parents += abstractResolvedQualifierBuilder
|
||||||
defaultFalse("isNullableLHSForCallableReference")
|
defaultFalse("isNullableLHSForCallableReference", "isFullyQualified")
|
||||||
}
|
}
|
||||||
|
|
||||||
// builder(safeCallExpression) {
|
// builder(safeCallExpression) {
|
||||||
|
|||||||
+1
@@ -595,6 +595,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
|
|||||||
+field("symbol", classLikeSymbolType, nullable = true)
|
+field("symbol", classLikeSymbolType, nullable = true)
|
||||||
+booleanField("isNullableLHSForCallableReference", withReplace = true)
|
+booleanField("isNullableLHSForCallableReference", withReplace = true)
|
||||||
+booleanField("resolvedToCompanionObject", withReplace = true)
|
+booleanField("resolvedToCompanionObject", withReplace = true)
|
||||||
|
+booleanField("isFullyQualified")
|
||||||
+fieldList("nonFatalDiagnostics", coneDiagnosticType, useMutableOrEmpty = true)
|
+fieldList("nonFatalDiagnostics", coneDiagnosticType, useMutableOrEmpty = true)
|
||||||
+typeArguments.withTransform()
|
+typeArguments.withTransform()
|
||||||
}
|
}
|
||||||
|
|||||||
compiler/testData/diagnostics/testsWithStdLib/annotations/annotationTargetResolvedAmbiguously.fir.kt
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
import kotlin.annotation.AnnotationTarget.FIELD
|
||||||
|
|
||||||
|
object Some {
|
||||||
|
@Target(<!ARGUMENT_TYPE_MISMATCH!>AnnotationTarget.<!AMBIGUOUS_ANNOTATION_ARGUMENT!>CLASS<!><!>)
|
||||||
|
annotation class Ann
|
||||||
|
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(<!AMBIGUOUS_ANNOTATION_ARGUMENT!>FIELD<!>)
|
||||||
|
annotation class Ann2
|
||||||
|
|
||||||
|
const val FIELD = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
object SomeMore {
|
||||||
|
@Target(<!ARGUMENT_TYPE_MISMATCH!>kotlin.annotation.AnnotationTarget.<!AMBIGUOUS_ANNOTATION_ARGUMENT!>FUNCTION<!><!>)
|
||||||
|
annotation class Ann3
|
||||||
|
|
||||||
|
object kotlin {
|
||||||
|
object annotation {
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
FUNCTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Base {
|
||||||
|
annotation class Target(val target: AnnotationTarget)
|
||||||
|
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived : Base() {
|
||||||
|
@<!PLUGIN_ANNOTATION_AMBIGUITY!>Target<!>(<!ARGUMENT_TYPE_MISMATCH!>AnnotationTarget.<!AMBIGUOUS_ANNOTATION_ARGUMENT!>TYPE<!><!>)
|
||||||
|
annotation class Ann
|
||||||
|
|
||||||
|
fun foo(x: <!WRONG_ANNOTATION_TARGET!>@Ann<!> String) {}
|
||||||
|
}
|
||||||
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
import kotlin.annotation.AnnotationTarget.FIELD
|
||||||
|
|
||||||
|
object Some {
|
||||||
|
@Target(<!TYPE_MISMATCH!>AnnotationTarget.CLASS<!>)
|
||||||
|
annotation class Ann
|
||||||
|
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
@Target(<!TYPE_MISMATCH!>FIELD<!>)
|
||||||
|
annotation class Ann2
|
||||||
|
|
||||||
|
const val FIELD = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
object SomeMore {
|
||||||
|
@Target(<!TYPE_MISMATCH!>kotlin.annotation.AnnotationTarget.FUNCTION<!>)
|
||||||
|
annotation class Ann3
|
||||||
|
|
||||||
|
object kotlin {
|
||||||
|
object annotation {
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
FUNCTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Base {
|
||||||
|
annotation class Target(val target: AnnotationTarget)
|
||||||
|
|
||||||
|
enum class AnnotationTarget {
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived : Base() {
|
||||||
|
@Target(AnnotationTarget.TYPE)
|
||||||
|
annotation class Ann
|
||||||
|
|
||||||
|
fun foo(x: <!WRONG_ANNOTATION_TARGET!>@Ann<!> String) {}
|
||||||
|
}
|
||||||
Generated
+6
@@ -35970,6 +35970,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(fir|ll)\\.kts?$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("annotationTargetResolvedAmbiguously.kt")
|
||||||
|
public void testAnnotationTargetResolvedAmbiguously() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/testsWithStdLib/annotations/annotationTargetResolvedAmbiguously.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
@TestMetadata("annotationsTargetingLateinitAccessors.kt")
|
||||||
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
public void testAnnotationsTargetingLateinitAccessors() throws Exception {
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ object StandardClassIds {
|
|||||||
|
|
||||||
val AnnotationRetention = "AnnotationRetention".annotationId()
|
val AnnotationRetention = "AnnotationRetention".annotationId()
|
||||||
val AnnotationTarget = "AnnotationTarget".annotationId()
|
val AnnotationTarget = "AnnotationTarget".annotationId()
|
||||||
|
val DeprecationLevel = "DeprecationLevel".baseId()
|
||||||
|
|
||||||
val EnumEntries = "EnumEntries".enumsId()
|
val EnumEntries = "EnumEntries".enumsId()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user