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 68da00a20d3..453540602bf 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -188,7 +188,7 @@ public class JetFlowInformationProvider { //////////////////////////////////////////////////////////////////////////////// // Uninitialized variables analysis - public void markUninitializedVariables(final boolean processLocalDeclaration) { + public void markUninitializedVariables() { final Collection varWithUninitializedErrorGenerated = Sets.newHashSet(); final Collection varWithValReassignErrorGenerated = Sets.newHashSet(); final boolean processClassOrObject = subroutine instanceof JetClassOrObject; @@ -218,7 +218,7 @@ public class JetFlowInformationProvider { JetElement element = ((WriteValueInstruction) instruction).getlValue(); boolean error = checkBackingField(ctxt, element); if (!(element instanceof JetExpression)) return; - if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration + if (!error) { error = checkValReassignment(ctxt, (JetExpression) element, varWithValReassignErrorGenerated); } if (!error && processClassOrObject) { @@ -229,8 +229,11 @@ public class JetFlowInformationProvider { } } }); + } + public void recordInitializedVariables() { Pseudocode pseudocode = pseudocodeVariablesData.getPseudocode(); + Map>> initializers = pseudocodeVariablesData.getVariableInitializers(); recordInitializedVariables(pseudocode, initializers); for (LocalDeclarationInstruction instruction : pseudocode.getLocalDeclarations()) { recordInitializedVariables(instruction.getBody(), initializers); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java index 06129923e8f..474ea917c4f 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java @@ -18,7 +18,6 @@ package org.jetbrains.jet.lang.resolve; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider; -import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyAccessorDescriptor; import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor; @@ -26,6 +25,7 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.types.JetType; import javax.inject.Inject; +import java.util.List; import java.util.Map; import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; @@ -48,13 +48,16 @@ public class ControlFlowAnalyzer { } public void process(@NotNull BodiesResolveContext bodiesResolveContext) { + for (JetFile file : bodiesResolveContext.getFiles()) { + checkDeclarationContainer(file); + } for (JetClass aClass : bodiesResolveContext.getClasses().keySet()) { if (!bodiesResolveContext.completeAnalysisNeeded(aClass)) continue; - checkClassOrObject(aClass); + checkDeclarationContainer(aClass); } for (JetObjectDeclaration objectDeclaration : bodiesResolveContext.getObjects().keySet()) { if (!bodiesResolveContext.completeAnalysisNeeded(objectDeclaration)) continue; - checkClassOrObject(objectDeclaration); + checkDeclarationContainer(objectDeclaration); } for (Map.Entry entry : bodiesResolveContext.getFunctions().entrySet()) { JetNamedFunction function = entry.getKey(); @@ -73,10 +76,15 @@ public class ControlFlowAnalyzer { } } - private void checkClassOrObject(JetClassOrObject klass) { - // A pseudocode of class initialization corresponds to a class - JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider(klass, trace); - flowInformationProvider.markUninitializedVariables(topDownAnalysisParameters.isDeclaredLocally()); + private void checkDeclarationContainer(JetDeclarationContainer declarationContainer) { + // A pseudocode of class/object initialization corresponds to a class/object + // or initialization of properties corresponds to a package declared in a file + JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetElement) declarationContainer, trace); + flowInformationProvider.recordInitializedVariables(); + + if (topDownAnalysisParameters.isDeclaredLocally()) return; + + flowInformationProvider.markUninitializedVariables(); } private void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor) { @@ -87,13 +95,6 @@ public class ControlFlowAnalyzer { assert accessorDescriptor != null; checkFunction(accessor, accessorDescriptor.getReturnType()); } - if (property.getInitializer() != null && propertyDescriptor.getContainingDeclaration() instanceof NamespaceDescriptor) { - // check initializer's pseudocode for uninitialized values - // initializers of properties inside classes/objects are checked in 'checkClassOrObject', - // because class/object may have anonymous initialization blocks that affect analysis - JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider(property, trace); - flowInformationProvider.markUninitializedVariables(false); - } } private void checkFunction(JetDeclarationWithBody function, final @NotNull JetType expectedReturnType) { @@ -103,11 +104,19 @@ public class ControlFlowAnalyzer { if (bodyExpression == null) return; JetFlowInformationProvider flowInformationProvider = new JetFlowInformationProvider((JetDeclaration) function, trace); + boolean isPropertyAccessor = function instanceof JetPropertyAccessor; + if (!isPropertyAccessor) { + flowInformationProvider.recordInitializedVariables(); + } + + if (topDownAnalysisParameters.isDeclaredLocally()) return; + flowInformationProvider.checkDefiniteReturn(expectedReturnType); - // Property accessor is checked through initialization of a class check (at 'checkClassOrObject') - boolean isPropertyAccessor = function instanceof JetPropertyAccessor; - flowInformationProvider.markUninitializedVariables(topDownAnalysisParameters.isDeclaredLocally() || isPropertyAccessor); + if (!isPropertyAccessor) { + // Property accessor is checked through initialization of a class/object or package properties (at 'checkDeclarationContainer') + flowInformationProvider.markUninitializedVariables(); + } flowInformationProvider.markUnusedVariables(); diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt new file mode 100644 index 00000000000..ec920b67de7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt @@ -0,0 +1,4 @@ +package a + +val a : Int = b +val b : Int = a \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 70450b56079..62e02bc18d9 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -964,6 +964,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/kt897.kt"); } + @TestMetadata("propertiesOrderInPackage.kt") + public void testPropertiesOrderInPackage() throws Exception { + doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/propertiesOrderInPackage.kt"); + } + @TestMetadata("uninitializedInLocalDeclarations.kt") public void testUninitializedInLocalDeclarations() throws Exception { doTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/uninitializedInLocalDeclarations.kt");