Skip to content

Commit 2b64be4

Browse files
sandwwraithSpace Team
authored andcommitted
[FIR] Improve "actual ignorability not match expect" diagnostic
by adding ignorability statuses to its text.
1 parent ee24ab1 commit 2b64be4

File tree

15 files changed

+65
-32
lines changed

15 files changed

+65
-32
lines changed

analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.name.FqName
3939
import org.jetbrains.kotlin.name.Name
4040
import org.jetbrains.kotlin.psi.KtExpression
4141
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
42+
import org.jetbrains.kotlin.resolve.ReturnValueStatus
4243
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
4344
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
4445
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
@@ -389,6 +390,7 @@ internal object FirToKtConversionCreator {
389390
IncompatibleVersionErrorData::class,
390391
RelationToType::class,
391392
KotlinTarget::class,
393+
ReturnValueStatus::class,
392394
)
393395

394396
private val KType.kClass: KClass<*>

analysis/analysis-api-fir/gen/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KaFirDataClassConverters.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4937,8 +4937,9 @@ internal val KT_DIAGNOSTIC_CONVERTER = KaDiagnosticConverterBuilder.buildConvert
49374937
add(FirErrors.ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT) { firDiagnostic ->
49384938
ActualIgnorabilityNotMatchExpectImpl(
49394939
firSymbolBuilder.buildSymbol(firDiagnostic.a),
4940-
firSymbolBuilder.buildSymbol(firDiagnostic.b),
4941-
firDiagnostic.c,
4940+
firDiagnostic.b,
4941+
firSymbolBuilder.buildSymbol(firDiagnostic.c),
4942+
firDiagnostic.d,
49424943
firDiagnostic as KtPsiDiagnostic,
49434944
token,
49444945
)

analysis/analysis-api-fir/gen/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KaFirDiagnostics.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
8686
import org.jetbrains.kotlin.psi.KtWhenEntry
8787
import org.jetbrains.kotlin.psi.KtWhenExpression
8888
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
89+
import org.jetbrains.kotlin.resolve.ReturnValueStatus
8990
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
9091
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
9192
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility.Mismatch
@@ -3460,8 +3461,9 @@ sealed interface KaFirDiagnostic<PSI : PsiElement> : KaDiagnosticWithPsi<PSI> {
34603461
interface ActualIgnorabilityNotMatchExpect : KaFirDiagnostic<KtNamedDeclaration> {
34613462
override val diagnosticClass get() = ActualIgnorabilityNotMatchExpect::class
34623463
val expectDeclaration: KaSymbol
3464+
val expectIgnorability: ReturnValueStatus
34633465
val actualDeclaration: KaSymbol
3464-
val reason: String
3466+
val actualIgnorability: ReturnValueStatus
34653467
}
34663468

34673469
interface OptionalDeclarationOutsideOfAnnotationEntry : KaFirDiagnostic<PsiElement> {

analysis/analysis-api-fir/gen/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KaFirDiagnosticsImpl.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
8787
import org.jetbrains.kotlin.psi.KtWhenEntry
8888
import org.jetbrains.kotlin.psi.KtWhenExpression
8989
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
90+
import org.jetbrains.kotlin.resolve.ReturnValueStatus
9091
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
9192
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
9293
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility.Mismatch
@@ -4146,8 +4147,9 @@ internal class ActualAnnotationsNotMatchExpectImpl(
41464147

41474148
internal class ActualIgnorabilityNotMatchExpectImpl(
41484149
override val expectDeclaration: KaSymbol,
4150+
override val expectIgnorability: ReturnValueStatus,
41494151
override val actualDeclaration: KaSymbol,
4150-
override val reason: String,
4152+
override val actualIgnorability: ReturnValueStatus,
41514153
firDiagnostic: KtPsiDiagnostic,
41524154
token: KaLifetimeToken,
41534155
) : KaAbstractFirDiagnostic<KtNamedDeclaration>(firDiagnostic, token), KaFirDiagnostic.ActualIgnorabilityNotMatchExpect

compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1685,8 +1685,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
16851685
// Same as expectActualIncompatibilityError, but warning
16861686
val ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT by warning<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME_ONLY) {
16871687
parameter<Symbol>("expectDeclaration")
1688+
parameter<ReturnValueStatus>("expectIgnorability")
16881689
parameter<Symbol>("actualDeclaration")
1689-
parameter<String>("reason")
1690+
parameter<ReturnValueStatus>("actualIgnorability")
16901691
}
16911692

16921693
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY by error<PsiElement>()

compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ import org.jetbrains.kotlin.psi.KtWhenCondition
137137
import org.jetbrains.kotlin.psi.KtWhenEntry
138138
import org.jetbrains.kotlin.psi.KtWhenExpression
139139
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
140+
import org.jetbrains.kotlin.resolve.ReturnValueStatus
140141
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
141142
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility
142143
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualMatchingCompatibility.Mismatch
@@ -834,7 +835,7 @@ object FirErrors : KtDiagnosticsContainer() {
834835
val EXPECT_ACTUAL_OPT_IN_ANNOTATION: KtDiagnosticFactory0 = KtDiagnosticFactory0("EXPECT_ACTUAL_OPT_IN_ANNOTATION", ERROR, SourceElementPositioningStrategies.EXPECT_ACTUAL_MODIFIER, KtNamedDeclaration::class, getRendererFactory())
835836
val ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION: KtDiagnosticFactory1<ClassId> = KtDiagnosticFactory1("ACTUAL_TYPEALIAS_TO_SPECIAL_ANNOTATION", ERROR, SourceElementPositioningStrategies.TYPEALIAS_TYPE_REFERENCE, KtTypeAlias::class, getRendererFactory())
836837
val ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT: KtDiagnosticFactory4<FirBasedSymbol<*>, FirBasedSymbol<*>, KtSourceElement?, ExpectActualAnnotationsIncompatibilityType<FirAnnotation>> = KtDiagnosticFactory4("ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT", WARNING, SourceElementPositioningStrategies.DECLARATION_NAME_ONLY, KtElement::class, getRendererFactory())
837-
val ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT: KtDiagnosticFactory3<FirBasedSymbol<*>, FirBasedSymbol<*>, String> = KtDiagnosticFactory3("ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT", WARNING, SourceElementPositioningStrategies.DECLARATION_NAME_ONLY, KtNamedDeclaration::class, getRendererFactory())
838+
val ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT: KtDiagnosticFactory4<FirBasedSymbol<*>, ReturnValueStatus, FirBasedSymbol<*>, ReturnValueStatus> = KtDiagnosticFactory4("ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT", WARNING, SourceElementPositioningStrategies.DECLARATION_NAME_ONLY, KtNamedDeclaration::class, getRendererFactory())
838839
val OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY: KtDiagnosticFactory0 = KtDiagnosticFactory0("OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY", ERROR, SourceElementPositioningStrategies.DEFAULT, PsiElement::class, getRendererFactory())
839840
val OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE: KtDiagnosticFactory0 = KtDiagnosticFactory0("OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE", ERROR, SourceElementPositioningStrategies.DEFAULT, PsiElement::class, getRendererFactory())
840841
val OPTIONAL_EXPECTATION_NOT_ON_EXPECTED: KtDiagnosticFactory0 = KtDiagnosticFactory0("OPTIONAL_EXPECTATION_NOT_ON_EXPECTED", ERROR, SourceElementPositioningStrategies.DEFAULT, PsiElement::class, getRendererFactory())

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirExpectActualDeclarationChecker.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker(MppChecker
186186
// A nicer diagnostic for functions with default params
187187
if (declaration is FirFunction && incompatibility == ExpectActualIncompatibility.ActualFunctionWithOptionalParameters) {
188188
reporter.reportOn(declaration.source, FirErrors.ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS)
189+
} else if (incompatibility == ExpectActualIncompatibility.IgnorabilityIsDifferent) {
190+
reportIgnorabilityIncompatibleMembers(expectedSingleCandidate, symbol, source)
189191
} else {
190192
reporter.reportOn(
191193
source,
@@ -285,24 +287,33 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker(MppChecker
285287
}
286288
}
287289

290+
context(reporter: DiagnosticReporter, context: CheckerContext)
291+
private fun reportIgnorabilityIncompatibleMembers(
292+
expect: FirBasedSymbol<*>,
293+
actual: FirBasedSymbol<*>,
294+
source: KtSourceElement?,
295+
) {
296+
val expectMember =
297+
expect as? FirCallableSymbol<*> ?: error("Ignorability incompatibility can be reported only for callables")
298+
val actualMember =
299+
actual as? FirCallableSymbol<*> ?: error("Ignorability incompatibility can be reported only for callables")
300+
reporter.reportOn(
301+
source,
302+
FirErrors.ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT,
303+
expectMember,
304+
expectMember.resolvedStatus.returnValueStatus,
305+
actualMember,
306+
actualMember.resolvedStatus.returnValueStatus
307+
)
308+
}
309+
288310
context(reporter: DiagnosticReporter, context: CheckerContext)
289311
private fun reportIgnorabilityIncompatibleMembers(
290312
ignorabilityIncompatibleMembers: List<MemberIncompatibility<FirBasedSymbol<*>>>,
291313
source: KtSourceElement?,
292314
) {
293-
// Separate handling because it is a warning
294315
for (member in ignorabilityIncompatibleMembers) {
295-
val expectMember =
296-
member.expect as? FirCallableSymbol<*> ?: error("Ignorability incompatibility can be reported only for callables")
297-
val actualMember =
298-
member.actual as? FirCallableSymbol<*> ?: error("Ignorability incompatibility can be reported only for callables")
299-
reporter.reportOn(
300-
source,
301-
FirErrors.ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT,
302-
expectMember,
303-
actualMember,
304-
member.incompatibility.reason
305-
)
316+
reportIgnorabilityIncompatibleMembers(member.expect, member.actual, source)
306317
}
307318
}
308319

@@ -408,6 +419,7 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker(MppChecker
408419
private fun ExpectActualIncompatibility<*>.toDiagnostic() = when (this) {
409420
ExpectActualIncompatibility.ActualFunctionWithOptionalParameters -> error("unreachable")
410421
is ExpectActualIncompatibility.ClassScopes<*> -> error("unreachable")
422+
ExpectActualIncompatibility.IgnorabilityIsDifferent -> error("Should be handled before")
411423

412424
ExpectActualIncompatibility.ClassKind -> FirErrors.EXPECT_ACTUAL_INCOMPATIBLE_CLASS_KIND
413425
ExpectActualIncompatibility.ClassModifiers -> FirErrors.EXPECT_ACTUAL_INCOMPATIBLE_CLASS_MODIFIERS
@@ -436,5 +448,4 @@ private fun ExpectActualIncompatibility<*>.toDiagnostic() = when (this) {
436448
is ExpectActualIncompatibility.Modality -> FirErrors.EXPECT_ACTUAL_INCOMPATIBLE_MODALITY
437449
is ExpectActualIncompatibility.PropertySetterVisibility -> FirErrors.EXPECT_ACTUAL_INCOMPATIBLE_PROPERTY_SETTER_VISIBILITY
438450
is ExpectActualIncompatibility.Visibility -> FirErrors.EXPECT_ACTUAL_INCOMPATIBLE_VISIBILITY
439-
ExpectActualIncompatibility.IgnorabilityIsDifferent -> FirErrors.ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT
440451
}

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticRenderers.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement
3232
import org.jetbrains.kotlin.name.CallableId
3333
import org.jetbrains.kotlin.name.ClassId
3434
import org.jetbrains.kotlin.name.Name
35+
import org.jetbrains.kotlin.resolve.ReturnValueStatus
3536
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
3637
import java.text.MessageFormat
3738

@@ -386,6 +387,14 @@ object FirDiagnosticRenderers {
386387
name?.asString()?.takeIf { it.isNotBlank() }?.let { " of '$it'" } ?: ""
387388
}
388389

390+
val IGNORABILITY_STATUS = Renderer { status: ReturnValueStatus ->
391+
when (status) {
392+
ReturnValueStatus.MustUse -> "must-use"
393+
ReturnValueStatus.ExplicitlyIgnorable -> "ignorable"
394+
ReturnValueStatus.Unspecified -> "unspecified (implicitly ignorable)"
395+
}
396+
}
397+
389398
val SYMBOL_WITH_CONTAINING_DECLARATION = Renderer { symbol: FirBasedSymbol<*> ->
390399
val containingClassId = when (symbol) {
391400
is FirCallableSymbol<*> -> symbol.callableId?.classId

compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECL
3737
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
3838
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FOR_OPTIONAL_OPERATOR
3939
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FUNCTIONAL_TYPE_KINDS
40+
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.IGNORABILITY_STATUS
4041
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.KOTLIN_TARGETS
4142
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.MODULE_DATA
4243
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.NAME_OF_CONTAINING_DECLARATION_OR_FILE
@@ -2978,8 +2979,9 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
29782979
)
29792980
map.put(
29802981
ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT,
2981-
"Ignorability statuses for expect declaration ''{0}'' and actual declaration ''{1}'' do not match. @IgnorableReturnValue declaration cannot be actualized with Must-Use declaration, and vice versa.",
2982-
SYMBOL_WITH_CONTAINING_DECLARATION, SYMBOL_WITH_CONTAINING_DECLARATION, EMPTY
2982+
"Ignorability statuses for expect and actual declarations are different. Expect declaration {0} is {1} and actual declaration {2} is {3}. " +
2983+
"Different statuses may result in inconsistent and incorrect warnings.",
2984+
SYMBOL_WITH_CONTAINING_DECLARATION, IGNORABILITY_STATUS, SYMBOL_WITH_CONTAINING_DECLARATION, IGNORABILITY_STATUS,
29832985
)
29842986
map.put(
29852987
OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY,

compiler/testData/diagnostics/tests/crv/expectActual/actualizeInheritance.fir.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ open class BaseFoo {
2929

3030
// FILE: jvm.kt
3131

32-
actual class <!ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect fun x(): String' defined in 'Foo'; 'fun x(): String' defined in 'BaseFoo'"), ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect fun ign(): String' defined in 'Foo'; 'fun ign(): String' defined in 'BaseFoo'"), ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect constructor(): Foo' defined in 'Foo'; 'constructor(): Foo' defined in 'Foo'")!>Foo<!> : BaseFoo() {
32+
actual class <!ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect fun x(): String' defined in 'Foo'; must-use; 'fun x(): String' defined in 'BaseFoo'; ignorable"), ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect fun ign(): String' defined in 'Foo'; ignorable; 'fun ign(): String' defined in 'BaseFoo'; must-use"), ACTUAL_IGNORABILITY_NOT_MATCH_EXPECT("'expect constructor(): Foo' defined in 'Foo'; must-use; 'constructor(): Foo' defined in 'Foo'; unspecified (implicitly ignorable)")!>Foo<!> : BaseFoo() {
3333
}
3434

3535
fun main() {

0 commit comments

Comments
 (0)