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 1174ae41c9d..5b1f7921e4a 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 @@ -157,6 +157,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val NEWER_VERSION_IN_SINCE_KOTLIN by warning { parameter("specifiedVersion") } + val DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS 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 9dfe8b773fa..48781bb58b4 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 @@ -163,6 +163,7 @@ object FirErrors { val ANNOTATION_USED_AS_ANNOTATION_ARGUMENT by error0() val ILLEGAL_KOTLIN_VERSION_STRING_VALUE by error0() val NEWER_VERSION_IN_SINCE_KOTLIN by warning1() + val DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS 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/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index cb6ec1a765f..4edcf475cca 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 @@ -86,6 +86,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATE_SPECIAL_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_MODIFIER_PAIR +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DEPRECATED_TYPE_PARAMETER_SYNTAX import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DESERIALIZATION_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_UPPER_BOUND @@ -423,8 +424,15 @@ 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')") + map.put( + ILLEGAL_KOTLIN_VERSION_STRING_VALUE, + "Invalid value in version annotation (should be 'major.minor' or 'major.minor.patch')" + ) map.put(NEWER_VERSION_IN_SINCE_KOTLIN, "The version is greater than the specified API version {0}", NULLABLE_STRING) + map.put( + DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS, + "Values of DeprecatedSinceKotlin annotation should be ordered so 'warningSince' <= 'errorSince' <= 'hiddenSince' if specified" + ) // Exposed visibility group // # map.put( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index d93d904049b..d7fd71ed5af 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -143,6 +143,7 @@ private fun mapInapplicableCandidateError( ) is UnsafeCall -> mapUnsafeCallError(diagnostic.candidate, rootCause, source, qualifiedAccessSource) is ManyLambdaExpressionArguments -> FirErrors.MANY_LAMBDA_EXPRESSION_ARGUMENTS.on(rootCause.argument.source ?: source) + is DeprecatedSinceKotlinWithUnorderedVersions -> FirErrors.DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS.on(source) else -> null } }.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt index 71e8b0de09c..40b63df05a7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionDiagnostic.kt @@ -101,4 +101,6 @@ class NullForNotNullType( class ManyLambdaExpressionArguments( val argument: FirExpression -) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) \ No newline at end of file +) : ResolutionDiagnostic(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR) + +object DeprecatedSinceKotlinWithUnorderedVersions : ResolutionDiagnostic(INAPPLICABLE) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index f60f8f82fce..e8cfdc421c6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -5,13 +5,12 @@ package org.jetbrains.kotlin.fir.resolve.calls +import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirSymbolOwner import org.jetbrains.kotlin.fir.FirVisibilityChecker import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.expressions.FirExpression -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression -import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.resolve.inference.* import org.jetbrains.kotlin.fir.resolve.toSymbol @@ -22,6 +21,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visibilityChecker +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -144,6 +144,7 @@ internal object CheckArguments : CheckerStage() { override suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext) { val argumentMapping = candidate.argumentMapping ?: error("Argument should be already mapped while checking arguments!") + for (argument in callInfo.arguments) { val parameter = argumentMapping[argument] candidate.resolveArgument( @@ -155,10 +156,70 @@ internal object CheckArguments : CheckerStage() { context = context ) } + + checkDeprecatedSinceKotlinArgs(candidate, argumentMapping, sink) + if (candidate.system.hasContradiction && callInfo.arguments.isNotEmpty()) { sink.yieldDiagnostic(InapplicableCandidate) } } + + private val deprecatedSinceKotlin = CallableId( + FqName("kotlin"), + FqName("DeprecatedSinceKotlin"), + Name.identifier("DeprecatedSinceKotlin") + ) + + private suspend fun checkDeprecatedSinceKotlinArgs( + candidate: Candidate, + argumentMapping: LinkedHashMap, + sink: CheckerSink + ) { + val symbol = candidate.symbol + if (symbol is FirFunctionSymbol<*>) { + if (symbol.callableId == deprecatedSinceKotlin) { + var warningSince: ApiVersion? = null + var errorSince: ApiVersion? = null + var hiddenSince: ApiVersion? = null + for (argument in argumentMapping) { + val identifier = argument.value.name.identifier + if (identifier == "warningSince" || identifier == "errorSince" || identifier == "hiddenSince") { + val argKey = argument.key + val constExpression = (argKey as? FirConstExpression<*>) + ?: ((argKey as? FirNamedArgumentExpression)?.expression as? FirConstExpression<*>) + val stringValue = constExpression?.value as? String + if (stringValue != null) { + val version = ApiVersion.parse(stringValue) + when (identifier) { + "warningSince" -> warningSince = version + "errorSince" -> errorSince = version + "hiddenSince" -> hiddenSince = version + } + } + } + } + + var isReportDeprecatedSinceKotlinWithUnorderedVersions = false + if (warningSince != null) { + if (errorSince != null) { + isReportDeprecatedSinceKotlinWithUnorderedVersions = warningSince > errorSince + } + + if (hiddenSince != null && !isReportDeprecatedSinceKotlinWithUnorderedVersions) { + isReportDeprecatedSinceKotlinWithUnorderedVersions = warningSince > hiddenSince + } + } + + if (errorSince != null && hiddenSince != null && !isReportDeprecatedSinceKotlinWithUnorderedVersions) { + isReportDeprecatedSinceKotlinWithUnorderedVersions = errorSince > hiddenSince + } + + if (isReportDeprecatedSinceKotlinWithUnorderedVersions) { + sink.yieldDiagnostic(DeprecatedSinceKotlinWithUnorderedVersions) + } + } + } + } } internal object EagerResolveOfCallableReferences : CheckerStage() { diff --git a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt index 17137fff6cf..5091d083a2a 100644 --- a/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt +++ b/compiler/testData/diagnostics/tests/deprecated/deprecatedSinceKotlin/deprecatedSinceKotlinDeclaration.fir.kt @@ -25,19 +25,19 @@ fun fooError() {} fun fooHidden() {} @Deprecated("") -@DeprecatedSinceKotlin(warningSince = "1.1", errorSince = "1.0") +@DeprecatedSinceKotlin(warningSince = "1.1", errorSince = "1.0") fun fooWarningIsGreater1() {} @Deprecated("") -@DeprecatedSinceKotlin(warningSince = "1.1", hiddenSince = "1.0") +@DeprecatedSinceKotlin(warningSince = "1.1", hiddenSince = "1.0") fun fooWarningIsGreater2() {} @Deprecated("") -@DeprecatedSinceKotlin(warningSince = "1.1", errorSince = "1.3", hiddenSince = "1.2") +@DeprecatedSinceKotlin(warningSince = "1.1", errorSince = "1.3", hiddenSince = "1.2") fun fooErrorIsGreater() {} @Deprecated("") -@DeprecatedSinceKotlin("1.2", "1.1", "1.1") +@DeprecatedSinceKotlin("1.2", "1.1", "1.1") fun fooDefault() {} @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 f3fb5886cc8..bc2940827f8 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 @@ -527,6 +527,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DEPRECATED_SINCE_KOTLIN_WITH_UNORDERED_VERSIONS) { firDiagnostic -> + DeprecatedSinceKotlinWithUnorderedVersionsImpl( + 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 89da81ea6f4..4b96c19a112 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 @@ -380,6 +380,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val specifiedVersion: String } + abstract class DeprecatedSinceKotlinWithUnorderedVersions : KtFirDiagnostic() { + override val diagnosticClass get() = DeprecatedSinceKotlinWithUnorderedVersions::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 55529fea4ce..0134671c813 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 @@ -612,6 +612,13 @@ internal class NewerVersionInSinceKotlinImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class DeprecatedSinceKotlinWithUnorderedVersionsImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.DeprecatedSinceKotlinWithUnorderedVersions(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ExposedTypealiasExpandedTypeImpl( override val elementVisibility: EffectiveVisibility, override val restrictingDeclaration: KtSymbol,