KT-610 Distinguish errors 'unused variable' and 'variable is assigned but never accessed'
This commit is contained in:
+36
-13
@@ -1,5 +1,6 @@
|
|||||||
package org.jetbrains.jet.lang.cfg;
|
package org.jetbrains.jet.lang.cfg;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -19,31 +20,36 @@ import java.util.Map;
|
|||||||
public class JetControlFlowGraphTraverser<D> {
|
public class JetControlFlowGraphTraverser<D> {
|
||||||
private final Pseudocode pseudocode;
|
private final Pseudocode pseudocode;
|
||||||
private final boolean lookInside;
|
private final boolean lookInside;
|
||||||
|
private final boolean straightDirection;
|
||||||
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
|
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
|
||||||
|
|
||||||
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside) {
|
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||||
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside);
|
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside, straightDirection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside) {
|
private JetControlFlowGraphTraverser(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
|
||||||
this.pseudocode = pseudocode;
|
this.pseudocode = pseudocode;
|
||||||
this.lookInside = lookInside;
|
this.lookInside = lookInside;
|
||||||
|
this.straightDirection = straightDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instruction getStartInstruction(Pseudocode pseudocode) {
|
||||||
|
return straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void collectInformationFromInstructionGraph(
|
public void collectInformationFromInstructionGraph(
|
||||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||||
D initialDataValue,
|
D initialDataValue,
|
||||||
D initialDataValueForEnterInstruction,
|
D initialDataValueForEnterInstruction) {
|
||||||
boolean straightDirection) {
|
|
||||||
initializeDataMap(pseudocode, initialDataValue);
|
initializeDataMap(pseudocode, initialDataValue);
|
||||||
dataMap.put(straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction(),
|
dataMap.put(getStartInstruction(pseudocode),
|
||||||
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
|
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
|
||||||
|
|
||||||
boolean[] changed = new boolean[1];
|
boolean[] changed = new boolean[1];
|
||||||
changed[0] = true;
|
changed[0] = true;
|
||||||
while (changed[0]) {
|
while (changed[0]) {
|
||||||
changed[0] = false;
|
changed[0] = false;
|
||||||
traverseSubGraph(pseudocode, instructionDataMergeStrategy, Collections.<Instruction>emptyList(), straightDirection, changed, false);
|
traverseSubGraph(pseudocode, instructionDataMergeStrategy, Collections.<Instruction>emptyList(), changed, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,20 +70,25 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
Pseudocode pseudocode,
|
Pseudocode pseudocode,
|
||||||
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
|
||||||
Collection<Instruction> previousSubGraphInstructions,
|
Collection<Instruction> previousSubGraphInstructions,
|
||||||
boolean straightDirection,
|
|
||||||
boolean[] changed,
|
boolean[] changed,
|
||||||
boolean isLocal) {
|
boolean isLocal) {
|
||||||
List<Instruction> instructions = pseudocode.getInstructions();
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
|
Instruction startInstruction = getStartInstruction(pseudocode);
|
||||||
|
|
||||||
|
if (!straightDirection) {
|
||||||
|
instructions = Lists.newArrayList(instructions);
|
||||||
|
Collections.reverse(instructions);
|
||||||
|
}
|
||||||
for (Instruction instruction : instructions) {
|
for (Instruction instruction : instructions) {
|
||||||
if (!isLocal && instruction instanceof SubroutineEnterInstruction) continue;
|
boolean isStart = straightDirection ? instruction instanceof SubroutineEnterInstruction : instruction instanceof SubroutineSinkInstruction;
|
||||||
|
if (!isLocal && isStart) continue;
|
||||||
|
|
||||||
Collection<Instruction> allPreviousInstructions;
|
Collection<Instruction> allPreviousInstructions;
|
||||||
Collection<Instruction> previousInstructions = straightDirection
|
Collection<Instruction> previousInstructions = straightDirection
|
||||||
? instruction.getPreviousInstructions()
|
? instruction.getPreviousInstructions()
|
||||||
: instruction.getNextInstructions();
|
: instruction.getNextInstructions();
|
||||||
|
|
||||||
if (instruction == enterInstruction && !previousSubGraphInstructions.isEmpty()) {
|
if (instruction == startInstruction && !previousSubGraphInstructions.isEmpty()) {
|
||||||
allPreviousInstructions = Lists.newArrayList(previousInstructions);
|
allPreviousInstructions = Lists.newArrayList(previousInstructions);
|
||||||
allPreviousInstructions.addAll(previousSubGraphInstructions);
|
allPreviousInstructions.addAll(previousSubGraphInstructions);
|
||||||
}
|
}
|
||||||
@@ -87,7 +98,15 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
|
|
||||||
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||||
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
|
||||||
traverseSubGraph(subroutinePseudocode, instructionDataMergeStrategy, previousInstructions, straightDirection, changed, true);
|
traverseSubGraph(subroutinePseudocode, instructionDataMergeStrategy, previousInstructions, changed, true);
|
||||||
|
Instruction lastInstruction = straightDirection ? subroutinePseudocode.getSinkInstruction() : subroutinePseudocode.getEnterInstruction();
|
||||||
|
Pair<D, D> previousValue = dataMap.get(instruction);
|
||||||
|
Pair<D, D> newValue = dataMap.get(lastInstruction);
|
||||||
|
if (!previousValue.equals(newValue)) {
|
||||||
|
changed[0] = true;
|
||||||
|
dataMap.put(instruction, newValue);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
Pair<D, D> previousDataValue = dataMap.get(instruction);
|
Pair<D, D> previousDataValue = dataMap.get(instruction);
|
||||||
|
|
||||||
@@ -116,12 +135,16 @@ public class JetControlFlowGraphTraverser<D> {
|
|||||||
Pseudocode pseudocode,
|
Pseudocode pseudocode,
|
||||||
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
|
||||||
List<Instruction> instructions = pseudocode.getInstructions();
|
List<Instruction> instructions = pseudocode.getInstructions();
|
||||||
|
if (!straightDirection) {
|
||||||
|
instructions = Lists.newArrayList(instructions);
|
||||||
|
Collections.reverse(instructions);
|
||||||
|
}
|
||||||
for (Instruction instruction : instructions) {
|
for (Instruction instruction : instructions) {
|
||||||
if (instruction.isDead()) continue;
|
if (instruction.isDead()) continue;
|
||||||
Pair<D, D> pair = dataMap.get(instruction);
|
|
||||||
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||||
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
|
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
|
||||||
}
|
}
|
||||||
|
Pair<D, D> pair = dataMap.get(instruction);
|
||||||
instructionDataAnalyzeStrategy.execute(instruction,
|
instructionDataAnalyzeStrategy.execute(instruction,
|
||||||
pair != null ? pair.getFirst() : null,
|
pair != null ? pair.getFirst() : null,
|
||||||
pair != null ? pair.getSecond() : null);
|
pair != null ? pair.getSecond() : null);
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ public class JetFlowInformationProvider {
|
|||||||
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false);
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableInitializers>> traverser = JetControlFlowGraphTraverser.create(pseudocode, false, true);
|
||||||
|
|
||||||
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
||||||
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||||
@@ -213,7 +213,7 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
traverser.collectInformationFromInstructionGraph(variableInitializersMergeStrategy, Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction, true);
|
traverser.collectInformationFromInstructionGraph(variableInitializersMergeStrategy, Collections.<VariableDescriptor, VariableInitializers>emptyMap(), initialMapForStartInstruction);
|
||||||
|
|
||||||
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithUninitializedErrorGenerated = Sets.newHashSet();
|
||||||
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
final Collection<VariableDescriptor> varWithValReassignErrorGenerated = Sets.newHashSet();
|
||||||
@@ -396,7 +396,7 @@ public class JetFlowInformationProvider {
|
|||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
@@ -419,73 +419,50 @@ public class JetFlowInformationProvider {
|
|||||||
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
public void markUnusedVariables(@NotNull JetElement subroutine) {
|
||||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
|
||||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
|
||||||
@Override
|
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
|
||||||
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
for (VariableDescriptor usedVariable : usedVariables) {
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
sinkInstructionData.put(usedVariable, VariableStatus.UNUSED);
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
|
||||||
usedVariables.add(variableDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
|
|
||||||
for (VariableDescriptor declaredVariable : declaredVariables) {
|
|
||||||
if (!usedVariables.contains(declaredVariable)) {
|
|
||||||
PsiElement element = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, declaredVariable);
|
|
||||||
//todo
|
|
||||||
if (element instanceof JetProperty && JetPsiUtil.isLocal((JetNamedDeclaration) element)) {
|
|
||||||
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
|
||||||
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
|
||||||
trace.report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration)element, elementToMark, declaredVariable));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
||||||
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.InstructionDataMergeStrategy<Set<VariableDescriptor>>() {
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<Set<VariableDescriptor>, Set<VariableDescriptor>> execute(Instruction instruction, @NotNull Collection<Set<VariableDescriptor>> incomingEdgesData) {
|
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
|
||||||
Set<VariableDescriptor> enterResult = Sets.newHashSet();
|
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
|
||||||
for (Set<VariableDescriptor> edgeData : incomingEdgesData) {
|
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
|
||||||
enterResult.addAll(edgeData);
|
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
|
||||||
}
|
VariableDescriptor variableDescriptor = entry.getKey();
|
||||||
Set<VariableDescriptor> exitResult = Sets.newHashSet(enterResult);
|
VariableStatus variableStatus = entry.getValue();
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
enterResult.put(variableDescriptor, variableStatus.merge(enterResult.get(variableDescriptor)));
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
|
||||||
if (variableDescriptor != null) {
|
|
||||||
exitResult.add(variableDescriptor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
Map<VariableDescriptor, VariableStatus> exitResult = Maps.newHashMap(enterResult);
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
|
||||||
if (variableDescriptor != null) {
|
if (variableDescriptor != null) {
|
||||||
exitResult.remove(variableDescriptor);
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
|
exitResult.put(variableDescriptor, VariableStatus.READ);
|
||||||
|
}
|
||||||
|
else if (instruction instanceof WriteValueInstruction) {
|
||||||
|
exitResult.put(variableDescriptor, VariableStatus.WRITTEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Pair<Set<VariableDescriptor>, Set<VariableDescriptor>>(enterResult, exitResult);
|
return new Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>>(enterResult, exitResult);
|
||||||
}
|
}
|
||||||
}, Collections.<VariableDescriptor>emptySet(), Collections.<VariableDescriptor>emptySet(), false);
|
}, Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData);
|
||||||
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Set<VariableDescriptor>>() {
|
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Set<VariableDescriptor> enterData, @Nullable Set<VariableDescriptor> exitData) {
|
public void execute(Instruction instruction, @Nullable Map<VariableDescriptor, VariableStatus> enterData, @Nullable Map<VariableDescriptor, VariableStatus> exitData) {
|
||||||
assert enterData != null && exitData != null;
|
assert enterData != null && exitData != null;
|
||||||
if (instruction instanceof WriteValueInstruction) {
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
||||||
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
|
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
||||||
if (!enterData.contains(variableDescriptor)) {
|
assert variableDeclarationElement instanceof JetDeclaration;
|
||||||
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
|
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
|
||||||
// assert variableDeclarationElement instanceof JetProperty || variableDeclarationElement instanceof JetParameter;
|
if (isLocal) {
|
||||||
// boolean isLocal = !(variableDeclarationElement instanceof JetProperty) || JetPsiUtil.isLocal((JetProperty) variableDeclarationElement);
|
if (instruction instanceof WriteValueInstruction) {
|
||||||
assert variableDeclarationElement instanceof JetDeclaration;
|
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
||||||
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
|
if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) {
|
||||||
if (isLocal) {
|
|
||||||
JetElement element = ((WriteValueInstruction) instruction).getElement();
|
|
||||||
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
|
||||||
JetExpression right = ((JetBinaryExpression) element).getRight();
|
JetExpression right = ((JetBinaryExpression) element).getRight();
|
||||||
if (right != null) {
|
if (right != null) {
|
||||||
@@ -499,6 +476,20 @@ public class JetFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
JetDeclaration element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||||
|
if (element instanceof JetProperty) {
|
||||||
|
PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier();
|
||||||
|
PsiElement elementToMark = nameIdentifier != null ? nameIdentifier : element;
|
||||||
|
if (enterData.get(variableDescriptor) == VariableStatus.UNUSED) {
|
||||||
|
trace.report(Errors.UNUSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor));
|
||||||
|
}
|
||||||
|
else if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN) {
|
||||||
|
trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, elementToMark, variableDescriptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,26 +497,44 @@ public class JetFlowInformationProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static enum VariableStatus {
|
||||||
|
READ(2),
|
||||||
|
WRITTEN(1),
|
||||||
|
UNUSED(0);
|
||||||
|
|
||||||
|
private int importance;
|
||||||
|
|
||||||
|
private VariableStatus(int importance) {
|
||||||
|
this.importance = importance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableStatus merge(@Nullable VariableStatus variableStatus) {
|
||||||
|
if (variableStatus == null || importance > variableStatus.importance) return this;
|
||||||
|
return variableStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Util methods
|
// Util methods
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
|
||||||
VariableDescriptor variableDescriptor = null;
|
JetElement element = null;
|
||||||
if (instruction instanceof ReadValueInstruction) {
|
if (instruction instanceof ReadValueInstruction) {
|
||||||
JetElement element = ((ReadValueInstruction) instruction).getElement();
|
element = ((ReadValueInstruction) instruction).getElement();
|
||||||
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
|
||||||
}
|
}
|
||||||
else if (instruction instanceof WriteValueInstruction) {
|
else if (instruction instanceof WriteValueInstruction) {
|
||||||
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
|
element = ((WriteValueInstruction) instruction).getlValue();
|
||||||
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), lValue, onlyReference);
|
|
||||||
}
|
}
|
||||||
return variableDescriptor;
|
else if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
|
||||||
|
}
|
||||||
|
return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
|
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
|
||||||
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
public void execute(Instruction instruction, Void enterData, Void exitData) {
|
||||||
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
|
||||||
@@ -542,7 +551,7 @@ public class JetFlowInformationProvider {
|
|||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
|
|
||||||
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
final Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
|
||||||
JetControlFlowGraphTraverser.<Void>create(pseudocode, false).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
JetControlFlowGraphTraverser.<Void>create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
|
||||||
if (instruction instanceof VariableDeclarationInstruction) {
|
if (instruction instanceof VariableDeclarationInstruction) {
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ public interface Errors {
|
|||||||
|
|
||||||
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME);
|
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> UNINITIALIZED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Variable ''{0}'' must be initialized", NAME);
|
||||||
PsiElementOnlyDiagnosticFactory1<JetNamedDeclaration, DeclarationDescriptor> UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME);
|
PsiElementOnlyDiagnosticFactory1<JetNamedDeclaration, DeclarationDescriptor> UNUSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is never used", NAME);
|
||||||
|
PsiElementOnlyDiagnosticFactory1<JetNamedDeclaration, DeclarationDescriptor> ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Variable ''{0}'' is assigned but never accessed", NAME);
|
||||||
PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor> UNUSED_VALUE = new PsiElementOnlyDiagnosticFactory2<JetElement, JetElement, DeclarationDescriptor>(WARNING, "The value ''{0}'' assigned to ''{1}'' 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
|
@Override
|
||||||
protected String makeMessageForA(@NotNull JetElement element) {
|
protected String makeMessageForA(@NotNull JetElement element) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class C() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun f(): Unit {
|
fun f(): Unit {
|
||||||
var x: Int? = 1
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!>: Int? = 1
|
||||||
x = 1
|
x = 1
|
||||||
x <!UNSAFE_INFIX_CALL!>+<!> 1
|
x <!UNSAFE_INFIX_CALL!>+<!> 1
|
||||||
x <!UNSAFE_INFIX_CALL!>plus<!> 1
|
x <!UNSAFE_INFIX_CALL!>plus<!> 1
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ fun declarations(a: Any?) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun vars(a: Any?) {
|
fun vars(a: Any?) {
|
||||||
var <!UNUSED_VARIABLE!>b<!>: Int = 0
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!>: Int = 0
|
||||||
if (a is Int) {
|
if (a is Int) {
|
||||||
b = <!UNUSED_VALUE!>a<!>
|
b = <!UNUSED_VALUE!>a<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class AClass() {
|
|||||||
val x : Any? = 1
|
val x : Any? = 1
|
||||||
|
|
||||||
fun Any?.vars(a: Any?) : Int {
|
fun Any?.vars(a: Any?) : Int {
|
||||||
var <!UNUSED_VARIABLE!>b<!>: Int = 0
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!>: Int = 0
|
||||||
if (ns.y is Int) {
|
if (ns.y is Int) {
|
||||||
b = <!UNUSED_VALUE!>ns.y<!>
|
b = <!UNUSED_VALUE!>ns.y<!>
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ trait T {}
|
|||||||
|
|
||||||
open class C {
|
open class C {
|
||||||
fun foo() {
|
fun foo() {
|
||||||
var <!UNUSED_VARIABLE!>t<!> : T? = null
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>t<!> : T? = null
|
||||||
if (this is T) {
|
if (this is T) {
|
||||||
t = <!UNUSED_VALUE!>this<!>
|
t = <!UNUSED_VALUE!>this<!>
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -16,7 +16,7 @@ fun t1(b : Boolean) {
|
|||||||
}
|
}
|
||||||
doSmth(<!UNINITIALIZED_VARIABLE!>u<!>)
|
doSmth(<!UNINITIALIZED_VARIABLE!>u<!>)
|
||||||
|
|
||||||
var r: String
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>r<!>: String
|
||||||
if (b) {
|
if (b) {
|
||||||
r = "s"
|
r = "s"
|
||||||
}
|
}
|
||||||
@@ -63,10 +63,10 @@ fun t4(a: A, val b: A, var c: A) {
|
|||||||
// reassigned vals
|
// reassigned vals
|
||||||
|
|
||||||
fun t1() {
|
fun t1() {
|
||||||
val <!UNUSED_VARIABLE!>a<!> : Int = 1
|
val <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Int = 1
|
||||||
<!VAL_REASSIGNMENT!>a<!> = <!UNUSED_VALUE!>2<!>
|
<!VAL_REASSIGNMENT!>a<!> = <!UNUSED_VALUE!>2<!>
|
||||||
|
|
||||||
var <!UNUSED_VARIABLE!>b<!> : Int = 1
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>b<!> : Int = 1
|
||||||
b = <!UNUSED_VALUE!>3<!>
|
b = <!UNUSED_VALUE!>3<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace unused_variables
|
namespace unused_variables
|
||||||
|
|
||||||
fun testSimpleCases() {
|
fun testSimpleCases() {
|
||||||
var i = 2
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 2
|
||||||
i = <!UNUSED_VALUE!>34<!>
|
i = <!UNUSED_VALUE!>34<!>
|
||||||
i = 34
|
i = 34
|
||||||
doSmth(i)
|
doSmth(i)
|
||||||
@@ -9,7 +9,7 @@ fun testSimpleCases() {
|
|||||||
|
|
||||||
var j = 2
|
var j = 2
|
||||||
j = <!UNUSED_CHANGED_VALUE!>j++<!>
|
j = <!UNUSED_CHANGED_VALUE!>j++<!>
|
||||||
j = <!UNUSED_CHANGED_VALUE, UNUSED_VALUE!>j--<!>
|
j = <!UNUSED_VALUE, UNUSED_CHANGED_VALUE!>j--<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
class IncDec() {
|
class IncDec() {
|
||||||
@@ -45,7 +45,7 @@ class MyTest() {
|
|||||||
fun testSimple() {
|
fun testSimple() {
|
||||||
a = "rro"
|
a = "rro"
|
||||||
|
|
||||||
var <!UNUSED_VARIABLE!>i<!> = 1;
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 1;
|
||||||
i = <!UNUSED_VALUE!>34<!>;
|
i = <!UNUSED_VALUE!>34<!>;
|
||||||
i = <!UNUSED_VALUE!>456<!>;
|
i = <!UNUSED_VALUE!>456<!>;
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ class MyTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testIf() {
|
fun testIf() {
|
||||||
var a : Any
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Any
|
||||||
if (1 < 2) {
|
if (1 < 2) {
|
||||||
a = 23
|
a = 23
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ class MyTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun testInnerFunctions() {
|
fun testInnerFunctions() {
|
||||||
var <!UNUSED_VARIABLE!>y<!> = 1
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>y<!> = 1
|
||||||
fun foo() {
|
fun foo() {
|
||||||
y = <!UNUSED_VALUE!>1<!>
|
y = <!UNUSED_VALUE!>1<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
//KT-610 Distinguish errors 'unused variable' and 'variable is assigned but never accessed'
|
||||||
|
|
||||||
|
namespace kt610
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
var <!UNUSED_VARIABLE!>j<!> = 9 //'unused variable' error
|
||||||
|
|
||||||
|
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 1 //should be an error 'variable i is assigned but never accessed'
|
||||||
|
i = <!UNUSED_VALUE!>2<!>
|
||||||
|
}
|
||||||
@@ -88,7 +88,7 @@ public class JetPsiChecker implements Annotator {
|
|||||||
}
|
}
|
||||||
else if (diagnostic.getSeverity() == Severity.WARNING) {
|
else if (diagnostic.getSeverity() == Severity.WARNING) {
|
||||||
annotation = holder.createWarningAnnotation(diagnostic.getFactory().getTextRange(diagnostic), getMessage(diagnostic));
|
annotation = holder.createWarningAnnotation(diagnostic.getFactory().getTextRange(diagnostic), getMessage(diagnostic));
|
||||||
if (diagnostic.getFactory() == Errors.UNUSED_VARIABLE) {
|
if (diagnostic.getFactory() == Errors.UNUSED_VARIABLE || diagnostic.getFactory() == Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE) {
|
||||||
annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL);
|
annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class C() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun f(): Unit {
|
fun f(): Unit {
|
||||||
var x: Int? = 1
|
var <warning>x</warning>: Int? = 1
|
||||||
x = 1
|
x = 1
|
||||||
x <error>+</error> 1
|
x <error>+</error> 1
|
||||||
x <error>plus</error> 1
|
x <error>plus</error> 1
|
||||||
|
|||||||
Reference in New Issue
Block a user