From 67d0d8b1a9d303b7c34bc56668b9139f6f5bda9e Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Tue, 18 Dec 2012 16:38:38 +0400 Subject: [PATCH] fix: val can be reassigned through backing field inside its own getter --- .../lang/cfg/JetFlowInformationProvider.java | 29 +++++++++++++++---- .../backingFieldInsideGetter.kt | 22 ++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 5 ++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index 453540602bf..a939b765f05 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -270,12 +270,24 @@ public class JetFlowInformationProvider { @NotNull JetExpression expression, @NotNull Collection 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; if (expression.getParent() instanceof JetProperty && ((JetProperty)expression).getInitializer() != null) { isInitializedNotHere = false; } boolean hasBackingField = true; - VariableDescriptor variableDescriptor = ctxt.variableDescriptor; if (variableDescriptor instanceof PropertyDescriptor) { 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) { VariableDescriptor variableDescriptor = cxtx.variableDescriptor; boolean[] error = new boolean[1]; - if (isBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked - if (!isBackingFieldReference(element, cxtx, error, true)) return false; + if (isCorrectBackingFieldReference((JetElement) element.getParent(), cxtx, error, false)) return false; // this expression has been already checked + if (!isCorrectBackingFieldReference(element, cxtx, error, true)) return false; if (error[0]) return true; if (!(variableDescriptor instanceof PropertyDescriptor)) { report(Errors.NOT_PROPERTY_BACKING_FIELD.on(element), cxtx); @@ -398,12 +410,13 @@ public class JetFlowInformationProvider { 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; - if (element instanceof JetSimpleNameExpression && ((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER) { + if (isBackingFieldReference(element)) { 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) { return true; } @@ -415,6 +428,10 @@ public class JetFlowInformationProvider { 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>> initializersMap) { Edges> initializers = initializersMap.get(pseudocode.getExitInstruction()); Set usedVariables = pseudocodeVariablesData.getUsedVariables(pseudocode); diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt new file mode 100644 index 00000000000..201af14a34f --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt @@ -0,0 +1,22 @@ +package a + +import java.util.HashSet + +val a: MutableSet? = null + get() { + if (a == null) { + $a = HashSet() + } + return a + } + +class R { + val b: String? = null + get() { + if (b == null) { + $b = "b" + } + return b + } +} + diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 62e02bc18d9..456263ed832 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -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); } + @TestMetadata("backingFieldInsideGetter.kt") + public void testBackingFieldInsideGetter() throws Exception { + doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/backingFieldInsideGetter.kt"); + } + @TestMetadata("checkInnerLocalDeclarations.kt") public void testCheckInnerLocalDeclarations() throws Exception { doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/checkInnerLocalDeclarations.kt");