Added "unused value analysis"

This commit is contained in:
svtk
2011-11-24 15:58:25 +04:00
parent 4bf3a61621
commit 97f377b529
6 changed files with 96 additions and 4 deletions
@@ -36,7 +36,8 @@ public class JetControlFlowGraphTraverser<D> {
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;
@@ -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<VariableDescriptor> declaredVariables) {
JetControlFlowGraphTraverser<Set<VariableDescriptor>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true);
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionsMergeStrategy<Set<VariableDescriptor>>() {
@Override
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
Set<VariableDescriptor> enterResult = Sets.newHashSet();
for (Set<VariableDescriptor> edgeData : incomingEdgesData) {
enterResult.addAll(edgeData);
}
Set<VariableDescriptor> 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<VariableDescriptor>, Set<VariableDescriptor>>(enterResult, exitResult);
}
}, Collections.<VariableDescriptor>emptySet(), Collections.<VariableDescriptor>emptySet(), false);
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Set<VariableDescriptor>>() {
@Override
public void execute(Instruction instruction, @Nullable Set<VariableDescriptor> enterData, @Nullable Set<VariableDescriptor> 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
@@ -145,6 +145,19 @@ public interface Errors {
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME);
PsiElementOnlyDiagnosticFactory1<JetProperty, DeclarationDescriptor> UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME);
PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor> UNUSED_VALUE = new PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor>(WARNING, "The value ''{0}'' assigned to ''{1}'' is never used", NAME) {
@Override
protected String makeMessageForA(@NotNull JetElement element) {
return element.getText();
}
};
PsiElementOnlyDiagnosticFactory1<JetElement, JetElement> UNUSED_CHANGED_VALUE = new PsiElementOnlyDiagnosticFactory1<JetElement, JetElement>(WARNING, "The value changed at ''{0}'' is never used", NAME) {
@Override
protected String makeMessageFor(JetElement argument) {
return argument.getText();
}
};
PsiElementOnlyDiagnosticFactory2<JetExpression, DeclarationDescriptor, JetProperty[]> VAL_REASSIGNMENT = new PsiElementOnlyDiagnosticFactory2<JetExpression, DeclarationDescriptor, JetProperty[]>(ERROR, "Val can not be reassigned", NAME) {
@NotNull
@Override
@@ -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;
}
}
@@ -84,6 +84,6 @@ public class ControlFlowAnalyzer {
flowInformationProvider.markUninitializedVariables(function.asElement(), false, inLocalDeclaration);
// flowInformationProvider.markUnusedVariables(function.asElement());
flowInformationProvider.markUnusedVariables(function.asElement());
}
}
@@ -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) {