diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowGraphTraverser.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowGraphTraverser.java index 05171e2ad95..d9b090ff885 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowGraphTraverser.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetControlFlowGraphTraverser.java @@ -36,7 +36,8 @@ public class JetControlFlowGraphTraverser { D initialDataValueForEnterInstruction, boolean straightDirection) { initializeDataMap(pseudocode, initialDataValue); - dataMap.put(pseudocode.getEnterInstruction(), Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction)); + dataMap.put(straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction(), + Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction)); boolean[] changed = new boolean[1]; changed[0] = true; 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 0af3af13785..426048e91fb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -5,6 +5,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.cfg.pseudocode.*; @@ -274,7 +275,7 @@ public class JetFlowInformationProvider { boolean hasInitializer = !possiblePoints.isEmpty() || enterInitializationPoints.isInitialized(); if (possiblePoints.size() == 1) { JetElement initializer = possiblePoints.iterator().next(); - if (initializer == element.getParent()) { + if (initializer instanceof JetProperty && initializer == element.getParent()) { hasInitializer = false; } } @@ -402,13 +403,73 @@ public class JetFlowInformationProvider { for (VariableDescriptor declaredVariable : declaredVariables) { if (!usedVariables.contains(declaredVariable)) { PsiElement element = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declaredVariable); - if (element instanceof JetProperty) { + if (element instanceof JetProperty && JetPsiUtil.isLocal((JetProperty) element)) { PsiElement nameIdentifier = ((JetProperty) element).getNameIdentifier(); PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element; trace.report(Errors.UNUSED_VARIABLE.on((JetProperty)element, elementToMark, declaredVariable)); } } } + + markUnusedValues(subroutine, pseudocode, declaredVariables); + } + + private void markUnusedValues(@NotNull JetElement subroutine, Pseudocode pseudocode, final Collection declaredVariables) { + JetControlFlowGraphTraverser> traverser = JetControlFlowGraphTraverser.create(pseudocode, true); + traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionsMergeStrategy>() { + @Override + public Pair, Set> execute(Instruction instruction, @NotNull Collection> incomingEdgesData) { + Set enterResult = Sets.newHashSet(); + for (Set edgeData : incomingEdgesData) { + enterResult.addAll(edgeData); + } + Set exitResult = Sets.newHashSet(enterResult); + if (instruction instanceof ReadValueInstruction) { + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); + if (variableDescriptor != null) { + exitResult.add(variableDescriptor); + } + } + else if (instruction instanceof WriteValueInstruction) { + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); + if (variableDescriptor != null) { + exitResult.remove(variableDescriptor); + } + } + return new Pair, Set>(enterResult, exitResult); + } + }, Collections.emptySet(), Collections.emptySet(), false); + traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy>() { + @Override + public void execute(Instruction instruction, @Nullable Set enterData, @Nullable Set exitData) { + assert enterData != null && exitData != null; + if (instruction instanceof WriteValueInstruction) { + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); + if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) { + if (!enterData.contains(variableDescriptor)) { + PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor); + assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter; + boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement); + if (isLocal) { + JetElement element = ((WriteValueInstruction) instruction).getElement(); + if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { + JetExpression right = ((JetBinaryExpression) element).getRight(); + if (right != null) { + trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor)); + } + } + else if (element instanceof JetPostfixExpression) { + IElementType operationToken = ((JetPostfixExpression) element).getOperationSign().getReferencedNameElementType(); + if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) { + trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element)); + } + } + } + } + } + } + } + }); } @Nullable 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 da7edd9d6fe..98f7f7d5d69 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -145,6 +145,19 @@ public interface Errors { PsiElementOnlyDiagnosticFactory1 UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME); PsiElementOnlyDiagnosticFactory1 UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME); + PsiElementOnlyDiagnosticFactory2 UNUSED_VALUE = new PsiElementOnlyDiagnosticFactory2(WARNING, "The value ''{0}'' assigned to ''{1}'' is never used", NAME) { + @Override + protected String makeMessageForA(@NotNull JetElement element) { + return element.getText(); + } + }; + PsiElementOnlyDiagnosticFactory1 UNUSED_CHANGED_VALUE = new PsiElementOnlyDiagnosticFactory1(WARNING, "The value changed at ''{0}'' is never used", NAME) { + @Override + protected String makeMessageFor(JetElement argument) { + return argument.getText(); + } + }; + PsiElementOnlyDiagnosticFactory2 VAL_REASSIGNMENT = new PsiElementOnlyDiagnosticFactory2(ERROR, "Val can not be reassigned", NAME) { @NotNull @Override 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 353ea233095..0fb77fc8703 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiUtil.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.psi; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lexer.JetTokens; @@ -102,4 +103,17 @@ public class JetPsiUtil { return unquoteIdentifier(quoted); } } + + public static boolean isLocal(@NotNull JetProperty property) { + JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(property, JetClassOrObject.class); + JetDeclarationWithBody function = PsiTreeUtil.getParentOfType(property, 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/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java index c8fbeafee31..96d09dce0bc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/ControlFlowAnalyzer.java @@ -84,6 +84,6 @@ public class ControlFlowAnalyzer { flowInformationProvider.markUninitializedVariables(function.asElement(), false, inLocalDeclaration); -// flowInformationProvider.markUnusedVariables(function.asElement()); + flowInformationProvider.markUnusedVariables(function.asElement()); } } diff --git a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java index 17353d877d8..09f872108fc 100644 --- a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java @@ -88,6 +88,9 @@ public class JetPsiChecker implements Annotator { } else if (diagnostic.getSeverity() == Severity.WARNING) { annotation = holder.createWarningAnnotation(diagnostic.getFactory().getTextRange(diagnostic), getMessage(diagnostic)); + if (diagnostic.getFactory() == Errors.UNUSED_VARIABLE) { + annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } } if (annotation != null) { if (diagnostic instanceof DiagnosticWithPsiElementImpl && diagnostic.getFactory() instanceof PsiElementOnlyDiagnosticFactory) {