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 21c0223bae1..0fc0e09e4b7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -16,6 +16,7 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lexer.JetTokens; @@ -354,8 +355,11 @@ public class JetFlowInformationProvider { return true; } - JetClassOrObject propertyClassOrObject = PsiTreeUtil.getParentOfType(property, JetClassOrObject.class); - if (propertyClassOrObject == null || !PsiTreeUtil.isAncestor(propertyClassOrObject, element, false)) { + JetNamedDeclaration parentDeclaration = PsiTreeUtil.getParentOfType(element, JetNamedDeclaration.class); + DeclarationDescriptor declarationDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, parentDeclaration); + assert declarationDescriptor != null; + ClassDescriptor variableClass = DescriptorUtils.getParentOfType(variableDescriptor, ClassDescriptor.class); + if (variableClass == null || !DescriptorUtils.isAncestor(variableClass, declarationDescriptor, false)) { trace.report(Errors.INACCESSIBLE_BACKING_FIELD.on(element)); return true; } @@ -482,11 +486,7 @@ public class JetFlowInformationProvider { assert pseudocode != null; JetControlFlowGraphTraverser> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false); final Collection declaredVariables = collectDeclaredVariables(subroutine); - Collection usedVariables = collectUsedVariables(pseudocode); Map sinkInstructionData = Maps.newHashMap(); - for (VariableDescriptor usedVariable : usedVariables) { - sinkInstructionData.put(usedVariable, VariableStatus.UNUSED); - } traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy>() { @Override public Pair, Map> execute(Instruction instruction, @NotNull Collection> incomingEdgesData) { @@ -505,7 +505,17 @@ public class JetFlowInformationProvider { exitResult.put(variableDescriptor, VariableStatus.READ); } else if (instruction instanceof WriteValueInstruction) { - exitResult.put(variableDescriptor, VariableStatus.WRITTEN); + VariableStatus variableStatus = enterResult.get(variableDescriptor); + if (variableStatus == null) variableStatus = VariableStatus.UNUSED; + switch(variableStatus) { + case UNUSED: + case ONLY_WRITTEN: + exitResult.put(variableDescriptor, VariableStatus.ONLY_WRITTEN); + break; + case WRITTEN: + case READ: + exitResult.put(variableDescriptor, VariableStatus.WRITTEN); + } } } return new Pair, Map>(enterResult, exitResult); @@ -516,13 +526,12 @@ public class JetFlowInformationProvider { public void execute(Instruction instruction, @Nullable Map enterData, @Nullable Map exitData) { assert enterData != null && exitData != null; VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor)) return; - PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); - assert variableDeclarationElement instanceof JetDeclaration; - if (!JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement)) return; + if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) || + !DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return; + VariableStatus variableStatus = enterData.get(variableDescriptor); if (instruction instanceof WriteValueInstruction) { JetElement element = ((WriteValueInstruction) instruction).getElement(); - if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) { + if (variableStatus != VariableStatus.READ) { if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { JetExpression right = ((JetBinaryExpression) element).getRight(); if (right != null) { @@ -542,7 +551,7 @@ public class JetFlowInformationProvider { if (element instanceof JetNamedDeclaration) { PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element; - if (enterData.get(variableDescriptor) == VariableStatus.UNUSED) { + if (variableStatus == null || variableStatus == VariableStatus.UNUSED) { if (element instanceof JetProperty) { trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, elementToMark, variableDescriptor)); } @@ -559,9 +568,15 @@ public class JetFlowInformationProvider { } } } - else if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN && element instanceof JetProperty) { + else if (variableStatus == VariableStatus.ONLY_WRITTEN && element instanceof JetProperty) { trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor)); } + else if (variableStatus == VariableStatus.WRITTEN && element instanceof JetProperty) { + JetExpression initializer = ((JetProperty) element).getInitializer(); + if (initializer != null) { + trace.report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on((JetNamedDeclaration) element, initializer, variableDescriptor)); + } + } } } @@ -570,8 +585,9 @@ public class JetFlowInformationProvider { } private static enum VariableStatus { - READ(2), - WRITTEN(1), + READ(3), + WRITTEN(2), + ONLY_WRITTEN(1), UNUSED(0); private int importance; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 0cf5938185d..72f82897c6b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -154,6 +154,7 @@ public interface Errors { UnusedElementDiagnosticFactory UNUSED_VARIABLE = UnusedElementDiagnosticFactory.create(WARNING, "Variable ''{0}'' is never used", NAME); UnusedElementDiagnosticFactory UNUSED_PARAMETER = UnusedElementDiagnosticFactory.create(WARNING, "Parameter ''{0}'' is never used", NAME); UnusedElementDiagnosticFactory ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE = UnusedElementDiagnosticFactory.create(WARNING, "Variable ''{0}'' is assigned but never accessed", NAME); + PsiElementOnlyDiagnosticFactory1 VARIABLE_WITH_REDUNDANT_INITIALIZER = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' initializer is redundant", NAME); PsiElementOnlyDiagnosticFactory2 UNUSED_VALUE = new PsiElementOnlyDiagnosticFactory2(WARNING, "The value ''{0}'' assigned to ''{1}'' is never used", NAME) { @Override protected String makeMessageForA(@NotNull JetElement element) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java index 73433fb6cc9..3fe1c9f4947 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -103,17 +103,4 @@ public class JetPsiUtil { return unquoteIdentifier(quoted); } } - - public static boolean isLocal(@NotNull JetElement element) { - JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(element, JetClassOrObject.class); - JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(element, JetDeclarationWithBody.class); - if (function != null && PsiTreeUtil.isAncestor(classOrObject, function, false)) { - return true; - } - if (classOrObject != null && PsiTreeUtil.isAncestor(function, classOrObject, false)) { - return false; - } - if (function != null) return true; - return false; - } } diff --git a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.jet b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.jet index f32efe534f1..78025e69a83 100644 --- a/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.jet +++ b/compiler/testData/diagnostics/tests/BinaryCallsOnNullableValues.jet @@ -11,7 +11,7 @@ class C() { } fun f(): Unit { - var x: Int? = 1 + var x: Int? = 1 x = 1 x + 1 x plus 1 diff --git a/compiler/testData/diagnostics/tests/FunctionReturnTypes.jet b/compiler/testData/diagnostics/tests/FunctionReturnTypes.jet index 27d52fb227b..0bab0cf8335 100644 --- a/compiler/testData/diagnostics/tests/FunctionReturnTypes.jet +++ b/compiler/testData/diagnostics/tests/FunctionReturnTypes.jet @@ -18,7 +18,7 @@ fun bbb() { return 1 } -fun foo(expr: StringBuilder): Int { +fun foo(expr: StringBuilder): Int { val c = 'a' when(c) { 0.chr => throw Exception("zero") @@ -211,4 +211,4 @@ fun testFunctionLiterals() { object A {} } -} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet b/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet index c9c53ef1fe7..22cfde8fdaa 100644 --- a/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet +++ b/compiler/testData/diagnostics/tests/UninitializedOrReassignedVariables.jet @@ -16,7 +16,7 @@ fun t1(b : Boolean) { } doSmth(u) - var r: String + var r: String if (b) { r = "s" } diff --git a/compiler/testData/diagnostics/tests/UnreachableCode.jet b/compiler/testData/diagnostics/tests/UnreachableCode.jet index 088bba18e54..f8a4062984e 100644 --- a/compiler/testData/diagnostics/tests/UnreachableCode.jet +++ b/compiler/testData/diagnostics/tests/UnreachableCode.jet @@ -137,7 +137,7 @@ fun tf() : Int { return 1 } -fun failtest(a : Int) : Int { +fun failtest(a : Int) : Int { if (fail() || true) { } diff --git a/compiler/testData/diagnostics/tests/UnusedVariables.jet b/compiler/testData/diagnostics/tests/UnusedVariables.jet index 060ad3f83a4..b41e4ab4002 100644 --- a/compiler/testData/diagnostics/tests/UnusedVariables.jet +++ b/compiler/testData/diagnostics/tests/UnusedVariables.jet @@ -1,7 +1,7 @@ namespace unused_variables fun testSimpleCases() { - var i = 2 + var i = 2 i = 34 i = 34 doSmth(i) @@ -62,7 +62,7 @@ class MyTest() { } fun testIf() { - var a : Any + var a : Any if (1 < 2) { a = 23 } diff --git a/idea/testData/checker/BinaryCallsOnNullableValues.jet b/idea/testData/checker/BinaryCallsOnNullableValues.jet index 31186881eea..9cf12a60527 100644 --- a/idea/testData/checker/BinaryCallsOnNullableValues.jet +++ b/idea/testData/checker/BinaryCallsOnNullableValues.jet @@ -11,7 +11,7 @@ class C() { } fun f(): Unit { - var x: Int? = 1 + var x: Int? = 1 x = 1 x + 1 x plus 1 diff --git a/idea/testData/checker/FunctionReturnTypes.jet b/idea/testData/checker/FunctionReturnTypes.jet index 652bc6648f3..4b123df6245 100644 --- a/idea/testData/checker/FunctionReturnTypes.jet +++ b/idea/testData/checker/FunctionReturnTypes.jet @@ -13,7 +13,7 @@ fun bbb() { return 1 } -fun foo(expr: StringBuilder): Int { +fun foo(expr: StringBuilder): Int { val c = 'a' when(c) { 0.chr => throw Exception("zero") diff --git a/idea/testData/checker/UnreachableCode.jet b/idea/testData/checker/UnreachableCode.jet index 3fdd4ffe9df..bec6501b754 100644 --- a/idea/testData/checker/UnreachableCode.jet +++ b/idea/testData/checker/UnreachableCode.jet @@ -135,7 +135,7 @@ fun tf() : Int { return 1 } -fun failtest(a : Int) : Int { +fun failtest(a : Int) : Int { if (fail() || true) { }