From 8ddc535caff727771f575a3d07f8fc4062d719c0 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 21 Nov 2022 15:35:25 +0100 Subject: [PATCH] Opt-in: forbid/deprecate constructor call with default arguments under marker #KT-55074 Fixed --- .../expression/FirOptInUsageBaseChecker.kt | 25 +++++++----------- .../resolve/checkers/OptInUsageChecker.kt | 22 +++++++++------- .../experimental/constructorCheck.fir.kt | 26 +++++++++++++++++++ .../experimental/constructorCheck.kt | 9 ++++--- .../experimental/constructorCheck.txt | 1 + 5 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.fir.kt diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt index df9d6bfcad6..034645c3a3a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageBaseChecker.kt @@ -150,13 +150,21 @@ object FirOptInUsageBaseChecker { if (fir is FirCallableDeclaration) { val parentClassSymbol = fir.containingClassLookupTag()?.toSymbol(session) as? FirRegularClassSymbol if (fir is FirConstructor) { + // For other callable we check dispatch receiver type instead parentClassSymbol?.loadExperimentalities( context, result, visited, fromSetter = false, dispatchReceiverType = null, fromSupertype = false ) } else { - fir.loadOverridableSpecificExperimentalities(context, visited, result) + // Without coneTypeSafe v fails in MT test (FirRenderer.kt) + fir.returnTypeRef.coneTypeSafe().addExperimentalities(context, result, visited) + fir.receiverParameter?.typeRef?.coneType.addExperimentalities(context, result, visited) } dispatchReceiverType?.addExperimentalities(context, result, visited) + if (fir is FirFunction) { + fir.valueParameters.forEach { + it.returnTypeRef.coneType.addExperimentalities(context, result, visited) + } + } if (fromSetter && this is FirPropertySymbol) { setterSymbol?.loadExperimentalities( context, result, visited, fromSetter = false, dispatchReceiverType, fromSupertype = false @@ -194,21 +202,6 @@ object FirOptInUsageBaseChecker { return result } - private fun FirCallableDeclaration.loadOverridableSpecificExperimentalities( - context: CheckerContext, - visited: MutableSet, - result: SmartSet - ) { - // Without coneTypeSafe v fails in MT test (FirRenderer.kt) - returnTypeRef.coneTypeSafe().addExperimentalities(context, result, visited) - receiverParameter?.typeRef?.coneType.addExperimentalities(context, result, visited) - if (this is FirSimpleFunction) { - valueParameters.forEach { - it.returnTypeRef.coneType.addExperimentalities(context, result, visited) - } - } - } - private fun ConeKotlinType?.addExperimentalities( context: CheckerContext, result: SmartSet, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptInUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptInUsageChecker.kt index 87762264c2f..04d3f1d18d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptInUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptInUsageChecker.kt @@ -224,14 +224,15 @@ class OptInUsageChecker(project: Project) : CallChecker { moduleAnnotationsResolver, context, languageVersionSettings, visited ) ) - if (this is FunctionDescriptor) { - valueParameters.forEach { - result.addAll( - it.type.loadOptIns( - moduleAnnotationsResolver, context, languageVersionSettings, visited - ) + } + if (this is FunctionDescriptor) { + valueParameters.forEach { + result.addAll( + it.type.loadOptIns( + moduleAnnotationsResolver, context, languageVersionSettings, visited, + warningsOnly = this is ConstructorDescriptor ) - } + ) } } @@ -258,19 +259,20 @@ class OptInUsageChecker(project: Project) : CallChecker { moduleAnnotationsResolver: ModuleAnnotationsResolver, context: BindingContext, languageVersionSettings: LanguageVersionSettings, - visitedClassifiers: MutableSet + visitedClassifiers: MutableSet, + warningsOnly: Boolean = false ): Set = when { this?.isError != false -> emptySet() this is AbbreviatedType -> abbreviation.constructor.declarationDescriptor?.loadOptIns( moduleAnnotationsResolver, context, languageVersionSettings, visitedClassifiers, - useFutureError = !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures) + useFutureError = warningsOnly || !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures) ).orEmpty() + expandedType.loadOptIns( moduleAnnotationsResolver, context, languageVersionSettings, visitedClassifiers ) else -> constructor.declarationDescriptor?.loadOptIns( moduleAnnotationsResolver, context, languageVersionSettings, visitedClassifiers, - useFutureError = !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures) + useFutureError = warningsOnly || !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures) ).orEmpty() + arguments.flatMap { if (it.isStarProjection) emptySet() else it.type.loadOptIns(moduleAnnotationsResolver, context, languageVersionSettings, visitedClassifiers) diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.fir.kt new file mode 100644 index 00000000000..bc88923e8b4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.fir.kt @@ -0,0 +1,26 @@ +@RequiresOptIn +annotation class Marker + +@Marker +class Some(val x: Int) + +class Other(val x: Int) { + @OptIn(Marker::class) + constructor(some: Some): this(some.x) + + @Marker + constructor(): this(42) + + @OptIn(Marker::class) + constructor(y: Long, some: Some? = null): this(some?.x ?: y.toInt()) +} + +fun foo(some: Some? = null) {} + +fun test() { + val o1 = Other() + val o2 = Other(Some(0)) + val o3 = Other(444L) + foo() + foo(null) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.kt index dcfc9bb69e3..1e304744673 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.kt @@ -1,4 +1,3 @@ -// FIR_IDENTICAL @RequiresOptIn annotation class Marker @@ -16,8 +15,12 @@ class Other(val x: Int) { constructor(y: Long, some: Some? = null): this(some?.x ?: y.toInt()) } +fun foo(some: Some? = null) {} + fun test() { val o1 = Other() - val o2 = Other(Some(0)) - val o3 = Other(444L) + val o2 = Other(Some(0)) + val o3 = Other(444L) + foo() + foo(null) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.txt index 50d5840eaa8..43c6eb8378c 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/constructorCheck.txt @@ -1,5 +1,6 @@ package +public fun foo(/*0*/ some: Some? = ...): kotlin.Unit public fun test(): kotlin.Unit @kotlin.RequiresOptIn public final annotation class Marker : kotlin.Annotation {