[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");
|
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
|
@Test
|
||||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||||
|
|||||||
+6
@@ -5684,6 +5684,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
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
|
@Test
|
||||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||||
|
|||||||
+6
@@ -5690,6 +5690,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/assignmentInLocalsInConstructor.kt");
|
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
|
@Test
|
||||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||||
|
|||||||
@@ -119,8 +119,7 @@ class FirSyntheticPropertiesScope private constructor(
|
|||||||
val parameter = setter.valueParameters.singleOrNull() ?: return
|
val parameter = setter.valueParameters.singleOrNull() ?: return
|
||||||
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
|
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
|
||||||
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
|
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
|
||||||
|
if (!setterTypeIsConsistentWithGetterType(propertyName, getterSymbol, setterSymbol, parameterType)) return
|
||||||
if (!setterTypeIsConsistentWithGetterType(getterSymbol, parameterType, baseScope)) return
|
|
||||||
matchingSetter = setterSymbol.fir
|
matchingSetter = setterSymbol.fir
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -161,21 +160,59 @@ class FirSyntheticPropertiesScope private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setterTypeIsConsistentWithGetterType(
|
private fun setterTypeIsConsistentWithGetterType(
|
||||||
|
propertyName: Name,
|
||||||
getterSymbol: FirNamedFunctionSymbol,
|
getterSymbol: FirNamedFunctionSymbol,
|
||||||
parameterType: ConeKotlinType,
|
setterSymbol: FirNamedFunctionSymbol,
|
||||||
scopeOfGetter: FirTypeScope
|
setterParameterType: ConeKotlinType
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val getterReturnType = getterSymbol.resolvedReturnTypeRef.type
|
val getterReturnType = getterSymbol.resolvedReturnTypeRef.type
|
||||||
if (AbstractTypeChecker.equalTypes(session.typeContext, getterReturnType, parameterType)) return true
|
if (AbstractTypeChecker.equalTypes(session.typeContext, getterReturnType, setterParameterType)) return true
|
||||||
if (!AbstractTypeChecker.isSubtypeOf(session.typeContext, getterReturnType, parameterType)) return false
|
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)
|
fun processOverrides(symbolToStart: FirNamedFunctionSymbol, setterSymbolToCompare: FirNamedFunctionSymbol?): Boolean {
|
||||||
if (foundGoodOverride) return true
|
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 {
|
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");
|
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
|
@Test
|
||||||
@TestMetadata("backingFieldInsideGetter_after.kt")
|
@TestMetadata("backingFieldInsideGetter_after.kt")
|
||||||
public void testBackingFieldInsideGetter_after() throws Exception {
|
public void testBackingFieldInsideGetter_after() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user