K1: introduce smartcast deprecation for a public API property in invisible class

#KT-56511 Fixed
Related to KT-57290
This commit is contained in:
Mikhail Glukhikh
2023-03-13 13:44:24 +01:00
committed by Space Team
parent 0b31e5ad63
commit 41c868445c
7 changed files with 61 additions and 16 deletions
@@ -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
@@ -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
@@ -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))
@@ -0,0 +1,10 @@
// -- Module: <m1> --
// -- Module: <m2> --
/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
^
@@ -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
}
}
@@ -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) {
<!DEBUG_INFO_SMARTCAST!>x<!>.length
<!DEBUG_INFO_SMARTCAST, DEPRECATED_SMARTCAST!>x<!>.length
}
}
}
internal class Internal : Base("456")
internal fun bar(i: Internal) {
if (i.x is String) {
<!DEBUG_INFO_SMARTCAST, DEPRECATED_SMARTCAST!>i.x<!>.length
}
}
@@ -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 --------------------------------------------------