fix: val can be reassigned through backing field inside its own getter
This commit is contained in:
@@ -270,12 +270,24 @@ public class JetFlowInformationProvider {
|
|||||||
@NotNull JetExpression expression,
|
@NotNull JetExpression expression,
|
||||||
@NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
|
@NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated
|
||||||
) {
|
) {
|
||||||
|
VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
|
||||||
|
if (isBackingFieldReference(expression) && variableDescriptor instanceof PropertyDescriptor) {
|
||||||
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor;
|
||||||
|
JetPropertyAccessor accessor = PsiTreeUtil.getParentOfType(expression, JetPropertyAccessor.class);
|
||||||
|
if (accessor != null) {
|
||||||
|
DeclarationDescriptor accessorDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, accessor);
|
||||||
|
if (propertyDescriptor.getGetter() == accessorDescriptor) {
|
||||||
|
//val can be reassigned through backing field inside its own getter
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
|
boolean isInitializedNotHere = ctxt.enterInitState.isInitialized;
|
||||||
if (expression.getParent() instanceof JetProperty && ((JetProperty)expression).getInitializer() != null) {
|
if (expression.getParent() instanceof JetProperty && ((JetProperty)expression).getInitializer() != null) {
|
||||||
isInitializedNotHere = false;
|
isInitializedNotHere = false;
|
||||||
}
|
}
|
||||||
boolean hasBackingField = true;
|
boolean hasBackingField = true;
|
||||||
VariableDescriptor variableDescriptor = ctxt.variableDescriptor;
|
|
||||||
if (variableDescriptor instanceof PropertyDescriptor) {
|
if (variableDescriptor instanceof PropertyDescriptor) {
|
||||||
hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
|
hasBackingField = trace.get(BindingContext.BACKING_FIELD_REQUIRED, (PropertyDescriptor) variableDescriptor);
|
||||||
}
|
}
|
||||||
@@ -366,8 +378,8 @@ public class JetFlowInformationProvider {
|
|||||||
private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
|
private boolean checkBackingField(@NotNull VariableContext cxtx, @NotNull JetElement element) {
|
||||||
VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
|
VariableDescriptor variableDescriptor = cxtx.variableDescriptor;
|
||||||
boolean[] error = new boolean[1];
|
boolean[] error = new boolean[1];
|
||||||
if (isBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked
|
if (isCorrectBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked
|
||||||
if (!isBackingFieldReference(element, cxtx, error, true)) return false;
|
if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false;
|
||||||
if (error[0]) return true;
|
if (error[0]) return true;
|
||||||
if (!(variableDescriptor instanceof PropertyDescriptor)) {
|
if (!(variableDescriptor instanceof PropertyDescriptor)) {
|
||||||
report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
|
report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx);
|
||||||
@@ -398,12 +410,13 @@ public class JetFlowInformationProvider {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isBackingFieldReference(@Nullable JetElement element, VariableContext ctxt, boolean[] error, boolean reportError) {
|
private boolean isCorrectBackingFieldReference(@Nullable JetElement element, VariableContext ctxt, boolean[] error, boolean reportError) {
|
||||||
error[0] = false;
|
error[0] = false;
|
||||||
if (element instanceof JetSimpleNameExpression && ((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) {
|
if (isBackingFieldReference(element)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (element instanceof JetDotQualifiedExpression && isBackingFieldReference(((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
|
if (element instanceof JetDotQualifiedExpression && isCorrectBackingFieldReference(
|
||||||
|
((JetDotQualifiedExpression) element).getSelectorExpression(), ctxt, error, false)) {
|
||||||
if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
|
if (((JetDotQualifiedExpression) element).getReceiverExpression() instanceof JetThisExpression) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -415,6 +428,10 @@ public class JetFlowInformationProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBackingFieldReference(@Nullable JetElement element) {
|
||||||
|
return element instanceof JetSimpleNameExpression && ((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER;
|
||||||
|
}
|
||||||
|
|
||||||
private void recordInitializedVariables(@NotNull Pseudocode pseudocode, @NotNull Map<Instruction, Edges<Map<VariableDescriptor,PseudocodeVariablesData.VariableInitState>>> initializersMap) {
|
private void recordInitializedVariables(@NotNull Pseudocode pseudocode, @NotNull Map<Instruction, Edges<Map<VariableDescriptor,PseudocodeVariablesData.VariableInitState>>> initializersMap) {
|
||||||
Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
|
Edges<Map<VariableDescriptor, VariableInitState>> initializers = initializersMap.get(pseudocode.getExitInstruction());
|
||||||
Set<VariableDescriptor> usedVariables = pseudocodeVariablesData.getUsedVariables(pseudocode);
|
Set<VariableDescriptor> usedVariables = pseudocodeVariablesData.getUsedVariables(pseudocode);
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
import java.util.HashSet
|
||||||
|
|
||||||
|
val a: MutableSet<String>? = null
|
||||||
|
get() {
|
||||||
|
if (a == null) {
|
||||||
|
$a = HashSet()
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
class R {
|
||||||
|
val b: String? = null
|
||||||
|
get() {
|
||||||
|
if (b == null) {
|
||||||
|
$b = "b"
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -824,6 +824,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlFlowAnalysis"), "kt", true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/controlFlowAnalysis"), "kt", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("backingFieldInsideGetter.kt")
|
||||||
|
public void testBackingFieldInsideGetter() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("checkInnerLocalDeclarations.kt")
|
@TestMetadata("checkInnerLocalDeclarations.kt")
|
||||||
public void testCheckInnerLocalDeclarations() throws Exception {
|
public void testCheckInnerLocalDeclarations() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/checkInnerLocalDeclarations.kt");
|
doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/checkInnerLocalDeclarations.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user