From 59bafedd8a55507a6f19b2d832d327bbb00d3533 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 16 Nov 2022 17:14:56 +0100 Subject: [PATCH] K2 resolve: prefer derived class property to base class field #KT-50082 Fixed --- ...nosisCompilerFirTestdataTestGenerated.java | 34 ++++++++++ ...TouchedTilContractsPhaseTestGenerated.java | 33 +++++++++ .../problems/enumEntryFieldShadow.fir.txt | 28 ++++++++ .../resolve/problems/enumEntryFieldShadow.kt | 24 +++++++ .../fieldPropertyShadow.fir.txt | 30 +++++++++ .../propertyVsField/fieldPropertyShadow.kt | 28 ++++++++ .../propertyAndTwoFields.fir.txt | 14 ++++ .../propertyVsField/propertyAndTwoFields.kt | 23 +++++++ .../propertyFieldShadow.fir.txt | 20 ++++++ .../propertyVsField/propertyFieldShadow.kt | 20 ++++++ .../runners/FirDiagnosticTestGenerated.java | 34 ++++++++++ ...DiagnosticsWithLightTreeTestGenerated.java | 34 ++++++++++ .../JvmPlatformOverloadsConflictResolver.kt | 67 +++++++++++++++---- .../kotlin/fir/FirVisibilityChecker.kt | 30 +-------- .../kotlin/fir/resolve/SupertypeUtils.kt | 29 ++++++++ 15 files changed, 407 insertions(+), 41 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.fir.txt create mode 100644 compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java index d3055b055f0..a1b6899f097 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerFirTestdataTestGenerated.java @@ -3407,6 +3407,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); } + @Test + @TestMetadata("enumEntryFieldShadow.kt") + public void testEnumEntryFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { @@ -3568,6 +3574,34 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis } } + @Nested + @TestMetadata("compiler/fir/analysis-tests/testData/resolve/propertyVsField") + @TestDataPath("$PROJECT_ROOT") + public class PropertyVsField { + @Test + public void testAllFilesPresentInPropertyVsField() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/propertyVsField"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("fieldPropertyShadow.kt") + public void testFieldPropertyShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt"); + } + + @Test + @TestMetadata("propertyAndTwoFields.kt") + public void testPropertyAndTwoFields() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt"); + } + + @Test + @TestMetadata("propertyFieldShadow.kt") + public void testPropertyFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt"); + } + } + @Nested @TestMetadata("compiler/fir/analysis-tests/testData/resolve/references") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java index d86da1817f6..6886fa8e4d1 100644 --- a/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java +++ b/compiler/fir/analysis-tests/legacy-fir-tests/tests-gen/org/jetbrains/kotlin/fir/LazyBodyIsNotTouchedTilContractsPhaseTestGenerated.java @@ -3005,6 +3005,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); } + @TestMetadata("enumEntryFieldShadow.kt") + public void testEnumEntryFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt"); + } + @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/problems/expectConstructor.kt"); @@ -3144,6 +3149,34 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract } } + @TestMetadata("compiler/fir/analysis-tests/testData/resolve/propertyVsField") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PropertyVsField extends AbstractLazyBodyIsNotTouchedTilContractsPhaseTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInPropertyVsField() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/propertyVsField"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @TestMetadata("fieldPropertyShadow.kt") + public void testFieldPropertyShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt"); + } + + @TestMetadata("propertyAndTwoFields.kt") + public void testPropertyAndTwoFields() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt"); + } + + @TestMetadata("propertyFieldShadow.kt") + public void testPropertyFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt"); + } + } + @TestMetadata("compiler/fir/analysis-tests/testData/resolve/references") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.fir.txt b/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.fir.txt new file mode 100644 index 00000000000..ca88f0af32a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.fir.txt @@ -0,0 +1,28 @@ +FILE: DerivedEnum.kt + public final enum class DerivedEnum : R|BaseJava|, R|kotlin/Enum| { + private constructor(): R|DerivedEnum| { + super|>() + } + + public final static enum entry x: R|DerivedEnum| + public final fun foo(): R|kotlin/Unit| { + R|/bar|(R|/DerivedEnum.x|) + R|/baz|(#) + R|/baz|(Q|BaseJava|.R|/BaseJava.y|) + } + + public final static fun values(): R|kotlin/Array| { + } + + public final static fun valueOf(value: R|kotlin/String|): R|DerivedEnum| { + } + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + } + public final fun bar(e: R|DerivedEnum|): R|kotlin/Unit| { + } + public final fun baz(s: R|kotlin/String|): R|kotlin/Unit| { + Q|DerivedEnum|.R|/DerivedEnum.x| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt b/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt new file mode 100644 index 00000000000..0a08a93773b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt @@ -0,0 +1,24 @@ +// FILE: BaseJava.java + +public interface BaseJava { + String x = ""; + String y = ""; +} + +// FILE: DerivedEnum.kt + +enum class DerivedEnum : BaseJava { + x; + + fun foo() { + bar(x) + baz(y) + baz(BaseJava.y) + } +} + +fun bar(e: DerivedEnum) {} + +fun baz(s: String) { + DerivedEnum.x +} diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.fir.txt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.fir.txt new file mode 100644 index 00000000000..27bf18dbb48 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.fir.txt @@ -0,0 +1,30 @@ +FILE: KotlinBase.kt + public open class KotlinBase : R|kotlin/Any| { + public constructor(): R|KotlinBase| { + super() + } + + public final val abcd: R|kotlin/String| = String(abcd) + public get(): R|kotlin/String| + + } +FILE: KotlinProxy.kt + public abstract interface KotlinProxy : R|kotlin/Any| { + public open val zyxw: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(zyxw) + } + + } +FILE: test.kt + public final class Derived : R|Test|, R|KotlinProxy| { + public constructor(): R|Derived| { + super() + } + + public final fun test(): R|kotlin/Unit| { + this@R|/Derived|.R|/Test.abcd| + this@R|/Derived|.R|/Test.zyxw| + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt new file mode 100644 index 00000000000..d74a1f8ba07 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt @@ -0,0 +1,28 @@ +// FILE: Test.java + +public class Test extends KotlinBase { + public final String abcd = "ABCD"; + + public final String zyxw = "ZYXW"; +} + +// FILE: KotlinBase.kt + +open class KotlinBase { + val abcd = "abcd" +} + +// FILE: KotlinProxy.kt + +interface KotlinProxy { + val zyxw get() = "zyxw" +} + +// FILE: test.kt + +class Derived : Test(), KotlinProxy { + fun test() { + abcd // field! + zyxw // field! + } +} diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.fir.txt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.fir.txt new file mode 100644 index 00000000000..52b01265a73 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.fir.txt @@ -0,0 +1,14 @@ +FILE: KotlinBase.kt + public open class KotlinBase : R|kotlin/Any| { + public constructor(): R|KotlinBase| { + super() + } + + public final val abcd: R|kotlin/String| = String(abcd) + public get(): R|kotlin/String| + + } +FILE: test.kt + public final fun test(d: R|JavaDerived|): R|kotlin/Unit| { + R|/d|.R|/JavaDerived.abcd| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt new file mode 100644 index 00000000000..76b606329ca --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt @@ -0,0 +1,23 @@ +// FILE: Test.java + +public class Test extends KotlinBase { + public final String abcd = "ABCD"; +} + +// FILE: KotlinBase.kt + +open class KotlinBase { + val abcd = "abcd" +} + +// FILE: JavaDerived.java + +public class JavaDerived extends Test { + public final String abcd = "AaBbCcDd" +} + +// FILE: test.kt + +fun test(d: JavaDerived) { + d.abcd // JavaDerived +} diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.fir.txt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.fir.txt new file mode 100644 index 00000000000..2ceb7509bcb --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.fir.txt @@ -0,0 +1,20 @@ +FILE: test.kt + public final class Test2 : R|Test| { + public constructor(): R|Test2| { + super() + } + + private final val text: R|kotlin/String| = String(BCDE) + private get(): R|kotlin/String| + + private final val publicPrivateText: R|kotlin/String| = String(YXWV) + private get(): R|kotlin/String| + + public final fun check(): R|kotlin/String| { + ^check this@R|/Test2|.R|/Test2.text| + } + + } + public final fun check(): R|kotlin/String!| { + ^check R|/Test2.Test2|().R|/Test.publicPrivateText| + } diff --git a/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt new file mode 100644 index 00000000000..505cbf65e40 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt @@ -0,0 +1,20 @@ +// FILE: Test.java + +public class Test { + protected final String text = "ABCD"; + + public final String publicPrivateText = "ZYXW"; +} + +// FILE: test.kt + +class Test2 : Test() { + private val text = "BCDE" + + private val publicPrivateText = "YXWV" + + fun check() = text // Should be resolved to Test2.text, not to Test.text +} + +fun check() = Test2().publicPrivateText // Should be resolved to Test.publicPrivateText (Test2 member is private) + diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index f0d886c8ca9..c04107b8996 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -3407,6 +3407,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); } + @Test + @TestMetadata("enumEntryFieldShadow.kt") + public void testEnumEntryFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { @@ -3568,6 +3574,34 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { } } + @Nested + @TestMetadata("compiler/fir/analysis-tests/testData/resolve/propertyVsField") + @TestDataPath("$PROJECT_ROOT") + public class PropertyVsField { + @Test + public void testAllFilesPresentInPropertyVsField() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/propertyVsField"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("fieldPropertyShadow.kt") + public void testFieldPropertyShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt"); + } + + @Test + @TestMetadata("propertyAndTwoFields.kt") + public void testPropertyAndTwoFields() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt"); + } + + @Test + @TestMetadata("propertyFieldShadow.kt") + public void testPropertyFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt"); + } + } + @Nested @TestMetadata("compiler/fir/analysis-tests/testData/resolve/references") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 269666163f1..334d7685fdd 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -3407,6 +3407,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/problems/emptySelectorInQualifiedExpression.kt"); } + @Test + @TestMetadata("enumEntryFieldShadow.kt") + public void testEnumEntryFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/enumEntryFieldShadow.kt"); + } + @Test @TestMetadata("expectConstructor.kt") public void testExpectConstructor() throws Exception { @@ -3568,6 +3574,34 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos } } + @Nested + @TestMetadata("compiler/fir/analysis-tests/testData/resolve/propertyVsField") + @TestDataPath("$PROJECT_ROOT") + public class PropertyVsField { + @Test + public void testAllFilesPresentInPropertyVsField() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/propertyVsField"), Pattern.compile("^([^.]+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("fieldPropertyShadow.kt") + public void testFieldPropertyShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/fieldPropertyShadow.kt"); + } + + @Test + @TestMetadata("propertyAndTwoFields.kt") + public void testPropertyAndTwoFields() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyAndTwoFields.kt"); + } + + @Test + @TestMetadata("propertyFieldShadow.kt") + public void testPropertyFieldShadow() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/propertyVsField/propertyFieldShadow.kt"); + } + } + @Nested @TestMetadata("compiler/fir/analysis-tests/testData/resolve/references") @TestDataPath("$PROJECT_ROOT") 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 9d24bcb50a3..9ede9297134 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 @@ -10,11 +10,12 @@ import org.jetbrains.kotlin.fir.containingClassLookupTag import org.jetbrains.kotlin.fir.declarations.FirField import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.languageVersionSettings -import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents +import org.jetbrains.kotlin.fir.resolve.* import org.jetbrains.kotlin.fir.resolve.calls.AbstractConeCallConflictResolver import org.jetbrains.kotlin.fir.resolve.calls.Candidate import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents -import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer +import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag +import org.jetbrains.kotlin.fir.unwrapFakeOverrides import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator class JvmPlatformOverloadsConflictResolver( @@ -31,21 +32,59 @@ class JvmPlatformOverloadsConflictResolver( return candidates } val result = mutableSetOf() - outerLoop@ for (myCandidate in candidates) { - val me = myCandidate.symbol.fir - if (me is FirProperty && me.symbol.containingClassLookupTag() != null) { - for (otherCandidate in candidates) { - val other = otherCandidate.symbol.fir - if (other is FirField && other.symbol.containingClassLookupTag() != null) { - // NB: FE 1.0 does class equivalence check here - // However, in FIR container classes aren't the same for our samples (see fieldPropertyOverloads.kt) - // E.g. we can have SomeConcreteJavaEnum for field and kotlin.Enum for static property 'name' - continue@outerLoop - } + for (myCandidate in candidates) { + when (val me = myCandidate.symbol.fir) { + is FirProperty -> if (!me.isShadowedByFieldCandidate(candidates)) { + result += myCandidate } + is FirField -> if (!me.isShadowedByPropertyCandidate(candidates)) { + result += myCandidate + } + else -> result += myCandidate } - result += myCandidate } return result } + + private fun FirProperty.isShadowedByFieldCandidate(candidates: Set): Boolean { + val propertyContainingClassLookupTag = unwrapFakeOverrides().symbol.containingClassLookupTag() ?: return false + for (otherCandidate in candidates) { + val field = otherCandidate.symbol.fir as? FirField ?: continue + val fieldContainingClassLookupTag = field.unwrapFakeOverrides().symbol.containingClassLookupTag() + if (fieldContainingClassLookupTag != null && + !propertyContainingClassLookupTag.strictlyDerivedFrom(fieldContainingClassLookupTag) + ) { + // NB: FE 1.0 does class equivalence check here ^^^ + // However, in FIR container classes aren't the same for our samples (see fieldPropertyOverloads.kt) + // E.g. we can have SomeConcreteJavaEnum for field and kotlin.Enum for static property 'name' + return true + } + } + return false + } + + private fun FirField.isShadowedByPropertyCandidate(candidates: Set): Boolean { + 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() + if (propertyContainingClassLookupTag != null && + propertyContainingClassLookupTag.strictlyDerivedFrom(fieldContainingClassLookupTag) + ) { + // NB: FE 1.0 does class equivalence check here ^^^ + // However, in FIR container classes aren't the same for our samples (see fieldPropertyOverloads.kt) + // E.g. we can have SomeConcreteJavaEnum for field and kotlin.Enum for static property 'name' + return true + } + } + return false + } + + private fun ConeClassLikeLookupTag.strictlyDerivedFrom(other: ConeClassLikeLookupTag): Boolean { + if (this == other) return false + val session = inferenceComponents.session + val thisClass = this.toFirRegularClassSymbol(session)?.fir ?: return false + + return thisClass.isSubclassOf(other, session, isStrict = true) + } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt index 3d18ba03270..2d000fc9c55 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt @@ -194,7 +194,7 @@ abstract class FirVisibilityChecker : FirSessionComponent { if (isStatic && containingClass != null) { containingUseSiteDeclarations.firstNotNullOfOrNull { if (it !is FirClass) return@firstNotNullOfOrNull null - it.takeIf { it.isSubClass(containingLookupTag, session, supertypeSupplier) } + it.takeIf { it.isSubclassOf(containingLookupTag, session, isStrict = false, supertypeSupplier) } }?.let { return it } } @@ -380,10 +380,6 @@ abstract class FirVisibilityChecker : FirSessionComponent { return false } - // 'local' isn't taken into account here - private fun ClassId.isSame(other: ClassId): Boolean = - packageFqName == other.packageFqName && relativeClassName == other.relativeClassName - private fun ConeClassLikeLookupTag.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? { if (classId.isLocal) return null val outerClassId = classId.outerClassId ?: return null @@ -405,11 +401,11 @@ abstract class FirVisibilityChecker : FirSessionComponent { supertypeSupplier: SupertypeSupplier ): Boolean { dispatchReceiver?.ownerIfCompanion(session)?.let { companionOwnerLookupTag -> - if (containingUseSiteClass.isSubClass(companionOwnerLookupTag, session, supertypeSupplier)) return true + if (containingUseSiteClass.isSubclassOf(companionOwnerLookupTag, session, isStrict = false, supertypeSupplier)) return true } return when { - !containingUseSiteClass.isSubClass(ownerLookupTag, session, supertypeSupplier) -> false + !containingUseSiteClass.isSubclassOf(ownerLookupTag, session, isStrict = false, supertypeSupplier) -> false isVariableOrNamedFunction -> doesReceiverFitForProtectedVisibility( dispatchReceiver, containingUseSiteClass, @@ -460,26 +456,6 @@ abstract class FirVisibilityChecker : FirSessionComponent { return false } - private fun FirClass.isSubClass( - ownerLookupTag: ConeClassLikeLookupTag, - session: FirSession, - supertypeSupplier: SupertypeSupplier - ): Boolean { - if (classId.isSame(ownerLookupTag.classId)) return true - - return lookupSuperTypes( - this, - lookupInterfaces = true, - deep = true, - session, - substituteTypes = false, - supertypeSupplier - ).any { superType -> - // Note: We check just classId here, so type substitution isn't needed ^ (we aren't interested in type arguments) - (superType as? ConeClassLikeType)?.fullyExpandedType(session)?.lookupTag?.classId?.isSame(ownerLookupTag.classId) == true - } - } - private fun ReceiverValue?.ownerIfCompanion(session: FirSession): ConeClassLikeLookupTag? = (this?.type as? ConeClassLikeType)?.lookupTag?.ownerIfCompanion(session) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt index 59842a87c84..4ce3da02d90 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.classId import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType import org.jetbrains.kotlin.fir.declarations.utils.isLocal import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes @@ -21,6 +22,7 @@ import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.model.CaptureStatus import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartSet @@ -90,6 +92,33 @@ fun lookupSuperTypes( } } +fun FirClass.isSubclassOf( + ownerLookupTag: ConeClassLikeLookupTag, + session: FirSession, + isStrict: Boolean, + supertypeSupplier: SupertypeSupplier = SupertypeSupplier.Default +): Boolean { + if (classId.isSame(ownerLookupTag.classId)) { + return !isStrict + } + + return lookupSuperTypes( + this, + lookupInterfaces = true, + deep = true, + session, + substituteTypes = false, + supertypeSupplier + ).any { superType -> + // Note: We check just classId here, so type substitution isn't needed + superType.lookupTag.classId.isSame(ownerLookupTag.classId) + } +} + +// 'local' isn't taken into account here +fun ClassId.isSame(other: ClassId): Boolean = + packageFqName == other.packageFqName && relativeClassName == other.relativeClassName + fun FirClass.isThereLoopInSupertypes(session: FirSession): Boolean { val visitedSymbols: MutableSet> = SmartSet.create() val inProcess: MutableSet> = mutableSetOf()