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 2f65b538860..596f325ce8a 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 @@ -45,6 +45,10 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { val DEPRECATED_JAVA_ANNOTATION by warning() { parameter("kotlinName") } + + val JVM_PACKAGE_NAME_CANNOT_BE_EMPTY by error() + val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error() + val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES 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 3ae15d9f6b7..097e022b876 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 @@ -39,5 +39,8 @@ object FirJvmErrors { val OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR by deprecationError0(ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses) val OVERLOADS_PRIVATE by warning0() val DEPRECATED_JAVA_ANNOTATION by warning1() + val JVM_PACKAGE_NAME_CANNOT_BE_EMPTY by error0() + val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error0() + val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error0() } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index 1cb43130a62..c013bae4a4a 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationCallCh import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirDeprecatedJavaAnnotationsChecker import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirJavaGenericVarianceViolationTypeChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirJvmPackageNameAnnotationsChecker object JvmExpressionCheckers : ExpressionCheckers() { override val functionCallCheckers: Set @@ -20,5 +21,6 @@ object JvmExpressionCheckers : ExpressionCheckers() { override val annotationCallCheckers: Set get() = setOf( FirDeprecatedJavaAnnotationsChecker, + FirJvmPackageNameAnnotationsChecker, ) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmPackageNameAnnotationsChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmPackageNameAnnotationsChecker.kt new file mode 100644 index 00000000000..1f6bd5c7244 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmPackageNameAnnotationsChecker.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2021 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.jvm.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirAnnotationCallChecker +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.FirClass +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.getStringArgument +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.coneTypeSafe +import org.jetbrains.kotlin.name.* + +object FirJvmPackageNameAnnotationsChecker : FirAnnotationCallChecker() { + + private val jvmPackageNameClassId = ClassId.topLevel(FqName("kotlin.jvm.JvmPackageName")) + private val stringParameterName = Name.identifier("name") + + override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { + val lookupTag = expression.annotationTypeRef.coneTypeSafe()?.lookupTag ?: return + if (lookupTag.classId != jvmPackageNameClassId) return + + val nameValue = expression.getStringArgument(stringParameterName) ?: return + if (nameValue.isEmpty()) { + reporter.reportOn(expression.source, FirJvmErrors.JVM_PACKAGE_NAME_CANNOT_BE_EMPTY, context) + } else if (!isValidJavaFqName(nameValue)) { + reporter.reportOn(expression.source, FirJvmErrors.JVM_PACKAGE_NAME_MUST_BE_VALID_NAME, context) + } + + val file = context.containingDeclarations.firstOrNull() as? FirFile ?: return + if (file.declarations.any { it is FirClass }) { + reporter.reportOn(expression.source, FirJvmErrors.JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES, context) + } + } +} \ No newline at end of file diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 6cd3d977086..42e9cc78120 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.fir.types.coneTypeSafe import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addToStdlib.safeAs private val RETENTION_CLASS_ID = ClassId.fromString("kotlin/annotation/Retention") private val TARGET_CLASS_ID = ClassId.fromString("kotlin/annotation/Target") @@ -125,3 +126,8 @@ fun FirAnnotationCall.findArgumentByName(name: Name): FirExpression? { // TODO: this line is still needed. However it should be replaced with 'return null' return arguments.singleOrNull() } + +fun FirAnnotationCall.getStringArgument(name: Name): String? = + findArgumentByName(name)?.let { expression -> + expression.safeAs>()?.value as? String + } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt index 3302e1517a8..1849f14c1fe 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/declarations/deprecationUtils.kt @@ -90,11 +90,6 @@ private fun FirBasedSymbol<*>.getDeprecationForCallSite( return (deprecations ?: EmptyDeprecationsPerUseSite).forUseSite(*sites) } -private fun FirAnnotationCall.getStringArgument(name: Name): String? = - findArgumentByName(name)?.let { expression -> - expression.safeAs>()?.value as? String - } - private fun FirAnnotationCall.getVersionFromArgument(name: Name): ApiVersion? = getStringArgument(name)?.let { ApiVersion.parse(it) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmPackageName/incorrectJvmPackageName.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmPackageName/incorrectJvmPackageName.fir.kt index b9e41cdcdd9..4b2b2b94c24 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmPackageName/incorrectJvmPackageName.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/jvmPackageName/incorrectJvmPackageName.fir.kt @@ -1,17 +1,17 @@ // !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE // FILE: b.kt -@file:JvmPackageName("") +@file:JvmPackageName("") package b fun b() {} // FILE: c.kt -@file:JvmPackageName("invalid-fq-name") +@file:JvmPackageName("invalid-fq-name") package c fun c() {} // FILE: d.kt -@file:JvmPackageName("d") +@file:JvmPackageName("d") package d class D fun d() {} 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 f5e57c7cef8..be405b2c36b 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 @@ -3532,4 +3532,22 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.JVM_PACKAGE_NAME_CANNOT_BE_EMPTY) { firDiagnostic -> + JvmPackageNameCannotBeEmptyImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirJvmErrors.JVM_PACKAGE_NAME_MUST_BE_VALID_NAME) { firDiagnostic -> + JvmPackageNameMustBeValidNameImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirJvmErrors.JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES) { firDiagnostic -> + JvmPackageNameNotSupportedInFilesWithClassesImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } } 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 c47f6903036..58f99d21d50 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 @@ -2461,4 +2461,16 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val kotlinName: FqName } + abstract class JvmPackageNameCannotBeEmpty : KtFirDiagnostic() { + override val diagnosticClass get() = JvmPackageNameCannotBeEmpty::class + } + + abstract class JvmPackageNameMustBeValidName : KtFirDiagnostic() { + override val diagnosticClass get() = JvmPackageNameMustBeValidName::class + } + + abstract class JvmPackageNameNotSupportedInFilesWithClasses : KtFirDiagnostic() { + override val diagnosticClass get() = JvmPackageNameNotSupportedInFilesWithClasses::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 0f5e9f65f2c..c118f28557e 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 @@ -3986,3 +3986,24 @@ internal class DeprecatedJavaAnnotationImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class JvmPackageNameCannotBeEmptyImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JvmPackageNameCannotBeEmpty(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class JvmPackageNameMustBeValidNameImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JvmPackageNameMustBeValidName(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class JvmPackageNameNotSupportedInFilesWithClassesImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JvmPackageNameNotSupportedInFilesWithClasses(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} +