FE: add & fix test with intersection property shadowed by base field
This commit is contained in:
committed by
teamcity
parent
949a39b80f
commit
7904f23660
+6
@@ -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 {
|
||||
|
||||
Vendored
+1
-1
@@ -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|<local>/d|.R|/C.x|
|
||||
lval a: R|kotlin/Int| = R|<local>/d|.R|/A.x|
|
||||
lval b: R|kotlin/Int| = R|<local>/d|.R|/D.y|
|
||||
}
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+3
-2
@@ -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<Candidate>): 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)
|
||||
) {
|
||||
|
||||
@@ -93,6 +93,15 @@ inline fun <reified D : FirCallableDeclaration> D.unwrapFakeOverrides(): D {
|
||||
} while (true)
|
||||
}
|
||||
|
||||
inline fun <reified D : FirCallableDeclaration> D.unwrapSubstitutionOverrides(): D {
|
||||
var current = this
|
||||
|
||||
do {
|
||||
val next = current.originalForSubstitutionOverride ?: return current
|
||||
current = next
|
||||
} while (true)
|
||||
}
|
||||
|
||||
inline fun <reified S : FirCallableSymbol<*>> S.unwrapFakeOverrides(): S = fir.unwrapFakeOverrides().symbol as S
|
||||
|
||||
private object SubstitutedOverrideOriginalKey : FirDeclarationDataKey()
|
||||
|
||||
+17
-13
@@ -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) }
|
||||
}
|
||||
}
|
||||
Vendored
+23
@@ -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
|
||||
}
|
||||
}
|
||||
Vendored
+27
@@ -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<R|Base|>()
|
||||
}
|
||||
|
||||
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<R|Intermediate|>()
|
||||
}
|
||||
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
this@R|/Derived|.R|/Proxy.x|
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -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() {
|
||||
<!BASE_CLASS_FIELD_SHADOWS_DERIVED_CLASS_PROPERTY!>x<!>
|
||||
}
|
||||
}
|
||||
+36
@@ -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
|
||||
}
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
@@ -468,6 +468,20 @@ public class DescriptorUtils {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <D extends CallableMemberDescriptor> D unwrapSubstitutionOverride(@NotNull D descriptor) {
|
||||
while (descriptor.getKind() == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
|
||||
Collection<? extends CallableMemberDescriptor> 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 extends DeclarationDescriptorWithVisibility> D unwrapFakeOverrideToAnyDeclaration(@NotNull D descriptor) {
|
||||
|
||||
Reference in New Issue
Block a user