[FIR] Properly check is some setter function is applicable for synthetic property
^KT-56506 Fixed
This commit is contained in:
committed by
Space Team
parent
ce39a02e0a
commit
d5e6102ed9
+6
@@ -5684,6 +5684,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("assignmentOfSyntheticVarWithInconsistentNullability.kt")
|
||||
public void testAssignmentOfSyntheticVarWithInconsistentNullability() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentOfSyntheticVarWithInconsistentNullability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||
|
||||
+6
@@ -5684,6 +5684,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("assignmentOfSyntheticVarWithInconsistentNullability.kt")
|
||||
public void testAssignmentOfSyntheticVarWithInconsistentNullability() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentOfSyntheticVarWithInconsistentNullability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||
|
||||
+6
@@ -5690,6 +5690,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("assignmentOfSyntheticVarWithInconsistentNullability.kt")
|
||||
public void testAssignmentOfSyntheticVarWithInconsistentNullability() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentOfSyntheticVarWithInconsistentNullability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||
|
||||
@@ -119,8 +119,7 @@ class FirSyntheticPropertiesScope private constructor(
|
||||
val parameter = setter.valueParameters.singleOrNull() ?: return
|
||||
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
|
||||
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
|
||||
|
||||
if (!setterTypeIsConsistentWithGetterType(getterSymbol, parameterType, baseScope)) return
|
||||
if (!setterTypeIsConsistentWithGetterType(propertyName, getterSymbol, setterSymbol, parameterType)) return
|
||||
matchingSetter = setterSymbol.fir
|
||||
})
|
||||
}
|
||||
@@ -161,21 +160,59 @@ class FirSyntheticPropertiesScope private constructor(
|
||||
}
|
||||
|
||||
private fun setterTypeIsConsistentWithGetterType(
|
||||
propertyName: Name,
|
||||
getterSymbol: FirNamedFunctionSymbol,
|
||||
parameterType: ConeKotlinType,
|
||||
scopeOfGetter: FirTypeScope
|
||||
setterSymbol: FirNamedFunctionSymbol,
|
||||
setterParameterType: ConeKotlinType
|
||||
): Boolean {
|
||||
val getterReturnType = getterSymbol.resolvedReturnTypeRef.type
|
||||
if (AbstractTypeChecker.equalTypes(session.typeContext, getterReturnType, parameterType)) return true
|
||||
if (!AbstractTypeChecker.isSubtypeOf(session.typeContext, getterReturnType, parameterType)) return false
|
||||
if (AbstractTypeChecker.equalTypes(session.typeContext, getterReturnType, setterParameterType)) return true
|
||||
if (!AbstractTypeChecker.isSubtypeOf(session.typeContext, getterReturnType, setterParameterType)) return false
|
||||
|
||||
/*
|
||||
* Here we search for some getter in overrides hierarchy which type is consistent with type of setter
|
||||
* If type of setter parameter is subtype of getter return type, we need to check corresponding "overridden" synthetic
|
||||
* properties from parent classes. If some of them has this setter or its overridden as base for setter, then current
|
||||
* setterSymbol can be used as setter for corresponding getterSymbol
|
||||
*
|
||||
* See corresponding code in FE 1.0 in `SyntheticJavaPropertyDescriptor.isGoodSetMethod`
|
||||
* Note that FE 1.0 looks through overrides just ones (by setter hierarchy), but FIR does twice (for setter and getter)
|
||||
* This is needed because FIR does not create fake overrides for all inherited methods of class, so there may be a
|
||||
* situation, when in inheritor class only getter is overridden, and setter does not have overriddens at all
|
||||
*
|
||||
* class Base {
|
||||
* public Object getX() {...}
|
||||
* public Object setX(Object x) {...} // setterSymbol
|
||||
* }
|
||||
*
|
||||
* class Derived extends Base {
|
||||
* public String getX() {...} // getterSymbol
|
||||
* // public fake-override Object setX(Object x) {...} // exist in FE 1.0 but not in FIR
|
||||
* }
|
||||
*/
|
||||
for ((overriddenGetter, scope) in scopeOfGetter.getDirectOverriddenFunctionsWithBaseScope(getterSymbol)) {
|
||||
val foundGoodOverride = setterTypeIsConsistentWithGetterType(overriddenGetter, parameterType, scope)
|
||||
if (foundGoodOverride) return true
|
||||
|
||||
fun processOverrides(symbolToStart: FirNamedFunctionSymbol, setterSymbolToCompare: FirNamedFunctionSymbol?): Boolean {
|
||||
var hasMatchingSetter = false
|
||||
baseScope.processDirectOverriddenFunctionsWithBaseScope(symbolToStart) l@{ symbol, scope ->
|
||||
if (hasMatchingSetter) return@l ProcessorAction.STOP
|
||||
val baseDispatchReceiverType = symbol.dispatchReceiverType ?: return@l ProcessorAction.NEXT
|
||||
val syntheticScope = FirSyntheticPropertiesScope(session, scope, baseDispatchReceiverType, syntheticNamesProvider)
|
||||
val baseProperties = syntheticScope.getProperties(propertyName)
|
||||
val propertyFound = baseProperties.any {
|
||||
val baseProperty = it.fir
|
||||
baseProperty is FirSyntheticProperty && baseProperty.setter?.delegate?.symbol == (setterSymbolToCompare ?: symbol)
|
||||
}
|
||||
if (propertyFound) {
|
||||
hasMatchingSetter = true
|
||||
ProcessorAction.STOP
|
||||
} else {
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
}
|
||||
return hasMatchingSetter
|
||||
}
|
||||
return false
|
||||
|
||||
return processOverrides(setterSymbol, setterSymbolToCompare = null)
|
||||
|| processOverrides(getterSymbol, setterSymbolToCompare = setterSymbol)
|
||||
}
|
||||
|
||||
private fun FirNamedFunctionSymbol.hasJavaOverridden(): Boolean {
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// ISSUE: KT-56506
|
||||
// FILE: JavaBase.java
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface JavaBase {
|
||||
@NotNull String getFoo();
|
||||
|
||||
// Important: no parameter nullability!
|
||||
void setFoo(String arg);
|
||||
}
|
||||
|
||||
// FILE: JavaOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JavaOverride implements JavaBase {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
@Override
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: NoOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class NoOverride {
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: KotlinBase.kt
|
||||
open class KotlinBase {
|
||||
val foo: String
|
||||
get() = ""
|
||||
}
|
||||
|
||||
// FILE: KotlinOverrideBase.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface KotlinOverrideBase extends KotlinBase {
|
||||
@Override
|
||||
@NotNull String getFoo();
|
||||
|
||||
void setFoo(String arg);
|
||||
}
|
||||
|
||||
// FILE: KotlinOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class KotlinOverride implements KotlinOverrideBase {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
@Override
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
fun test_1(d: NoOverride, s: String) {
|
||||
d.<!VAL_REASSIGNMENT!>foo<!> = s
|
||||
}
|
||||
|
||||
fun test_2(d: JavaBase, s: String) {
|
||||
d.foo = s
|
||||
}
|
||||
|
||||
fun test_3(d: JavaOverride, s: String) {
|
||||
d.foo = s
|
||||
}
|
||||
|
||||
fun test_4(d: KotlinOverrideBase, s: String) {
|
||||
d.<!VAL_REASSIGNMENT!>foo<!> = s
|
||||
}
|
||||
|
||||
fun test_5(d: KotlinOverride, s: String) {
|
||||
d.<!VAL_REASSIGNMENT!>foo<!> = s
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// ISSUE: KT-56506
|
||||
// FILE: JavaBase.java
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface JavaBase {
|
||||
@NotNull String getFoo();
|
||||
|
||||
// Important: no parameter nullability!
|
||||
void setFoo(String arg);
|
||||
}
|
||||
|
||||
// FILE: JavaOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JavaOverride implements JavaBase {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
@Override
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: NoOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class NoOverride {
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: KotlinBase.kt
|
||||
open class KotlinBase {
|
||||
val foo: String
|
||||
get() = ""
|
||||
}
|
||||
|
||||
// FILE: KotlinOverrideBase.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface KotlinOverrideBase extends KotlinBase {
|
||||
@Override
|
||||
@NotNull String getFoo();
|
||||
|
||||
void setFoo(String arg);
|
||||
}
|
||||
|
||||
// FILE: KotlinOverride.java
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class KotlinOverride implements KotlinOverrideBase {
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFoo() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Important: parameter is nullable!
|
||||
@Override
|
||||
public void setFoo(@Nullable String arg) {}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
fun test_1(d: NoOverride, s: String) {
|
||||
<!VAL_REASSIGNMENT!>d.foo<!> = s
|
||||
}
|
||||
|
||||
fun test_2(d: JavaBase, s: String) {
|
||||
d.foo = s
|
||||
}
|
||||
|
||||
fun test_3(d: JavaOverride, s: String) {
|
||||
d.foo = s
|
||||
}
|
||||
|
||||
fun test_4(d: KotlinOverrideBase, s: String) {
|
||||
<!VAL_REASSIGNMENT!>d.foo<!> = s
|
||||
}
|
||||
|
||||
fun test_5(d: KotlinOverride, s: String) {
|
||||
<!VAL_REASSIGNMENT!>d.foo<!> = s
|
||||
}
|
||||
Generated
+6
@@ -5690,6 +5690,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("assignmentOfSyntheticVarWithInconsistentNullability.kt")
|
||||
public void testAssignmentOfSyntheticVarWithInconsistentNullability() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentOfSyntheticVarWithInconsistentNullability.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user