[FE, IR] Store PsiElement for mismatched annotation's actual target

There is IDE quick fix which suggests to copy mismatched annotation
from `expect` to `actual` (see KTIJ-26633). It needs to find
`actual` PsiElement where to add annotation. Before previous commit, it
was easy - just get source of `Incompatibility.actualSymbol`.
After previous commit, the problem might be in value parameter, while
`actualSymbol` would contain function symbol. This is solved by adding
new field `Incompatibility.actualAnnotationTargetElement`.

`SourceElementMarker` introduced, because it's needed to be used in
abstract checker. Existing `DeclarationSymbolMarker` doesn't fit
because in next PR for this issue annotations set on types will be reported,
and types are not declarations.

^KT-60671
This commit is contained in:
Roman Efremov
2023-09-07 16:50:34 +02:00
committed by Space Team
parent 5c95f69aef
commit 420dceb7d8
20 changed files with 108 additions and 22 deletions
@@ -214,6 +214,14 @@ internal object FirToKtConversionCreator {
"org.jetbrains.kotlin.psi" "org.jetbrains.kotlin.psi"
) )
), ),
KtSourceElement::class to HLFunctionCallConversion(
"({0} as? KtPsiSourceElement)?.psi",
PsiElement::class.createType(nullable = true),
importsToAdd = listOf(
"org.jetbrains.kotlin.psi",
"org.jetbrains.kotlin.KtPsiSourceElement"
)
),
) )
private val typeMapping: Map<KClass<*>, HLFunctionCallConversion> = mapOf( private val typeMapping: Map<KClass<*>, HLFunctionCallConversion> = mapOf(
@@ -3717,7 +3717,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
ActualAnnotationsNotMatchExpectImpl( ActualAnnotationsNotMatchExpectImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a), firSymbolBuilder.buildSymbol(firDiagnostic.a),
firSymbolBuilder.buildSymbol(firDiagnostic.b), firSymbolBuilder.buildSymbol(firDiagnostic.b),
firDiagnostic.c, (firDiagnostic.c as? KtPsiSourceElement)?.psi,
firDiagnostic.d,
firDiagnostic as KtPsiDiagnostic, firDiagnostic as KtPsiDiagnostic,
token, token,
) )
@@ -2601,6 +2601,7 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = ActualAnnotationsNotMatchExpect::class override val diagnosticClass get() = ActualAnnotationsNotMatchExpect::class
val expectSymbol: KtSymbol val expectSymbol: KtSymbol
val actualSymbol: KtSymbol val actualSymbol: KtSymbol
val actualAnnotationTargetSourceElement: PsiElement?
val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation> val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation>
} }
@@ -3133,6 +3133,7 @@ internal class ActualTypealiasToSpecialAnnotationImpl(
internal class ActualAnnotationsNotMatchExpectImpl( internal class ActualAnnotationsNotMatchExpectImpl(
override val expectSymbol: KtSymbol, override val expectSymbol: KtSymbol,
override val actualSymbol: KtSymbol, override val actualSymbol: KtSymbol,
override val actualAnnotationTargetSourceElement: PsiElement?,
override val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation>, override val incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation>,
firDiagnostic: KtPsiDiagnostic, firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken, token: KtLifetimeToken,
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirFunction
@@ -41,6 +42,7 @@ import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Inco
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.PrivateForInline import org.jetbrains.kotlin.util.PrivateForInline
import java.util.Optional
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@@ -1268,6 +1270,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning<KtElement>(PositioningStrategy.DECLARATION_NAME_ONLY) { val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning<KtElement>(PositioningStrategy.DECLARATION_NAME_ONLY) {
parameter<Symbol>("expectSymbol") parameter<Symbol>("expectSymbol")
parameter<Symbol>("actualSymbol") parameter<Symbol>("actualSymbol")
parameter<KtSourceElement?>("actualAnnotationTargetSourceElement")
parameter<ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>("incompatibilityType") parameter<ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>("incompatibilityType")
} }
@@ -662,7 +662,7 @@ object FirErrors {
val NOT_A_MULTIPLATFORM_COMPILATION by error0<PsiElement>() val NOT_A_MULTIPLATFORM_COMPILATION by error0<PsiElement>()
val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER) val EXPECT_ACTUAL_OPT_IN_ANNOTATION by error0<KtNamedDeclaration>(SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER)
val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1<KtTypeAlias, ClassId>(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE) val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION by error1<KtTypeAlias, ClassId>(SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE)
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning3<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>, ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY) val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT by warning4<KtElement, FirBasedSymbol<*>, FirBasedSymbol<*>, KtSourceElement?, ExpectActualAnnotationsIncompatibilityType<FirAnnotation>>(SourceElementPositioningStrategies.DECLARATION_NAME_ONLY)
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error0<PsiElement>() val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error0<PsiElement>()
val OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE by error0<PsiElement>() val OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE by error0<PsiElement>()
val OPTIONAL_EXPECTATION_NOT_ON_EXPECTED by error0<PsiElement>() val OPTIONAL_EXPECTATION_NOT_ON_EXPECTED by error0<PsiElement>()
@@ -8,10 +8,9 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList import org.jetbrains.kotlin.fir.analysis.checkers.getModifierList
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
@@ -35,7 +34,8 @@ import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.* import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Compatible
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible import org.jetbrains.kotlin.resolve.multiplatform.isCompatibleOrWeaklyIncompatible
@Suppress("DuplicatedCode") @Suppress("DuplicatedCode")
@@ -276,6 +276,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
return result return result
} }
@OptIn(InternalDiagnosticFactoryMethod::class)
private fun checkAnnotationsMatch( private fun checkAnnotationsMatch(
expectSymbol: FirBasedSymbol<*>, expectSymbol: FirBasedSymbol<*>,
actualSymbol: FirBasedSymbol<*>, actualSymbol: FirBasedSymbol<*>,
@@ -286,12 +287,18 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession) val matchingContext = context.session.expectActualMatchingContextFactory.create(context.session, context.scopeSession)
val incompatibility = val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: return AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectSymbol, actualSymbol, matchingContext) ?: return
reporter.reportOn( val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as FirSourceElement).element
actualSymbol.source, FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
incompatibility.expectSymbol as FirBasedSymbol<*>, reporter.report(
incompatibility.actualSymbol as FirBasedSymbol<*>, FirErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on(
incompatibility.type.mapAnnotationType { it.annotationSymbol as FirAnnotation }, actualSymbol.source.requireNotNull(),
context incompatibility.expectSymbol as FirBasedSymbol<*>,
incompatibility.actualSymbol as FirBasedSymbol<*>,
actualAnnotationTargetSourceElement,
incompatibility.type.mapAnnotationType { it.annotationSymbol as FirAnnotation },
positioningStrategy = null,
),
context,
) )
} }
@@ -2005,10 +2005,11 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
) )
map.put( map.put(
ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"{2}.\n" + "{3}.\n" +
"All annotations from expect ''{0}'' must be present with the same arguments on actual ''{1}'', otherwise they might behave incorrectly.", "All annotations from expect ''{0}'' must be present with the same arguments on actual ''{1}'', otherwise they might behave incorrectly.",
FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER,
FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.SYMBOL_RENDERER,
NOT_RENDERED,
FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY, FirExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY,
) )
map.put( map.put(
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.mpp.SourceElementMarker
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
@@ -480,6 +481,8 @@ class FirExpectActualMatchingContextImpl private constructor(
return mapping?.get(actualMember to expectClass) ?: emptyMap() return mapping?.get(actualMember to expectClass) ?: emptyMap()
} }
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker = FirSourceElement(asSymbol().source)
object Factory : FirExpectActualMatchingContextFactory { object Factory : FirExpectActualMatchingContextFactory {
override fun create( override fun create(
session: FirSession, scopeSession: ScopeSession, session: FirSession, scopeSession: ScopeSession,
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2023 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.fir
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.mpp.SourceElementMarker
class FirSourceElement(val element: KtSourceElement?) : SourceElementMarker
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability;
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier; import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier;
import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo; import org.jetbrains.kotlin.resolve.calls.util.BuilderLambdaLabelingInfo;
import org.jetbrains.kotlin.resolve.deprecation.DescriptorBasedDeprecationInfo; import org.jetbrains.kotlin.resolve.deprecation.DescriptorBasedDeprecationInfo;
import org.jetbrains.kotlin.resolve.multiplatform.ClassicSourceElement;
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType; import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType;
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible; import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible;
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMemberDiff; import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMemberDiff;
@@ -42,10 +43,7 @@ import org.jetbrains.kotlin.types.KotlinType;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Collection; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.ACTUAL_DECLARATION_NAME;
import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION; import static org.jetbrains.kotlin.diagnostics.ClassicPositioningStrategies.INCOMPATIBLE_DECLARATION;
@@ -875,9 +873,9 @@ public interface Errors {
DiagnosticFactory0<KtNamedDeclaration> EXPECT_ACTUAL_OPT_IN_ANNOTATION = DiagnosticFactory0.create(ERROR, EXPECT_ACTUAL_MODIFIER); DiagnosticFactory0<KtNamedDeclaration> EXPECT_ACTUAL_OPT_IN_ANNOTATION = DiagnosticFactory0.create(ERROR, EXPECT_ACTUAL_MODIFIER);
DiagnosticFactory1<KtTypeAlias, ClassId> ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION = DiagnosticFactory1<KtTypeAlias, ClassId> ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION =
DiagnosticFactory1.create(ERROR, TYPEALIAS_TYPE_REFERENCE); DiagnosticFactory1.create(ERROR, TYPEALIAS_TYPE_REFERENCE);
DiagnosticFactory3<KtNamedDeclaration, DeclarationDescriptor, DeclarationDescriptor, DiagnosticFactory4<KtNamedDeclaration, DeclarationDescriptor, DeclarationDescriptor, Optional<SourceElement>,
ExpectActualAnnotationsIncompatibilityType<AnnotationDescriptor>> ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT = ExpectActualAnnotationsIncompatibilityType<AnnotationDescriptor>> ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT =
DiagnosticFactory3.create(WARNING, DECLARATION_NAME_ONLY); DiagnosticFactory4.create(WARNING, DECLARATION_NAME_ONLY);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -444,10 +444,11 @@ public class DefaultErrorMessages {
"`actual typealias` to annotation which affects code compilation can lead to incorrect behavior. Instead, use ''{0}'' annotation directly.", "`actual typealias` to annotation which affects code compilation can lead to incorrect behavior. Instead, use ''{0}'' annotation directly.",
TO_STRING); TO_STRING);
MAP.put(ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, MAP.put(ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"{2}.\n" + "{3}.\n" +
"All annotations from expect `{0}` must be present with the same arguments on actual `{1}`, otherwise they might behave incorrectly.", "All annotations from expect `{0}` must be present with the same arguments on actual `{1}`, otherwise they might behave incorrectly.",
ExpectActualAnnotationIncompatibilityDiagnosticRenderers.DESCRIPTOR_RENDERER, ExpectActualAnnotationIncompatibilityDiagnosticRenderers.DESCRIPTOR_RENDERER,
ExpectActualAnnotationIncompatibilityDiagnosticRenderers.DESCRIPTOR_RENDERER, ExpectActualAnnotationIncompatibilityDiagnosticRenderers.DESCRIPTOR_RENDERER,
NOT_RENDERED,
ExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY); ExpectActualAnnotationIncompatibilityDiagnosticRenderers.INCOMPATIBILITY);
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties"); MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
@@ -69,6 +69,9 @@ object Renderers {
element.toString() element.toString()
} }
@JvmField
val NOT_RENDERED = Renderer<Any?> { _ -> "" }
@JvmField @JvmField
val NAME = Renderer<Named> { it.name.asString() } val NAME = Renderer<Named> { it.name.asString() }
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.resolve.source.PsiSourceFile import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import java.io.File import java.io.File
import java.util.*
private val implicitlyActualizedAnnotationFqn = StandardClassIds.Annotations.ImplicitlyActualizedByJvmDeclaration.asSingleFqName() private val implicitlyActualizedAnnotationFqn = StandardClassIds.Annotations.ImplicitlyActualizedByJvmDeclaration.asSingleFqName()
@@ -514,11 +515,13 @@ class ExpectedActualDeclarationChecker(
val matchingContext = ClassicExpectActualMatchingContext(actualDescriptor.module) val matchingContext = ClassicExpectActualMatchingContext(actualDescriptor.module)
val incompatibility = val incompatibility =
AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectDescriptor, actualDescriptor, matchingContext) ?: return AbstractExpectActualAnnotationMatchChecker.areAnnotationsCompatible(expectDescriptor, actualDescriptor, matchingContext) ?: return
val actualAnnotationTargetSourceElement = (incompatibility.actualAnnotationTargetElement as ClassicSourceElement).element
context.trace.report( context.trace.report(
Errors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on( Errors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT.on(
reportOn, reportOn,
incompatibility.expectSymbol as DeclarationDescriptor, incompatibility.expectSymbol as DeclarationDescriptor,
incompatibility.actualSymbol as DeclarationDescriptor, incompatibility.actualSymbol as DeclarationDescriptor,
Optional.ofNullable(actualAnnotationTargetSourceElement),
incompatibility.type.mapAnnotationType { it.annotationSymbol as AnnotationDescriptor } incompatibility.type.mapAnnotationType { it.annotationSymbol as AnnotationDescriptor }
) )
) )
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.mpp.SourceElementMarker
import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
@@ -523,4 +524,9 @@ internal abstract class IrExpectActualMatchingContext(
actualMember: DeclarationSymbolMarker, actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>> = error("Should not be called") ): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>> = error("Should not be called")
// It's a stub, because not needed anywhere
private object IrSourceElementStub : SourceElementMarker
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker = IrSourceElementStub
} }
@@ -38,6 +38,13 @@ object AbstractExpectActualAnnotationMatchChecker {
*/ */
val expectSymbol: DeclarationSymbolMarker, val expectSymbol: DeclarationSymbolMarker,
val actualSymbol: DeclarationSymbolMarker, val actualSymbol: DeclarationSymbolMarker,
/**
* Link to source code element (possibly holding null, if no source) from actual declaration
* where mismatched actual annotation is set (or should be set if it is missing).
* Needed for the implementation of IDE intention.
*/
val actualAnnotationTargetElement: SourceElementMarker,
val type: IncompatibilityType<ExpectActualMatchingContext.AnnotationCallInfo>, val type: IncompatibilityType<ExpectActualMatchingContext.AnnotationCallInfo>,
) )
@@ -122,7 +129,7 @@ object AbstractExpectActualAnnotationMatchChecker {
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) -> return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let { areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
// Write containing declarations into diagnostic // Write containing declarations into diagnostic
Incompatibility(expectSymbol, actualSymbol, it.type) Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
} }
} }
} }
@@ -150,6 +157,7 @@ object AbstractExpectActualAnnotationMatchChecker {
return Incompatibility( return Incompatibility(
expectSymbol, expectSymbol,
actualSymbol, actualSymbol,
actualSymbol.getSourceElement(),
IncompatibilityType.MissingOnActual(expectAnnotation) IncompatibilityType.MissingOnActual(expectAnnotation)
) )
} }
@@ -163,7 +171,7 @@ object AbstractExpectActualAnnotationMatchChecker {
// In the case of repeatable annotations, we can't choose on which to report // In the case of repeatable annotations, we can't choose on which to report
IncompatibilityType.MissingOnActual(expectAnnotation) IncompatibilityType.MissingOnActual(expectAnnotation)
} }
return Incompatibility(expectSymbol, actualSymbol, incompatibilityType) return Incompatibility(expectSymbol, actualSymbol, actualSymbol.getSourceElement(), incompatibilityType)
} }
} }
return null return null
@@ -213,4 +213,6 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
actualMember: DeclarationSymbolMarker, actualMember: DeclarationSymbolMarker,
checkClassScopesCompatibility: Boolean, checkClassScopesCompatibility: Boolean,
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>> ): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>>
fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker
} }
@@ -442,4 +442,8 @@ class ClassicExpectActualMatchingContext(
} }
} }
} }
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker {
return ClassicSourceElement((asDescriptor() as? DeclarationDescriptorWithSource)?.source)
}
} }
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2023 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.multiplatform
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.mpp.SourceElementMarker
class ClassicSourceElement(val element: SourceElement?) : SourceElementMarker
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2023 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.mpp
/**
* Common interface, which stores a link to a source element of declaration or type reference (or null, if no source).
* The idea is similar to [DeclarationSymbolMarker], but it doesn't fit, because
* sources of type references also must be supported, which are not declarations.
* Used in [org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker].
*/
interface SourceElementMarker