[FIR] JVM_PACKAGE_NAME* diagnostics

This commit is contained in:
Andrey Zinovyev
2021-08-18 15:15:49 +03:00
committed by TeamCityServer
parent ae558c0290
commit 4661656b8c
10 changed files with 111 additions and 8 deletions
@@ -45,6 +45,10 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
val DEPRECATED_JAVA_ANNOTATION by warning<KtAnnotationEntry>() {
parameter<FqName>("kotlinName")
}
val JVM_PACKAGE_NAME_CANNOT_BE_EMPTY by error<KtAnnotationEntry>()
val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error<KtAnnotationEntry>()
val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error<KtAnnotationEntry>()
}
}
@@ -39,5 +39,8 @@ object FirJvmErrors {
val OVERLOADS_ANNOTATION_CLASS_CONSTRUCTOR by deprecationError0<KtAnnotationEntry>(ProhibitJvmOverloadsOnConstructorsOfAnnotationClasses)
val OVERLOADS_PRIVATE by warning0<KtAnnotationEntry>()
val DEPRECATED_JAVA_ANNOTATION by warning1<KtAnnotationEntry, FqName>()
val JVM_PACKAGE_NAME_CANNOT_BE_EMPTY by error0<KtAnnotationEntry>()
val JVM_PACKAGE_NAME_MUST_BE_VALID_NAME by error0<KtAnnotationEntry>()
val JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES by error0<KtAnnotationEntry>()
}
@@ -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<FirFunctionCallChecker>
@@ -20,5 +21,6 @@ object JvmExpressionCheckers : ExpressionCheckers() {
override val annotationCallCheckers: Set<FirAnnotationCallChecker>
get() = setOf(
FirDeprecatedJavaAnnotationsChecker,
FirJvmPackageNameAnnotationsChecker,
)
}
@@ -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<ConeClassLikeType>()?.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)
}
}
}
@@ -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<FirConstExpression<*>>()?.value as? String
}
@@ -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<FirConstExpression<*>>()?.value as? String
}
private fun FirAnnotationCall.getVersionFromArgument(name: Name): ApiVersion? =
getStringArgument(name)?.let { ApiVersion.parse(it) }
@@ -1,17 +1,17 @@
// !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE
// FILE: b.kt
@file:JvmPackageName("")
<!JVM_PACKAGE_NAME_CANNOT_BE_EMPTY!>@file:JvmPackageName("")<!>
package b
fun b() {}
// FILE: c.kt
@file:JvmPackageName("invalid-fq-name")
<!JVM_PACKAGE_NAME_MUST_BE_VALID_NAME!>@file:JvmPackageName("invalid-fq-name")<!>
package c
fun c() {}
// FILE: d.kt
@file:JvmPackageName("d")
<!JVM_PACKAGE_NAME_NOT_SUPPORTED_IN_FILES_WITH_CLASSES!>@file:JvmPackageName("d")<!>
package d
class D
fun d() {}
@@ -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,
)
}
}
@@ -2461,4 +2461,16 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val kotlinName: FqName
}
abstract class JvmPackageNameCannotBeEmpty : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = JvmPackageNameCannotBeEmpty::class
}
abstract class JvmPackageNameMustBeValidName : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = JvmPackageNameMustBeValidName::class
}
abstract class JvmPackageNameNotSupportedInFilesWithClasses : KtFirDiagnostic<KtAnnotationEntry>() {
override val diagnosticClass get() = JvmPackageNameNotSupportedInFilesWithClasses::class
}
}
@@ -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<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class JvmPackageNameMustBeValidNameImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.JvmPackageNameMustBeValidName(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class JvmPackageNameNotSupportedInFilesWithClassesImpl(
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.JvmPackageNameNotSupportedInFilesWithClasses(), KtAbstractFirDiagnostic<KtAnnotationEntry> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}