KT-610 Distinguish errors 'unused variable' and 'variable is assigned but never accessed'

This commit is contained in:
svtk
2011-11-28 13:15:03 +04:00
parent 4c2b0b9444
commit ecb3db0912
11 changed files with 139 additions and 96 deletions
@@ -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<!>
} }
@@ -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