Added "unused value analysis"
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user