[FIR] Properly check is some setter function is applicable for synthetic property

^KT-56506 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-03-02 13:13:55 +02:00
committed by Space Team
parent ce39a02e0a
commit d5e6102ed9
7 changed files with 264 additions and 11 deletions
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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
}
@@ -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
}
@@ -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 {