diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 1bd239e7ed5..0667c6258a6 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -153,6 +153,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val VAR_ANNOTATION_PARAMETER by error(PositioningStrategy.VAL_OR_VAR_NODE) val SUPERTYPES_FOR_ANNOTATION_CLASS by error(PositioningStrategy.SUPERTYPES_LIST) val ANNOTATION_USED_AS_ANNOTATION_ARGUMENT by error() + val ILLEGAL_KOTLIN_VERSION_STRING_VALUE by error() } val EXPOSED_VISIBILITY by object : DiagnosticGroup("Exposed visibility") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 3cc61bd4d0f..68e3154e30d 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -161,6 +161,7 @@ object FirErrors { val VAR_ANNOTATION_PARAMETER by error0(SourceElementPositioningStrategies.VAL_OR_VAR_NODE) val SUPERTYPES_FOR_ANNOTATION_CLASS by error0(SourceElementPositioningStrategies.SUPERTYPES_LIST) val ANNOTATION_USED_AS_ANNOTATION_ARGUMENT by error0() + val ILLEGAL_KOTLIN_VERSION_STRING_VALUE by error0() // Exposed visibility val EXPOSED_TYPEALIAS_EXPANDED_TYPE by error3(SourceElementPositioningStrategies.DECLARATION_NAME) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationChecker.kt new file mode 100644 index 00000000000..40f61f1b57d --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationChecker.kt @@ -0,0 +1,57 @@ +/* + * 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.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.FirConstExpression +import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression +import org.jetbrains.kotlin.fir.expressions.unwrapArgument +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.resolve.RequireKotlinConstants + +object FirAnnotationChecker : FirAnnotationCallChecker() { + private val deprecatedSinceKotlinClassId = ClassId.fromString("kotlin/DeprecatedSinceKotlin") + + private val annotationClassIdsWithVersion = setOf( + ClassId.fromString("kotlin/internal/RequireKotlin"), + ClassId.fromString("kotlin/SinceKotlin"), + deprecatedSinceKotlinClassId + ) + + override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { + val classId = ((expression.annotationTypeRef as? FirResolvedTypeRef)?.type as? ConeClassLikeType)?.lookupTag?.classId + if (annotationClassIdsWithVersion.contains(classId)) { + for (argIndex in expression.argumentList.arguments.withIndex()) { + if (argIndex.index > 0 && classId != deprecatedSinceKotlinClassId) { + continue + } + + val arg = argIndex.value + val argSource = arg.source + if (argSource != null) { + val constExpression = (arg as? FirConstExpression<*>) + ?: ((arg as? FirNamedArgumentExpression)?.expression as? FirConstExpression<*>) + if ((constExpression?.value as? String)?.matches(RequireKotlinConstants.VERSION_REGEX) == false) { + reporter.reportOn(argSource, FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE, context) + } + } + } + } + + val args = expression.argumentList.arguments + for (arg in args) { + for (ann in arg.unwrapArgument().annotations) { + reporter.reportOn(ann.source, FirErrors.ANNOTATION_USED_AS_ANNOTATION_ARGUMENT, context) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationUsedAsAnnotationArgumentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationUsedAsAnnotationArgumentChecker.kt deleted file mode 100644 index 349de8efe0b..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAnnotationUsedAsAnnotationArgumentChecker.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.checkers.expression - -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors -import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn -import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall -import org.jetbrains.kotlin.fir.expressions.unwrapArgument - -object FirAnnotationUsedAsAnnotationArgumentChecker : FirAnnotationCallChecker() { - override fun check(expression: FirAnnotationCall, context: CheckerContext, reporter: DiagnosticReporter) { - val args = expression.argumentList.arguments - for (arg in args) { - for (ann in arg.unwrapArgument().annotations) { - reporter.reportOn(ann.source, FirErrors.ANNOTATION_USED_AS_ANNOTATION_ARGUMENT, context) - } - } - } -} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 554f94c417b..8ab7cf18d40 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -127,6 +127,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXPRESSION +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_INFIX_MODIFIER @@ -421,6 +422,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { map.put(VAR_ANNOTATION_PARAMETER, "An annotation parameter cannot be 'var'") map.put(NOT_AN_ANNOTATION_CLASS, "Illegal annotation class: {0}", NULLABLE_STRING) map.put(SUPERTYPES_FOR_ANNOTATION_CLASS, "Annotation class cannot have supertypes") + map.put(ILLEGAL_KOTLIN_VERSION_STRING_VALUE, "Invalid value in version annotation (should be 'major.minor' or 'major.minor.patch')") // Exposed visibility group // # map.put( diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index f1e535f3960..c53c1af6262 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -11,8 +11,8 @@ import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirAnonymousFunctionSyn object CommonExpressionCheckers : ExpressionCheckers() { override val annotationCallCheckers: Set get() = setOf( - FirAnnotationArgumentChecker, - FirAnnotationUsedAsAnnotationArgumentChecker, + FirAnnotationChecker, + FirAnnotationArgumentChecker ) override val basicExpressionCheckers: Set diff --git a/compiler/testData/diagnostics/tests/annotations/illegalRequireKotlinValue.fir.kt b/compiler/testData/diagnostics/tests/annotations/illegalRequireKotlinValue.fir.kt index f253cd804d0..4169d622a37 100644 --- a/compiler/testData/diagnostics/tests/annotations/illegalRequireKotlinValue.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/illegalRequireKotlinValue.fir.kt @@ -1,30 +1,31 @@ +// FIR_IDENTICAL @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") package test import kotlin.internal.RequireKotlin -@RequireKotlin("") +@RequireKotlin("") fun f01() {} -@RequireKotlin("x") +@RequireKotlin("x") fun f02() {} -@RequireKotlin("1") +@RequireKotlin("1") fun f03() {} -@RequireKotlin("1.0-beta") +@RequireKotlin("1.0-beta") fun f04() {} -@RequireKotlin("1.1.0-dev-1111") +@RequireKotlin("1.1.0-dev-1111") fun f05() {} -@RequireKotlin("1.5.3.7") +@RequireKotlin("1.5.3.7") fun f06() {} -@RequireKotlin("1..0") +@RequireKotlin("1..0") fun f07() {} -@RequireKotlin(" 1.0") +@RequireKotlin(" 1.0") fun f08() {} diff --git a/compiler/testData/diagnostics/tests/annotations/illegalSinceKotlinValue.fir.kt b/compiler/testData/diagnostics/tests/annotations/illegalSinceKotlinValue.fir.kt index e5b29c8b002..d24ff2702de 100644 --- a/compiler/testData/diagnostics/tests/annotations/illegalSinceKotlinValue.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/illegalSinceKotlinValue.fir.kt @@ -1,61 +1,61 @@ -@SinceKotlin("") +@SinceKotlin("") fun f01() {} -@SinceKotlin("x") +@SinceKotlin("x") fun f02() {} -@SinceKotlin("1") +@SinceKotlin("1") fun f03() {} -@SinceKotlin("1,0") +@SinceKotlin("1,0") fun f04() {} -@SinceKotlin("1,0,1") +@SinceKotlin("1,0,1") fun f05() {} -@SinceKotlin("a.b") +@SinceKotlin("a.b") fun f06() {} -@SinceKotlin("1.a") +@SinceKotlin("1.a") fun f07() {} -@SinceKotlin("1.0.a") +@SinceKotlin("1.0.a") fun f08() {} -@SinceKotlin("1.0-beta") +@SinceKotlin("1.0-beta") fun f09() {} -@SinceKotlin("1.1.0-dev-1111") +@SinceKotlin("1.1.0-dev-1111") fun f10() {} -@SinceKotlin("1.1.0+rc") +@SinceKotlin("1.1.0+rc") fun f11() {} -@SinceKotlin("1.5.3.7") +@SinceKotlin("1.5.3.7") fun f12() {} -@SinceKotlin("01.1") +@SinceKotlin("01.1") fun f13() {} -@SinceKotlin("1.01") +@SinceKotlin("1.01") fun f14() {} -@SinceKotlin("-1.0") +@SinceKotlin("-1.0") fun f15() {} -@SinceKotlin("1.-1.0") +@SinceKotlin("1.-1.0") fun f16() {} -@SinceKotlin("0.00.1") +@SinceKotlin("0.00.1") fun f17() {} -@SinceKotlin("1..0") +@SinceKotlin("1..0") fun f18() {} -@SinceKotlin(" 1.0") +@SinceKotlin(" 1.0") fun f19() {} -@SinceKotlin("1.0 ") +@SinceKotlin("1.0 ") fun f20() {} diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/checkValuesAreParseableAsVersion.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/checkValuesAreParseableAsVersion.fir.kt index b68c5e38114..a430f7245bd 100644 --- a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/checkValuesAreParseableAsVersion.fir.kt +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/checkValuesAreParseableAsVersion.fir.kt @@ -1,19 +1,19 @@ package kotlin @Deprecated("") -@DeprecatedSinceKotlin("", errorSince = "1.0") +@DeprecatedSinceKotlin("", errorSince = "1.0") fun test1() {} @Deprecated("") -@DeprecatedSinceKotlin("") +@DeprecatedSinceKotlin("") fun test2() {} @Deprecated("") -@DeprecatedSinceKotlin("", "", "") +@DeprecatedSinceKotlin("", "", "") fun test3() {} @Deprecated("") -@DeprecatedSinceKotlin("1.4-M2") +@DeprecatedSinceKotlin("1.4-M2") fun test4() {} @Deprecated("") 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 99e8c92313b..f7f04f1e6ba 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 @@ -514,6 +514,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE) { firDiagnostic -> + IllegalKotlinVersionStringValueImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE) { firDiagnostic -> ExposedTypealiasExpandedTypeImpl( firDiagnostic.a, 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 c77df8c8a93..d0ac437af88 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 @@ -371,6 +371,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = AnnotationUsedAsAnnotationArgument::class } + abstract class IllegalKotlinVersionStringValue : KtFirDiagnostic() { + override val diagnosticClass get() = IllegalKotlinVersionStringValue::class + } + abstract class ExposedTypealiasExpandedType : KtFirDiagnostic() { override val diagnosticClass get() = ExposedTypealiasExpandedType::class abstract val elementVisibility: EffectiveVisibility 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 293b998cf2b..f336e7f54d0 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 @@ -597,6 +597,13 @@ internal class AnnotationUsedAsAnnotationArgumentImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class IllegalKotlinVersionStringValueImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.IllegalKotlinVersionStringValue(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ExposedTypealiasExpandedTypeImpl( override val elementVisibility: EffectiveVisibility, override val restrictingDeclaration: KtSymbol,