diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 668a3d2b0c4..eb7c9bef79e 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -225,6 +225,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/derivedClassPropertyShadowsBaseClassField13.kt"); } + @Test + @TestMetadata("derivedIntersectionPropertyShadowsBaseClassField.kt") + public void testDerivedIntersectionPropertyShadowsBaseClassField() throws Exception { + runTest("compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt"); + } + @Test @TestMetadata("DiamondFunction.kt") public void testDiamondFunction() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolve/scopes/explicitOverrideOfTwoMembers_java.fir.txt b/compiler/fir/analysis-tests/testData/resolve/scopes/explicitOverrideOfTwoMembers_java.fir.txt index 90f085fa513..c66041e9726 100644 --- a/compiler/fir/analysis-tests/testData/resolve/scopes/explicitOverrideOfTwoMembers_java.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/scopes/explicitOverrideOfTwoMembers_java.fir.txt @@ -21,6 +21,6 @@ FILE: lib.kt } FILE: main.kt public final fun test(d: R|D|): R|kotlin/Unit| { - lval a: R|kotlin/Int| = R|/d|.R|/C.x| + lval a: R|kotlin/Int| = R|/d|.R|/A.x| lval b: R|kotlin/Int| = R|/d|.R|/D.y| } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 0281fbfe955..5cf2b3bba1a 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -225,6 +225,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/derivedClassPropertyShadowsBaseClassField13.kt"); } + @Test + @TestMetadata("derivedIntersectionPropertyShadowsBaseClassField.kt") + public void testDerivedIntersectionPropertyShadowsBaseClassField() throws Exception { + runTest("compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt"); + } + @Test @TestMetadata("DiamondFunction.kt") public void testDiamondFunction() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index c334a605eea..a5a05957da9 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -225,6 +225,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/derivedClassPropertyShadowsBaseClassField13.kt"); } + @Test + @TestMetadata("derivedIntersectionPropertyShadowsBaseClassField.kt") + public void testDerivedIntersectionPropertyShadowsBaseClassField() throws Exception { + runTest("compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt"); + } + @Test @TestMetadata("DiamondFunction.kt") public void testDiamondFunction() throws Exception { diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmPlatformOverloadsConflictResolver.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmPlatformOverloadsConflictResolver.kt index 9ede9297134..c03bef2ab22 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmPlatformOverloadsConflictResolver.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/calls/jvm/JvmPlatformOverloadsConflictResolver.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.unwrapFakeOverrides +import org.jetbrains.kotlin.fir.unwrapSubstitutionOverrides import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator class JvmPlatformOverloadsConflictResolver( @@ -47,7 +48,7 @@ class JvmPlatformOverloadsConflictResolver( } private fun FirProperty.isShadowedByFieldCandidate(candidates: Set): Boolean { - val propertyContainingClassLookupTag = unwrapFakeOverrides().symbol.containingClassLookupTag() ?: return false + val propertyContainingClassLookupTag = unwrapSubstitutionOverrides().symbol.containingClassLookupTag() ?: return false for (otherCandidate in candidates) { val field = otherCandidate.symbol.fir as? FirField ?: continue val fieldContainingClassLookupTag = field.unwrapFakeOverrides().symbol.containingClassLookupTag() @@ -67,7 +68,7 @@ class JvmPlatformOverloadsConflictResolver( val fieldContainingClassLookupTag = unwrapFakeOverrides().symbol.containingClassLookupTag() ?: return false for (otherCandidate in candidates) { val property = otherCandidate.symbol.fir as? FirProperty ?: continue - val propertyContainingClassLookupTag = property.unwrapFakeOverrides().symbol.containingClassLookupTag() + val propertyContainingClassLookupTag = property.unwrapSubstitutionOverrides().symbol.containingClassLookupTag() if (propertyContainingClassLookupTag != null && propertyContainingClassLookupTag.strictlyDerivedFrom(fieldContainingClassLookupTag) ) { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/ClassMembers.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/ClassMembers.kt index 112a3d12a91..55bd4de4b9d 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/ClassMembers.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/ClassMembers.kt @@ -93,6 +93,15 @@ inline fun D.unwrapFakeOverrides(): D { } while (true) } +inline fun D.unwrapSubstitutionOverrides(): D { + var current = this + + do { + val next = current.originalForSubstitutionOverride ?: return current + current = next + } while (true) +} + inline fun > S.unwrapFakeOverrides(): S = fir.unwrapFakeOverrides().symbol as S private object SubstitutedOverrideOriginalKey : FirDeclarationDataKey() diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmPropertyVsFieldAmbiguityCallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmPropertyVsFieldAmbiguityCallChecker.kt index 781e03f0a2e..253933205a5 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmPropertyVsFieldAmbiguityCallChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmPropertyVsFieldAmbiguityCallChecker.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.resolve.jvm.checkers import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality @@ -36,14 +37,14 @@ object JvmPropertyVsFieldAmbiguityCallChecker : CallChecker { resultingDescriptor.name, NoLookupLocation.FOR_ALREADY_TRACKED ).forEach { alternativePropertyDescriptor -> if (alternativePropertyDescriptor !== resultingDescriptor) { - val hasLateInit = alternativePropertyDescriptor.isLateInit - if (!hasLateInit && - alternativePropertyDescriptor.getter?.isDefault != false && - alternativePropertyDescriptor.setter?.isDefault != false && - alternativePropertyDescriptor.modality == Modality.FINAL + val basePropertyDescriptor = DescriptorUtils.unwrapSubstitutionOverride(alternativePropertyDescriptor) + val propertyClassDescriptor = basePropertyDescriptor.containingDeclaration as? ClassDescriptor + val hasLateInit = basePropertyDescriptor.selfOrBaseForFakeOverride { it.isLateInit } + val hasCustomGetter = basePropertyDescriptor.selfOrBaseForFakeOverride { it.getter?.isDefault == false } + val hasCustomSetter = basePropertyDescriptor.selfOrBaseForFakeOverride { it.setter?.isDefault == false } + if (!hasLateInit && !hasCustomGetter && !hasCustomSetter && + basePropertyDescriptor.modality == Modality.FINAL ) return@forEach - val propertyClassDescriptor = - DescriptorUtils.unwrapFakeOverride(alternativePropertyDescriptor).containingDeclaration as? ClassDescriptor if (fieldClassDescriptor != null && propertyClassDescriptor != null && DescriptorUtils.isSubclass(fieldClassDescriptor, propertyClassDescriptor) ) return@forEach @@ -55,12 +56,9 @@ object JvmPropertyVsFieldAmbiguityCallChecker : CallChecker { ) ) { val factory = when { - alternativePropertyDescriptor.getter?.isDefault == false -> - ErrorsJvm.BASE_CLASS_FIELD_SHADOWS_DERIVED_CLASS_PROPERTY - hasLateInit || alternativePropertyDescriptor.setter?.isDefault == false -> - ErrorsJvm.BACKING_FIELD_ACCESSED_DUE_TO_PROPERTY_FIELD_CONFLICT - else -> - ErrorsJvm.BASE_CLASS_FIELD_MAY_SHADOW_DERIVED_CLASS_PROPERTY + hasCustomGetter -> ErrorsJvm.BASE_CLASS_FIELD_SHADOWS_DERIVED_CLASS_PROPERTY + hasLateInit || hasCustomSetter -> ErrorsJvm.BACKING_FIELD_ACCESSED_DUE_TO_PROPERTY_FIELD_CONFLICT + else -> ErrorsJvm.BASE_CLASS_FIELD_MAY_SHADOW_DERIVED_CLASS_PROPERTY } context.trace.report( factory.on( @@ -74,4 +72,10 @@ object JvmPropertyVsFieldAmbiguityCallChecker : CallChecker { } } } + + private fun PropertyDescriptor.selfOrBaseForFakeOverride(predicate: (PropertyDescriptor) -> Boolean): Boolean { + if (predicate(this)) return true + if (kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) return false + return overriddenDescriptors.any { it.selfOrBaseForFakeOverride(predicate) } + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.kt b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.kt new file mode 100644 index 00000000000..8672bc239a9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.kt @@ -0,0 +1,23 @@ +// WITH_STDLIB +// FIR_DUMP +// FILE: Base.java + +public class Base { + public String x = ""; +} + +// FILE: test.kt + +interface Proxy { + val x: String +} + +open class Intermediate : Base() { + val x get() = " " +} + +class Derived : Proxy, Intermediate() { + fun test() { + x + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.txt b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.txt new file mode 100644 index 00000000000..77a478efda4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.fir.txt @@ -0,0 +1,27 @@ +FILE: test.kt + public abstract interface Proxy : R|kotlin/Any| { + public abstract val x: R|kotlin/String| + public get(): R|kotlin/String| + + } + public open class Intermediate : R|Base| { + public constructor(): R|Intermediate| { + super() + } + + public final val x: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String( ) + } + + } + public final class Derived : R|Proxy|, R|Intermediate| { + public constructor(): R|Derived| { + super() + } + + public final fun test(): R|kotlin/Unit| { + this@R|/Derived|.R|/Proxy.x| + } + + } diff --git a/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt new file mode 100644 index 00000000000..a7054e7502c --- /dev/null +++ b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt @@ -0,0 +1,23 @@ +// WITH_STDLIB +// FIR_DUMP +// FILE: Base.java + +public class Base { + public String x = ""; +} + +// FILE: test.kt + +interface Proxy { + val x: String +} + +open class Intermediate : Base() { + val x get() = " " +} + +class Derived : Proxy, Intermediate() { + fun test() { + x + } +} diff --git a/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.txt b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.txt new file mode 100644 index 00000000000..18ab07a90d2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.txt @@ -0,0 +1,36 @@ +package + +public open class Base { + public constructor Base() + public final var x: kotlin.String! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Derived : Proxy, Intermediate { + public constructor Derived() + public final override /*2*/ /*fake_override*/ val x: kotlin.String + public final override /*1*/ /*fake_override*/ var x: kotlin.String! + public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test(): kotlin.Unit + public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class Intermediate : Base { + public constructor Intermediate() + public final val x: kotlin.String + public final override /*1*/ /*fake_override*/ var x: kotlin.String! + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface Proxy { + public abstract val x: kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + 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 877950e5519..5400a975e0d 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 @@ -225,6 +225,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/derivedClassPropertyShadowsBaseClassField13.kt"); } + @Test + @TestMetadata("derivedIntersectionPropertyShadowsBaseClassField.kt") + public void testDerivedIntersectionPropertyShadowsBaseClassField() throws Exception { + runTest("compiler/testData/diagnostics/tests/derivedIntersectionPropertyShadowsBaseClassField.kt"); + } + @Test @TestMetadata("DiamondFunction.kt") public void testDiamondFunction() throws Exception { diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 0e8700da60e..a7b25398cd2 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -468,6 +468,20 @@ public class DescriptorUtils { return descriptor; } + @NotNull + @SuppressWarnings("unchecked") + public static D unwrapSubstitutionOverride(@NotNull D descriptor) { + while (descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + Collection overridden = descriptor.getOverriddenDescriptors(); + if (overridden.isEmpty()) { + throw new IllegalStateException("Fake override should have at least one overridden descriptor: " + descriptor); + } + if (overridden.size() > 1) return descriptor; + descriptor = (D) overridden.iterator().next(); + } + return descriptor; + } + @NotNull @SuppressWarnings("unchecked") public static D unwrapFakeOverrideToAnyDeclaration(@NotNull D descriptor) {