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;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -19,31 +20,36 @@ import java.util.Map;
public class JetControlFlowGraphTraverser<D> {
private final Pseudocode pseudocode;
private final boolean lookInside;
private final boolean straightDirection;
private final Map<Instruction, Pair<D, D>> dataMap = Maps.newLinkedHashMap();
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside) {
return new JetControlFlowGraphTraverser<D>(pseudocode, lookInside);
public static <D> JetControlFlowGraphTraverser<D> create(Pseudocode pseudocode, boolean lookInside, boolean straightDirection) {
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.lookInside = lookInside;
this.straightDirection = straightDirection;
}
private Instruction getStartInstruction(Pseudocode pseudocode) {
return straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
}
public void collectInformationFromInstructionGraph(
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
D initialDataValue,
D initialDataValueForEnterInstruction,
boolean straightDirection) {
D initialDataValueForEnterInstruction) {
initializeDataMap(pseudocode, initialDataValue);
dataMap.put(straightDirection ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction(),
dataMap.put(getStartInstruction(pseudocode),
Pair.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
boolean[] changed = new boolean[1];
changed[0] = true;
while (changed[0]) {
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,
InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
Collection<Instruction> previousSubGraphInstructions,
boolean straightDirection,
boolean[] changed,
boolean isLocal) {
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) {
if (!isLocal && instruction instanceof SubroutineEnterInstruction) continue;
boolean isStart = straightDirection ? instruction instanceof SubroutineEnterInstruction : instruction instanceof SubroutineSinkInstruction;
if (!isLocal && isStart) continue;
Collection<Instruction> allPreviousInstructions;
Collection<Instruction> previousInstructions = straightDirection
? instruction.getPreviousInstructions()
: instruction.getNextInstructions();
if (instruction == enterInstruction && !previousSubGraphInstructions.isEmpty()) {
if (instruction == startInstruction && !previousSubGraphInstructions.isEmpty()) {
allPreviousInstructions = Lists.newArrayList(previousInstructions);
allPreviousInstructions.addAll(previousSubGraphInstructions);
}
@@ -87,7 +98,15 @@ public class JetControlFlowGraphTraverser<D> {
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
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);
@@ -116,12 +135,16 @@ public class JetControlFlowGraphTraverser<D> {
Pseudocode pseudocode,
InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
List<Instruction> instructions = pseudocode.getInstructions();
if (!straightDirection) {
instructions = Lists.newArrayList(instructions);
Collections.reverse(instructions);
}
for (Instruction instruction : instructions) {
if (instruction.isDead()) continue;
Pair<D, D> pair = dataMap.get(instruction);
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
}
Pair<D, D> pair = dataMap.get(instruction);
instructionDataAnalyzeStrategy.execute(instruction,
pair != null ? pair.getFirst() : null,
pair != null ? pair.getSecond() : null);
@@ -194,7 +194,7 @@ public class JetFlowInformationProvider {
final Pseudocode pseudocode = pseudocodeMap.get(subroutine);
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);
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> varWithValReassignErrorGenerated = Sets.newHashSet();
@@ -396,7 +396,7 @@ public class JetFlowInformationProvider {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
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
public void execute(Instruction instruction, Void enterData, Void exitData) {
if (instruction instanceof ReadValueInstruction) {
@@ -419,73 +419,50 @@ public class JetFlowInformationProvider {
public void markUnusedVariables(@NotNull JetElement subroutine) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
final Set<VariableDescriptor> usedVariables = Sets.newHashSet();
JetControlFlowGraphTraverser.<Void>create(pseudocode, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Void>() {
@Override
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
if (instruction instanceof ReadValueInstruction) {
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));
}
}
JetControlFlowGraphTraverser<Map<VariableDescriptor, VariableStatus>> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false);
final Collection<VariableDescriptor> declaredVariables = collectDeclaredVariables(subroutine);
Collection<VariableDescriptor> usedVariables = collectUsedVariables(pseudocode);
Map<VariableDescriptor, VariableStatus> sinkInstructionData = Maps.newHashMap();
for (VariableDescriptor usedVariable : usedVariables) {
sinkInstructionData.put(usedVariable, VariableStatus.UNUSED);
}
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>>() {
traverser.collectInformationFromInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableStatus>>() {
@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);
public Pair<Map<VariableDescriptor, VariableStatus>, Map<VariableDescriptor, VariableStatus>> execute(Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableStatus>> incomingEdgesData) {
Map<VariableDescriptor, VariableStatus> enterResult = Maps.newHashMap();
for (Map<VariableDescriptor, VariableStatus> edgeData : incomingEdgesData) {
for (Map.Entry<VariableDescriptor, VariableStatus> entry : edgeData.entrySet()) {
VariableDescriptor variableDescriptor = entry.getKey();
VariableStatus variableStatus = entry.getValue();
enterResult.put(variableDescriptor, variableStatus.merge(enterResult.get(variableDescriptor)));
}
}
else if (instruction instanceof WriteValueInstruction) {
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
if (variableDescriptor != null) {
exitResult.remove(variableDescriptor);
Map<VariableDescriptor, VariableStatus> exitResult = Maps.newHashMap(enterResult);
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true);
if (variableDescriptor != null) {
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);
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Set<VariableDescriptor>>() {
}, Collections.<VariableDescriptor, VariableStatus>emptyMap(), sinkInstructionData);
traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy<Map<VariableDescriptor, VariableStatus>>() {
@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;
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);
assert variableDeclarationElement instanceof JetDeclaration;
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
if (isLocal) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
if (variableDescriptor != null && declaredVariables.contains(variableDescriptor)) {
PsiElement variableDeclarationElement = trace.get(BindingContext.DESCRIPTOR_TO_DECLARATION, variableDescriptor);
assert variableDeclarationElement instanceof JetDeclaration;
boolean isLocal = JetPsiUtil.isLocal((JetDeclaration) variableDeclarationElement);
if (isLocal) {
if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
if (enterData.get(variableDescriptor) == VariableStatus.WRITTEN || enterData.get(variableDescriptor) == VariableStatus.UNUSED) {
if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) {
JetExpression right = ((JetBinaryExpression) element).getRight();
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
@Nullable
private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) {
VariableDescriptor variableDescriptor = null;
JetElement element = null;
if (instruction instanceof ReadValueInstruction) {
JetElement element = ((ReadValueInstruction) instruction).getElement();
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
element = ((ReadValueInstruction) instruction).getElement();
}
else if (instruction instanceof WriteValueInstruction) {
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
variableDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), lValue, onlyReference);
element = ((WriteValueInstruction) instruction).getlValue();
}
return variableDescriptor;
else if (instruction instanceof VariableDeclarationInstruction) {
element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
}
return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference);
}
private Collection<VariableDescriptor> collectUsedVariables(Pseudocode pseudocode) {
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
public void execute(Instruction instruction, Void enterData, Void exitData) {
VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false);
@@ -542,7 +551,7 @@ public class JetFlowInformationProvider {
assert pseudocode != null;
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
public void execute(Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) {
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<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) {
@Override
protected String makeMessageForA(@NotNull JetElement element) {
@@ -11,7 +11,7 @@ class C() {
}
fun f(): Unit {
var x: Int? = 1
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!>: Int? = 1
x = 1
x <!UNSAFE_INFIX_CALL!>+<!> 1
x <!UNSAFE_INFIX_CALL!>plus<!> 1
@@ -179,7 +179,7 @@ fun declarations(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) {
b = <!UNUSED_VALUE!>a<!>
}
@@ -17,7 +17,7 @@ class AClass() {
val x : Any? = 1
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) {
b = <!UNUSED_VALUE!>ns.y<!>
}
@@ -74,7 +74,7 @@ trait T {}
open class C {
fun foo() {
var <!UNUSED_VARIABLE!>t<!> : T? = null
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>t<!> : T? = null
if (this is T) {
t = <!UNUSED_VALUE!>this<!>
}
@@ -16,7 +16,7 @@ fun t1(b : Boolean) {
}
doSmth(<!UNINITIALIZED_VARIABLE!>u<!>)
var r: String
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>r<!>: String
if (b) {
r = "s"
}
@@ -63,10 +63,10 @@ fun t4(a: A, val b: A, var c: A) {
// reassigned vals
fun t1() {
val <!UNUSED_VARIABLE!>a<!> : Int = 1
val <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Int = 1
<!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<!>
}
@@ -1,7 +1,7 @@
namespace unused_variables
fun testSimpleCases() {
var i = 2
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 2
i = <!UNUSED_VALUE!>34<!>
i = 34
doSmth(i)
@@ -9,7 +9,7 @@ fun testSimpleCases() {
var j = 2
j = <!UNUSED_CHANGED_VALUE!>j++<!>
j = <!UNUSED_CHANGED_VALUE, UNUSED_VALUE!>j--<!>
j = <!UNUSED_VALUE, UNUSED_CHANGED_VALUE!>j--<!>
}
class IncDec() {
@@ -45,7 +45,7 @@ class MyTest() {
fun testSimple() {
a = "rro"
var <!UNUSED_VARIABLE!>i<!> = 1;
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>i<!> = 1;
i = <!UNUSED_VALUE!>34<!>;
i = <!UNUSED_VALUE!>456<!>;
}
@@ -62,7 +62,7 @@ class MyTest() {
}
fun testIf() {
var a : Any
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>a<!> : Any
if (1 < 2) {
a = 23
}
@@ -91,7 +91,7 @@ class MyTest() {
}
fun testInnerFunctions() {
var <!UNUSED_VARIABLE!>y<!> = 1
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>y<!> = 1
fun foo() {
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) {
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);
}
}
@@ -11,7 +11,7 @@ class C() {
}
fun f(): Unit {
var x: Int? = 1
var <warning>x</warning>: Int? = 1
x = 1
x <error>+</error> 1
x <error>plus</error> 1