From 41c868445c9d84ecd0d97027e36dee0af1ab6b36 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 13 Mar 2023 13:44:24 +0100 Subject: [PATCH] K1: introduce smartcast deprecation for a public API property in invisible class #KT-56511 Fixed Related to KT-57290 --- .../resolve/calls/smartcasts/DataFlowValue.kt | 10 +++++- .../smartcasts/DataFlowValueKindUtils.kt | 35 +++++++++++-------- .../calls/smartcasts/SmartCastManager.kt | 1 + ...CastOnAlienPropertyFromInnerClass.diag.txt | 10 ++++++ ...rtCastOnAlienPropertyFromInnerClass.fir.kt | 9 +++++ .../smartCastOnAlienPropertyFromInnerClass.kt | 11 +++++- .../kotlin/config/LanguageVersionSettings.kt | 1 + 7 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.diag.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt index 19616215f9a..eaf848685c7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValue.kt @@ -57,6 +57,13 @@ class DataFlowValue( // Should be unstable, but can be used as stable with deprecation warning LEGACY_ALIEN_BASE_PROPERTY("alien derived", "property declared in base class from different module"), + // Protected / public member value from derived class from another module, in case the derived class is non-public API + // Should be unstable, but can be used as stable with deprecation warning + LEGACY_ALIEN_BASE_PROPERTY_INHERITED_IN_INVISIBLE_CLASS( + "alien inherited in invisible", + "property declared in base class from different module inherited in non-public API class" + ), + // Protected / public member value from another module // Smart casts are not safe ALIEN_PUBLIC_PROPERTY("alien public", "public API property declared in different module"), @@ -88,7 +95,8 @@ class DataFlowValue( kind == Kind.STABLE_VARIABLE || kind == Kind.STABLE_COMPLEX_EXPRESSION || kind == Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY || - kind == Kind.LEGACY_ALIEN_BASE_PROPERTY + kind == Kind.LEGACY_ALIEN_BASE_PROPERTY || + kind == Kind.LEGACY_ALIEN_BASE_PROPERTY_INHERITED_IN_INVISIBLE_CLASS val canBeBound get() = identifierInfo.canBeBound diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt index 42ad7cb7908..e1d895a7a7b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/DataFlowValueKindUtils.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.resolve.calls.smartcasts import org.jetbrains.kotlin.cfg.getDeclarationDescriptorIncludingConstructors import org.jetbrains.kotlin.cfg.getElementParentDeclaration -import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageFeature.* import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor @@ -26,21 +26,29 @@ internal fun PropertyDescriptor.propertyKind( if (isVar) return DataFlowValue.Kind.MUTABLE_PROPERTY if (isOverridable) return DataFlowValue.Kind.PROPERTY_WITH_GETTER if (!hasDefaultGetter()) return DataFlowValue.Kind.PROPERTY_WITH_GETTER - if (!isInvisibleFromOtherModules()) { - val declarationModule = DescriptorUtils.getContainingModule(this) - if (!areCompiledTogether(usageModule, declarationModule)) { - return DataFlowValue.Kind.ALIEN_PUBLIC_PROPERTY - } - if (kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - if (overriddenDescriptors.any { isDeclaredInAnotherModule(usageModule) }) { - return if (!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSmartcastsOnPropertyFromAlienBaseClass)) { + val originalDescriptor = DescriptorUtils.unwrapFakeOverride(this) + val isInvisibleFromOtherModuleUnwrappedFakeOverride = originalDescriptor.isInvisibleFromOtherModules() + if (isInvisibleFromOtherModuleUnwrappedFakeOverride) return DataFlowValue.Kind.STABLE_VALUE + + if (kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + if (overriddenDescriptors.any { isDeclaredInAnotherModule(usageModule) }) { + val deprecationForInvisibleFakeOverride = + isInvisibleFromOtherModules() != isInvisibleFromOtherModuleUnwrappedFakeOverride && + !languageVersionSettings.supportsFeature(ProhibitSmartcastsOnPropertyFromAlienBaseClassInheritedInInvisibleClass) + return when { + deprecationForInvisibleFakeOverride -> + DataFlowValue.Kind.LEGACY_ALIEN_BASE_PROPERTY_INHERITED_IN_INVISIBLE_CLASS + !languageVersionSettings.supportsFeature(ProhibitSmartcastsOnPropertyFromAlienBaseClass) -> DataFlowValue.Kind.LEGACY_ALIEN_BASE_PROPERTY - } else { + else -> DataFlowValue.Kind.ALIEN_PUBLIC_PROPERTY - } } } } + + val declarationModule = DescriptorUtils.getContainingModule(originalDescriptor) + if (!areCompiledTogether(usageModule, declarationModule)) return DataFlowValue.Kind.ALIEN_PUBLIC_PROPERTY + return DataFlowValue.Kind.STABLE_VALUE } @@ -76,7 +84,7 @@ internal fun VariableDescriptor.variableKind( if (this is LocalVariableDescriptor && this.isDelegated) { // Local delegated property: normally unstable, but can be treated as stable in legacy mode - return if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSmartcastsOnLocalDelegatedProperty)) + return if (languageVersionSettings.supportsFeature(ProhibitSmartcastsOnLocalDelegatedProperty)) DataFlowValue.Kind.PROPERTY_WITH_GETTER else DataFlowValue.Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY @@ -101,7 +109,7 @@ internal fun VariableDescriptor.variableKind( val variableContainingDeclaration = this.containingDeclaration if (isAccessedInsideClosure(variableContainingDeclaration, bindingContext, accessElement)) { // stable iff we have no writers in closures AND this closure is AFTER all writers - return if (preliminaryVisitor.languageVersionSettings.supportsFeature(LanguageFeature.CapturedInClosureSmartCasts) && + return if (preliminaryVisitor.languageVersionSettings.supportsFeature(CapturedInClosureSmartCasts) && hasNoWritersInClosures(variableContainingDeclaration, writers, bindingContext) && isAccessedInsideClosureAfterAllWriters(writers, accessElement) ) { @@ -157,7 +165,6 @@ private fun isAccessedBeforeAllClosureWriters( return true } - private fun DeclarationDescriptorWithVisibility.isInvisibleFromOtherModules(): Boolean { if (DescriptorVisibilities.INVISIBLE_FROM_OTHER_MODULES.contains(visibility)) return true diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt index d33ac250c31..3c41a017031 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.kt @@ -226,6 +226,7 @@ class SmartCastManager(private val argumentTypeResolver: ArgumentTypeResolver) { if (KotlinBuiltIns.isNullableNothing(type)) return if (dataFlowValue.isStable) { if (dataFlowValue.kind == DataFlowValue.Kind.LEGACY_ALIEN_BASE_PROPERTY || + dataFlowValue.kind == DataFlowValue.Kind.LEGACY_ALIEN_BASE_PROPERTY_INHERITED_IN_INVISIBLE_CLASS || dataFlowValue.kind == DataFlowValue.Kind.LEGACY_STABLE_LOCAL_DELEGATED_PROPERTY ) { trace.report(Errors.DEPRECATED_SMARTCAST.on(expression, type, expression.text, dataFlowValue.kind.description)) diff --git a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.diag.txt b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.diag.txt new file mode 100644 index 00000000000..5020b6e1af0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.diag.txt @@ -0,0 +1,10 @@ +// -- Module: -- + +// -- Module: -- +/B.kt:13:13: warning: smart cast to 'String' is deprecated, because 'x' is a property declared in base class from different module inherited in non-public API class + x.length + ^ +/B.kt:22:9: warning: smart cast to 'String' is deprecated, because 'i.x' is a property declared in base class from different module inherited in non-public API class + i.x.length + ^ + diff --git a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.fir.kt index dd177536d37..d78f2d16c82 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.fir.kt @@ -1,3 +1,4 @@ +// RENDER_DIAGNOSTICS_FULL_TEXT // MODULE: m1 // FILE: A.kt @@ -13,3 +14,11 @@ private class Derived : Base("123") { } } } + +internal class Internal : Base("456") + +internal fun bar(i: Internal) { + if (i.x is String) { + i.x.length + } +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.kt b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.kt index f637ee16803..d8694c2e9ce 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/smartCastOnAlienPropertyFromInnerClass.kt @@ -1,3 +1,4 @@ +// RENDER_DIAGNOSTICS_FULL_TEXT // MODULE: m1 // FILE: A.kt @@ -9,7 +10,15 @@ open class Base(val x: Any) private class Derived : Base("123") { fun foo() { if (x is String) { - x.length + x.length } } } + +internal class Internal : Base("456") + +internal fun bar(i: Internal) { + if (i.x is String) { + i.x.length + } +} diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index bba4b444bdc..f7760b2b08c 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -285,6 +285,7 @@ enum class LanguageFeature( EnhanceNullabilityOfPrimitiveArrays(KOTLIN_2_0, kind = BUG_FIX), // KT-54521 AllowEmptyIntersectionsInResultTypeResolver(KOTLIN_2_0, kind = OTHER), // KT-51221 + ProhibitSmartcastsOnPropertyFromAlienBaseClassInheritedInInvisibleClass(KOTLIN_2_0, kind = BUG_FIX), // KT-57290 // End of 2.* language features --------------------------------------------------