[FE, IR] Make expect actual annotations diagnostic message more friendly

Print which annotation exactly has a problem instead of
printing whole declarations with all annotations.

^KT-58551
This commit is contained in:
Roman Efremov
2023-07-31 14:05:40 +02:00
committed by Space Team
parent 11ccad7e40
commit 6943d03883
11 changed files with 153 additions and 46 deletions
@@ -179,14 +179,4 @@ object FirDiagnosticRenderers {
}
val FUNCTIONAL_TYPE_KINDS = KtDiagnosticRenderers.COLLECTION(FUNCTIONAL_TYPE_KIND)
@OptIn(SymbolInternals::class)
val SYMBOL_WITH_ANNOTATIONS = Renderer { s: FirBasedSymbol<*> ->
FirRenderer(
typeRenderer = ConeTypeRenderer(),
idRenderer = ConeIdShortRenderer(),
classMemberRenderer = null,
bodyRenderer = null,
).renderElementAsString(s.fir, trim = true)
}
}
@@ -1886,11 +1886,11 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
)
map.put(
ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.\n" +
"Expected: {0}\n" +
"Actual: {1}",
FirDiagnosticRenderers.SYMBOL_WITH_ANNOTATIONS, FirDiagnosticRenderers.SYMBOL_WITH_ANNOTATIONS,
TO_STRING
"{2}.\n" +
"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.INCOMPATIBILITY,
)
// Destructuring declaration
@@ -0,0 +1,54 @@
/*
* 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.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.renderer.ConeIdShortRenderer
import org.jetbrains.kotlin.fir.renderer.ConeTypeRenderer
import org.jetbrains.kotlin.fir.renderer.FirRenderer
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
internal object FirExpectActualAnnotationIncompatibilityDiagnosticRenderers {
@OptIn(SymbolInternals::class)
val SYMBOL_RENDERER = Renderer<FirBasedSymbol<*>> {
FirRenderer(
typeRenderer = ConeTypeRenderer(),
idRenderer = ConeIdShortRenderer(),
classMemberRenderer = null,
bodyRenderer = null,
annotationRenderer = null,
modifierRenderer = null,
contractRenderer = null,
).renderElementAsString(it.fir, trim = true)
}
val INCOMPATIBILITY = Renderer { incompatibilityType: ExpectActualAnnotationsIncompatibilityType<FirAnnotation> ->
val sb = StringBuilder("Annotation `")
.append(renderAnnotation(incompatibilityType.expectAnnotation))
.append("` ")
when (incompatibilityType) {
is ExpectActualAnnotationsIncompatibilityType.MissingOnActual -> {
sb.append("is missing on actual declaration")
}
is ExpectActualAnnotationsIncompatibilityType.DifferentOnActual -> {
sb.append("has different arguments on actual declaration: `")
.append(renderAnnotation(incompatibilityType.actualAnnotation))
.append("`")
}
}
sb.toString()
}
private fun renderAnnotation(ann: FirAnnotation): String {
return FirRenderer(
typeRenderer = ConeTypeRenderer(),
idRenderer = ConeIdShortRenderer(),
).renderElementAsString(ann, trim = true)
}
}
@@ -404,10 +404,11 @@ public class DefaultErrorMessages {
"`actual typealias` to annotation which affects code compilation can lead to incorrect behavior. Instead, use ''{0}'' annotation directly.",
TO_STRING);
MAP.put(ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.\n" +
" Expected: {0}\n" +
" Actual: {1}",
DESCRIPTOR_WITH_ANNOTATIONS, DESCRIPTOR_WITH_ANNOTATIONS, TO_STRING);
"{2}.\n" +
"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.INCOMPATIBILITY);
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
MAP.put(SUPERTYPE_NOT_INITIALIZED, "This type has a constructor, and thus must be initialized here");
@@ -0,0 +1,42 @@
/*
* 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.diagnostics.rendering
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
internal object ExpectActualAnnotationIncompatibilityDiagnosticRenderers {
private val descriptorRender = DescriptorRenderer.withOptions {
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
modifiers = emptySet()
withDefinedIn = true
classifierNamePolicy = ClassifierNamePolicy.SHORT
}
@JvmField
val DESCRIPTOR_RENDERER = descriptorRender.asRenderer()
@JvmField
val INCOMPATIBILITY = Renderer { incompatibilityType: ExpectActualAnnotationsIncompatibilityType<AnnotationDescriptor> ->
val sb = StringBuilder("Annotation `")
.append(descriptorRender.renderAnnotation(incompatibilityType.expectAnnotation))
.append("` ")
when (incompatibilityType) {
is ExpectActualAnnotationsIncompatibilityType.MissingOnActual -> {
sb.append("is missing on actual declaration")
}
is ExpectActualAnnotationsIncompatibilityType.DifferentOnActual -> {
sb.append("has different arguments on actual declaration: `")
.append(descriptorRender.renderAnnotation(incompatibilityType.actualAnnotation))
.append("`")
}
}
sb.toString()
}
}
@@ -711,13 +711,6 @@ object Renderers {
@JvmField
val COMPACT_WITH_MODIFIERS = DescriptorRenderer.COMPACT_WITH_MODIFIERS.asRenderer()
@JvmField
val DESCRIPTOR_WITH_ANNOTATIONS = DescriptorRenderer.withOptions {
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
modifiers = modifiers.plus(DescriptorRendererModifier.ANNOTATIONS)
withDefinedIn = true
}.asRenderer()
@JvmField
val DEPRECATION_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions {
withoutTypeParameters = false
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.BackendDiagnosticRenderers.EXPECT_ACTUAL_ANNOTATION_INCOMPATIBILITY
import org.jetbrains.kotlin.backend.common.BackendDiagnosticRenderers.INCOMPATIBILITY
import org.jetbrains.kotlin.backend.common.BackendDiagnosticRenderers.SYMBOL_OWNER_DECLARATION_FQ_NAME
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
@@ -64,10 +66,11 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() {
)
map.put(
CommonBackendErrors.ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT,
"All annotations from expect declaration `{0}` must be present with the same arguments on actual declaration `{1}` otherwise they have no effect",
"{2}.\n" +
"All annotations from expect `{0}` must be present with the same arguments on actual `{1}`, otherwise they might behave incorrectly.",
SYMBOL_OWNER_DECLARATION_FQ_NAME,
SYMBOL_OWNER_DECLARATION_FQ_NAME,
STRING,
EXPECT_ACTUAL_ANNOTATION_INCOMPATIBILITY,
)
map.put(
CommonBackendErrors.EVALUATION_ERROR,
@@ -84,4 +87,13 @@ object BackendDiagnosticRenderers {
val SYMBOL_OWNER_DECLARATION_FQ_NAME = Renderer<IrSymbol> {
(it.owner as? IrDeclarationWithName)?.fqNameWhenAvailable?.asString() ?: "unknown name"
}
val EXPECT_ACTUAL_ANNOTATION_INCOMPATIBILITY =
Renderer { incompatibilityType: ExpectActualAnnotationsIncompatibilityType<IrConstructorCall> ->
val expectAnnotationFqName = incompatibilityType.expectAnnotation.type.classFqName ?: "<unknown>"
val reason = when (incompatibilityType) {
is ExpectActualAnnotationsIncompatibilityType.MissingOnActual -> "is missing on actual declaration"
is ExpectActualAnnotationsIncompatibilityType.DifferentOnActual -> "has different arguments on actual declaration"
}
"Annotation `$expectAnnotationFqName` $reason"
}
}
@@ -1,19 +1,20 @@
// -- Module: <m1-common> --
// -- Module: <m1-jvm> --
/jvm.kt:23:14: warning: all annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann public final expect class OnClass defined in root package in file common.kt
Actual: public final actual class OnClass defined in root package in file jvm.kt
/jvm.kt:27:14: warning: annotation `@Ann` is missing on actual declaration.
All annotations from expect `class OnClass defined in root package in file common.kt` must be present with the same arguments on actual `class OnClass defined in root package in file jvm.kt`, otherwise they might behave incorrectly.
actual class OnClass
^
/jvm.kt:26:16: warning: all annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann public final expect fun onMember(): Unit defined in OnMember
Actual: public final actual fun onMember(): Unit defined in OnMember
/jvm.kt:30:16: warning: annotation `@Ann` is missing on actual declaration.
All annotations from expect `fun onMember(): Unit defined in OnMember` must be present with the same arguments on actual `fun onMember(): Unit defined in OnMember`, otherwise they might behave incorrectly.
actual fun onMember() {}
^
/jvm.kt:33:18: warning: all annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann public final expect class ViaTypealias defined in root package in file common.kt
Actual: public final class ViaTypealiasImpl defined in root package in file jvm.kt
/jvm.kt:37:18: warning: annotation `@Ann` is missing on actual declaration.
All annotations from expect `class ViaTypealias defined in root package in file common.kt` must be present with the same arguments on actual `class ViaTypealiasImpl defined in root package in file jvm.kt`, otherwise they might behave incorrectly.
actual typealias ViaTypealias = ViaTypealiasImpl
^
/jvm.kt:40:12: warning: annotation `@WithArg(s = "str")` has different arguments on actual declaration: `@WithArg(s = "other str")`.
All annotations from expect `fun withDifferentArg(): Unit defined in root package in file common.kt` must be present with the same arguments on actual `fun withDifferentArg(): Unit defined in root package in file jvm.kt`, otherwise they might behave incorrectly.
actual fun withDifferentArg() {}
^
@@ -1,11 +1,11 @@
/jvm.kt:(82,89): warning: All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann() public final expect class OnClass : Any
Actual: public final actual class OnClass : Any
/jvm.kt:(86,93): warning: Annotation `@Ann()` is missing on actual declaration.
All annotations from expect `class OnClass : Any` must be present with the same arguments on actual `class OnClass : Any`, otherwise they might behave incorrectly.
/jvm.kt:(130,138): warning: All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann() public final expect fun onMember(): Unit
Actual: public final actual fun onMember(): Unit
/jvm.kt:(134,142): warning: Annotation `@Ann()` is missing on actual declaration.
All annotations from expect `fun onMember(): Unit` must be present with the same arguments on actual `fun onMember(): Unit`, otherwise they might behave incorrectly.
/jvm.kt:(209,221): warning: All annotations from `expect` must be presented with same arguments on `actual`, otherwise they have no effect.
Expected: @Ann() public final expect class ViaTypealias : Any
Actual: public final class ViaTypealiasImpl : Any
/jvm.kt:(213,225): warning: Annotation `@Ann()` is missing on actual declaration.
All annotations from expect `class ViaTypealias : Any` must be present with the same arguments on actual `class ViaTypealiasImpl : Any`, otherwise they might behave incorrectly.
/jvm.kt:(279,295): warning: Annotation `@WithArg(s = String(str))` has different arguments on actual declaration: `@WithArg(s = String(other str))`.
All annotations from expect `fun withDifferentArg(): Unit` must be present with the same arguments on actual `fun withDifferentArg(): Unit`, otherwise they might behave incorrectly.
@@ -17,6 +17,10 @@ expect class ViaTypealias {
fun foo()
}
annotation class WithArg(val s: String)
@WithArg("str")
expect fun withDifferentArg()
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -31,3 +35,6 @@ class ViaTypealiasImpl {
}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>ViaTypealias<!> = ViaTypealiasImpl<!>
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>@WithArg("other str")
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>withDifferentArg<!>() {}<!>
@@ -17,6 +17,10 @@ expect class ViaTypealias {
fun foo()
}
annotation class WithArg(val s: String)
@WithArg("str")
expect fun withDifferentArg()
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -31,3 +35,6 @@ class ViaTypealiasImpl {
}
actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>ViaTypealias<!> = ViaTypealiasImpl
@WithArg("other str")
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>withDifferentArg<!>() {}