From 5607be392009fcb200ba1ba98db75722d6368982 Mon Sep 17 00:00:00 2001 From: Nikolay Lunyak Date: Wed, 28 Jul 2021 18:01:20 +0300 Subject: [PATCH] [FIR] Add support for INAPPLICABLE_JVM_NAME diagnostic --- .../diagnostics/FirJvmDiagnosticsList.kt | 2 + .../analysis/diagnostics/jvm/FirJvmErrors.kt | 1 + .../checkers/declaration/FirJvmNameChecker.kt | 39 ++++++++++++++++--- .../diagnostics/FirJvmDefaultErrorMessages.kt | 2 + .../annotationApplicability/jvmName.fir.kt | 10 ++--- .../jvmNamesDuplicate.fir.kt | 8 ++-- .../jetbrains/kotlin/name/StandardClassIds.kt | 6 +++ .../diagnostics/KtFirDataClassConverters.kt | 6 +++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 ++ .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 ++++ 10 files changed, 71 insertions(+), 14 deletions(-) diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 6408da8a666..223e2557a3e 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -20,6 +20,8 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { val DECLARATIONS by object : DiagnosticGroup("Declarations") { val CONFLICTING_JVM_DECLARATIONS by error() + + val INAPPLICABLE_JVM_NAME by error() val ILLEGAL_JVM_NAME by error() } diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index a60f1cede92..b918472719d 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.psi.KtExpression object FirJvmErrors { // Declarations val CONFLICTING_JVM_DECLARATIONS by error0() + val INAPPLICABLE_JVM_NAME by error0() val ILLEGAL_JVM_NAME by error0() // Types diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmNameChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmNameChecker.kt index a9e9af60b42..8c6ec4fd4db 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmNameChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmNameChecker.kt @@ -7,27 +7,33 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers.declaration import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn -import org.jetbrains.kotlin.fir.declarations.FirAnnotatedDeclaration -import org.jetbrains.kotlin.fir.declarations.FirDeclaration -import org.jetbrains.kotlin.fir.declarations.findArgumentByName +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.isInline +import org.jetbrains.kotlin.fir.declarations.utils.isOpen +import org.jetbrains.kotlin.fir.declarations.utils.isOverride import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.getContainingClass import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirJvmNameChecker : FirBasicDeclarationChecker() { + private val NAME = Name.identifier("name") + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration !is FirAnnotatedDeclaration) { return } val jvmName = declaration.findJvmNameAnnotation() ?: return - val name = jvmName.findArgumentByName(Name.identifier("name")) ?: return + val name = jvmName.findArgumentByName(NAME) ?: return if (name.typeRef.coneType != context.session.builtinTypes.stringType.type) { return @@ -38,11 +44,34 @@ object FirJvmNameChecker : FirBasicDeclarationChecker() { if (!Name.isValidIdentifier(value)) { reporter.reportOn(jvmName.source, FirJvmErrors.ILLEGAL_JVM_NAME, context) } + + if (declaration is FirFunction && !context.isRenamableFunction(declaration)) { + reporter.reportOn(jvmName.source, FirJvmErrors.INAPPLICABLE_JVM_NAME, context) + } else if (declaration is FirCallableDeclaration) { + val containingClass = declaration.getContainingClass(context.session) + + if ( + declaration.isOverride || + declaration.isOpen || + containingClass?.isInlineThatRequiresMangling() == true + ) { + reporter.reportOn(jvmName.source, FirJvmErrors.INAPPLICABLE_JVM_NAME, context) + } + } } private fun FirAnnotatedDeclaration.findJvmNameAnnotation(): FirAnnotationCall? { return annotations.firstOrNull { - it.calleeReference.safeAs()?.name?.toString() == "JvmName" + it.calleeReference.safeAs()?.name == StandardClassIds.JvmName.shortClassName } } + + private fun CheckerContext.isRenamableFunction(function: FirFunction): Boolean { + val containingClass = function.getContainingClassSymbol(session) + return containingClass != null || !function.symbol.callableId.isLocal + } + + private fun FirRegularClass.isInlineThatRequiresMangling(): Boolean { + return isInline && name == StandardClassIds.Result.shortClassName + } } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt index 710e5a33311..f4963bea858 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/diagnostics/FirJvmDefaultErrorMessages.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ENUM_JVM_R import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.FIELD_IN_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ILLEGAL_JVM_NAME +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INNER_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_PACKAGE_NAME_CANNOT_BE_EMPTY @@ -95,6 +96,7 @@ object FirJvmDefaultErrorMessages { map.put(NON_DATA_CLASS_JVM_RECORD, "Only data classes are allowed to be marked as @JvmRecord") map.put(ILLEGAL_JAVA_LANG_RECORD_SUPERTYPE, "Classes cannot have explicit 'java.lang.Record' supertype") + map.put(INAPPLICABLE_JVM_NAME, "'@JvmName' annotation is not applicable to this declaration") map.put(ILLEGAL_JVM_NAME, "Illegal JVM name") } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/jvmName.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/jvmName.fir.kt index 1319ffd3d8a..7515fdb16c5 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/jvmName.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/annotationApplicability/jvmName.fir.kt @@ -58,7 +58,7 @@ class C @JvmName("primary") constructor() { } fun foo1() { - @JvmName("a") + @JvmName("a") fun foo() {} @JvmName("a") @@ -71,17 +71,17 @@ abstract class AB { abstract fun absFun2() - @JvmName("AB_openFun") + @JvmName("AB_openFun") open fun openFun() {} } class D: AB() { override fun absFun1() {} - @JvmName("D_absFun2") + @JvmName("D_absFun2") override fun absFun2() {} - @JvmName("D_openFun") + @JvmName("D_openFun") final override fun openFun() {} @JvmName("D_finalFun") @@ -92,4 +92,4 @@ interface Intf { @get:JvmName("getBar") // no error in IDE @set:JvmName("setBar") // no error in IDE var foo: Int -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/duplicateJvmSignature/jvmNamesDuplicate.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/duplicateJvmSignature/jvmNamesDuplicate.fir.kt index 10d82927fd3..d0e9db24970 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/duplicateJvmSignature/jvmNamesDuplicate.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/duplicateJvmSignature/jvmNamesDuplicate.fir.kt @@ -17,7 +17,7 @@ class C { // A1 -> B1 with accidental override open class A1 { - @JvmName("bar") + @JvmName("bar") open fun foo() {} } @@ -28,7 +28,7 @@ class B1 : A1() { // A2 -> B2 with intended override and conflicting JVM declarations open class A2 { - @JvmName("bar") + @JvmName("bar") open fun foo() {} } @@ -41,7 +41,7 @@ class B2 : A2() { // A3 -> B3 -> C3 with accidental override open class A3 { - @JvmName("bar") + @JvmName("bar") open fun foo() {} } @@ -50,4 +50,4 @@ open class B3: A3() { class C3: B3() { fun bar() {} -} \ No newline at end of file +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt index 206cf0fa6e1..710f0cfe44d 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/name/StandardClassIds.kt @@ -12,11 +12,13 @@ object StandardClassIds { val BASE_KOTLIN_PACKAGE = FqName("kotlin") val BASE_REFLECT_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("reflect")) val BASE_COLLECTIONS_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("collections")) + val BASE_JVM_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("jvm")) private fun String.baseId() = ClassId(BASE_KOTLIN_PACKAGE, Name.identifier(this)) private fun ClassId.unsignedId() = ClassId(BASE_KOTLIN_PACKAGE, Name.identifier("U" + shortClassName.identifier)) private fun String.reflectId() = ClassId(BASE_REFLECT_PACKAGE, Name.identifier(this)) private fun Name.primitiveArrayId() = ClassId(Array.packageFqName, Name.identifier(identifier + Array.shortClassName.identifier)) private fun String.collectionsId() = ClassId(BASE_COLLECTIONS_PACKAGE, Name.identifier(this)) + private fun String.jvmId() = ClassId(BASE_JVM_PACKAGE, Name.identifier(this)) val Nothing = "Nothing".baseId() val Unit = "Unit".baseId() @@ -114,6 +116,10 @@ object StandardClassIds { val coroutineContext = CallableId(StandardNames.COROUTINES_PACKAGE_FQ_NAME, Name.identifier("coroutineContext")) val suspend = CallableId(BASE_KOTLIN_PACKAGE, Name.identifier("suspend")) + + val JvmName = "JvmName".jvmId() + + val Result = "Result".baseId() } private fun Map.inverseMap() = entries.associate { (k, v) -> v to k } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 7e052662f24..808fd5b9dd2 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3664,6 +3664,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.INAPPLICABLE_JVM_NAME) { firDiagnostic -> + InapplicableJvmNameImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirJvmErrors.ILLEGAL_JVM_NAME) { firDiagnostic -> IllegalJvmNameImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 9aeab6dc246..871d32c1fc1 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2550,6 +2550,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = IllegalJavaLangRecordSupertype::class } + abstract class InapplicableJvmName : KtFirDiagnostic() { + override val diagnosticClass get() = InapplicableJvmName::class + } + abstract class IllegalJvmName : KtFirDiagnostic() { override val diagnosticClass get() = IllegalJvmName::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index bf5d091fde6..0ce887ccb48 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -4138,6 +4138,13 @@ internal class IllegalJavaLangRecordSupertypeImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class InapplicableJvmNameImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.InapplicableJvmName(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class IllegalJvmNameImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,