diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 7238e2d09ca..9061e417143 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -27554,6 +27554,18 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/scopes/initializerScopeOfExtensionProperty.kt"); } + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClass.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt"); + } + + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClassOn.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClassOn() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt"); + } + @Test @TestMetadata("invisibleSetter.kt") public void testInvisibleSetter() throws Exception { diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 1e90d730b88..37001f80a67 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -27554,6 +27554,18 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/scopes/initializerScopeOfExtensionProperty.kt"); } + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClass.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt"); + } + + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClassOn.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClassOn() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt"); + } + @Test @TestMetadata("invisibleSetter.kt") public void testInvisibleSetter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index cccb0d8c962..27e1c859594 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -27554,6 +27554,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/scopes/initializerScopeOfExtensionProperty.kt"); } + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClass.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt"); + } + + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClassOn.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClassOn() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt"); + } + @Test @TestMetadata("invisibleSetter.kt") public void testInvisibleSetter() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index fe53e4ca0f8..5acda395aeb 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -27566,6 +27566,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/scopes/initializerScopeOfExtensionProperty.kt"); } + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClass.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt"); + } + + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClassOn.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClassOn() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt"); + } + @Test @TestMetadata("invisibleSetter.kt") public void testInvisibleSetter() throws Exception { diff --git a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProviderImpl.kt b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProviderImpl.kt index 711cf160a5b..0eb4d7a0ecf 100644 --- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProviderImpl.kt +++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProviderImpl.kt @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.calls.util.* import org.jetbrains.kotlin.resolve.checkers.PlatformDiagnosticSuppressor +import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver @@ -494,15 +495,19 @@ class ControlFlowInformationProviderImpl private constructor( if (DescriptorVisibilityUtils.isVisible(receiverValue, variableDescriptor, descriptor, languageVersionSettings) && setterDescriptor != null - && !DescriptorVisibilityUtils.isVisible(receiverValue, setterDescriptor, descriptor, languageVersionSettings) ) { - report( - Errors.INVISIBLE_SETTER.on( - expression, variableDescriptor, setterDescriptor.visibility, - setterDescriptor - ), ctxt - ) - return true + if (!DescriptorVisibilityUtils.isVisible(receiverValue, setterDescriptor, descriptor, languageVersionSettings)) { + report( + INVISIBLE_SETTER.on( + expression, variableDescriptor, setterDescriptor.visibility, + setterDescriptor + ), ctxt + ) + return true + } else { + // don't return anything as only warning is reported (not error), so further diagnostics are also important + reportVisibilityWarningForInternalFakeSetterOverride(setterDescriptor, expression, variableDescriptor, ctxt) + } } } val isThisOrNoDispatchReceiver = PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.bindingContext) @@ -560,6 +565,35 @@ class ControlFlowInformationProviderImpl private constructor( return false } + private fun reportVisibilityWarningForInternalFakeSetterOverride( + setterDescriptor: PropertySetterDescriptor, + expression: KtExpression, + variableDescriptor: PropertyDescriptor, + ctxt: VariableInitContext + ) { + if (setterDescriptor.kind.isReal) return + if (setterDescriptor.visibility.isPublicAPI) return + + val containingClass = setterDescriptor.containingDeclaration as? ClassDescriptor ?: return + val firstRealOverridden = setterDescriptor.firstOverridden { it.kind.isReal } ?: return + + val visibleOverrides = OverridingUtil.filterVisibleFakeOverrides(containingClass, listOf(firstRealOverridden)) + if (visibleOverrides.isEmpty()) { + val diagnostic = + when (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitAccessToInvisibleSetterFromDerivedClass)) { + true -> INVISIBLE_SETTER + else -> INVISIBLE_SETTER_FROM_DERIVED + } + + report( + diagnostic.on( + expression, variableDescriptor, setterDescriptor.visibility, + setterDescriptor + ), ctxt + ) + } + } + private fun reportValReassigned(expression: KtExpression, variableDescriptor: VariableDescriptor, ctxt: VariableInitContext) { report(VAL_REASSIGNMENT_VIA_BACKING_FIELD.on(languageVersionSettings, expression, variableDescriptor), ctxt) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 0ea6229dedd..dbd9dbcbb84 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1204,6 +1204,8 @@ public interface Errors { DiagnosticFactory3 INVISIBLE_SETTER = DiagnosticFactory3.create(ERROR); + DiagnosticFactory3 INVISIBLE_SETTER_FROM_DERIVED = + DiagnosticFactory3.create(WARNING); DiagnosticFactory1 VAL_OR_VAR_ON_LOOP_PARAMETER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 VAL_OR_VAR_ON_FUN_PARAMETER = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 782b708cdf5..1246a34fb65 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -414,6 +414,8 @@ public class DefaultErrorMessages { MAP.put(SETTER_PROJECTED_OUT, "Setter for ''{0}'' is removed by type projection", NAME); MAP.put(INVISIBLE_SETTER, "Cannot assign to ''{0}'': the setter is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE); + MAP.put(INVISIBLE_SETTER_FROM_DERIVED, "Cannot assign to ''{0}'': the setter is {1} in {2}. This warning will be an error soon. See https://youtrack.jetbrains.com/issue/KT-56662 for details", NAME, VISIBILITY, + NAME_OF_CONTAINING_DECLARATION_OR_FILE); MAP.put(INITIALIZATION_BEFORE_DECLARATION, "Variable cannot be initialized before declaration", NAME); MAP.put(VARIABLE_EXPECTED, "Variable expected"); diff --git a/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.fir.kt b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.fir.kt new file mode 100644 index 00000000000..61fe021dd2a --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.fir.kt @@ -0,0 +1,56 @@ +// !LANGUAGE:-ProhibitAccessToInvisibleSetterFromDerivedClass +// IGNORE_REVERSED_RESOLVE + +// MODULE: m1 +// FILE: base.kt + +open class Base { + var foo: String = "" + internal set +} + +// MODULE: m2(m1) +// FILE: derived1.kt + +fun testBase(b: Base) { + b.foo = "other" +} + +open class Derived1(foo: String) : Base() { + init { + this.foo = foo + } + + fun bar(param: String) { + foo = param + this.foo = param + } +} + +open class Derived2 : Derived1("") + +fun testFunction(d: Derived1) { + d.foo = "other" +} + +// MODULE: m3(m2, m1) +// FILE: derived2.kt + +fun testDerivied1(d: Derived1) { + d.foo = "other" +} + +fun testDerivied2(d: Derived2) { + d.foo = "other" +} + +class Derivied3(foo: String) : Derived2() { + init { + this.foo = foo + } + + fun bar1(param: String) { + foo = param + this.foo = param + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt new file mode 100644 index 00000000000..50176c96cc6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt @@ -0,0 +1,56 @@ +// !LANGUAGE:-ProhibitAccessToInvisibleSetterFromDerivedClass +// IGNORE_REVERSED_RESOLVE + +// MODULE: m1 +// FILE: base.kt + +open class Base { + var foo: String = "" + internal set +} + +// MODULE: m2(m1) +// FILE: derived1.kt + +fun testBase(b: Base) { + b.foo = "other" +} + +open class Derived1(foo: String) : Base() { + init { + this.foo = foo + } + + fun bar(param: String) { + foo = param + this.foo = param + } +} + +open class Derived2 : Derived1("") + +fun testFunction(d: Derived1) { + d.foo = "other" +} + +// MODULE: m3(m2, m1) +// FILE: derived2.kt + +fun testDerivied1(d: Derived1) { + d.foo = "other" +} + +fun testDerivied2(d: Derived2) { + d.foo = "other" +} + +class Derivied3(foo: String) : Derived2() { + init { + this.foo = foo + } + + fun bar1(param: String) { + foo = param + this.foo = param + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.fir.kt b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.fir.kt new file mode 100644 index 00000000000..e0356a364c2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.fir.kt @@ -0,0 +1,56 @@ +// !LANGUAGE:+ProhibitAccessToInvisibleSetterFromDerivedClass +// IGNORE_REVERSED_RESOLVE + +// MODULE: m1 +// FILE: base.kt + +open class Base { + var foo: String = "" + internal set +} + +// MODULE: m2(m1) +// FILE: derived1.kt + +fun testBase(b: Base) { + b.foo = "other" +} + +open class Derived1(foo: String) : Base() { + init { + this.foo = foo + } + + fun bar(param: String) { + foo = param + this.foo = param + } +} + +open class Derived2 : Derived1("") + +fun testFunction(d: Derived1) { + d.foo = "other" +} + +// MODULE: m3(m2, m1) +// FILE: derived2.kt + +fun testDerivied1(d: Derived1) { + d.foo = "other" +} + +fun testDerivied2(d: Derived2) { + d.foo = "other" +} + +class Derivied3(foo: String) : Derived2() { + init { + this.foo = foo + } + + fun bar1(param: String) { + foo = param + this.foo = param + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt new file mode 100644 index 00000000000..44830419f4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt @@ -0,0 +1,56 @@ +// !LANGUAGE:+ProhibitAccessToInvisibleSetterFromDerivedClass +// IGNORE_REVERSED_RESOLVE + +// MODULE: m1 +// FILE: base.kt + +open class Base { + var foo: String = "" + internal set +} + +// MODULE: m2(m1) +// FILE: derived1.kt + +fun testBase(b: Base) { + b.foo = "other" +} + +open class Derived1(foo: String) : Base() { + init { + this.foo = foo + } + + fun bar(param: String) { + foo = param + this.foo = param + } +} + +open class Derived2 : Derived1("") + +fun testFunction(d: Derived1) { + d.foo = "other" +} + +// MODULE: m3(m2, m1) +// FILE: derived2.kt + +fun testDerivied1(d: Derived1) { + d.foo = "other" +} + +fun testDerivied2(d: Derived2) { + d.foo = "other" +} + +class Derivied3(foo: String) : Derived2() { + init { + this.foo = foo + } + + fun bar1(param: String) { + foo = param + this.foo = param + } +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index cc2e8b9bc74..b027acd2f8b 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -28332,6 +28332,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/scopes/initializerScopeOfExtensionProperty.kt"); } + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClass.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClass() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClass.kt"); + } + + @Test + @TestMetadata("invisibleInternalSetterAccessFromDeriviedClassOn.kt") + public void testInvisibleInternalSetterAccessFromDeriviedClassOn() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/invisibleInternalSetterAccessFromDeriviedClassOn.kt"); + } + @Test @TestMetadata("invisibleSetter.kt") public void testInvisibleSetter() throws Exception { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 60b6dda2868..2e9768ae29c 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -287,6 +287,7 @@ enum class LanguageFeature( ForbidInferringPostponedTypeVariableIntoDeclaredUpperBound(KOTLIN_2_0, kind = BUG_FIX), // KT-47986 ProhibitUseSiteGetTargetAnnotations(KOTLIN_2_0, kind = BUG_FIX), // KT-15470 KeepNullabilityWhenApproximatingLocalType(KOTLIN_2_0, kind = BUG_FIX), // KT-53982 + ProhibitAccessToInvisibleSetterFromDerivedClass(KOTLIN_2_0, kind = BUG_FIX), // KT-56662 // 2.1 diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java index 14ab093feb7..000ab0041e2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/OverridingUtil.java @@ -805,7 +805,7 @@ public class OverridingUtil { } @NotNull - private static Collection filterVisibleFakeOverrides( + public static Collection filterVisibleFakeOverrides( @NotNull final ClassDescriptor current, @NotNull Collection toFilter ) {