Added 'uninitialized variables' check

This commit is contained in:
svtk
2011-10-21 15:43:15 +04:00
parent f0db1e8a21
commit 54bd401026
5 changed files with 114 additions and 65 deletions
@@ -1,5 +1,6 @@
package org.jetbrains.jet.lang.cfg; package org.jetbrains.jet.lang.cfg;
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;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -22,7 +23,7 @@ public class JetFlowInformationProvider {
private final Map<JetElement, Pseudocode> pseudocodeMap; private final Map<JetElement, Pseudocode> pseudocodeMap;
private BindingTrace trace; private BindingTrace trace;
public JetFlowInformationProvider(@NotNull JetElement declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) { public JetFlowInformationProvider(@NotNull JetDeclaration declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) {
this.trace = trace; this.trace = trace;
final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration); final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration);
pseudocodeMap = new HashMap<JetElement, Pseudocode>(); pseudocodeMap = new HashMap<JetElement, Pseudocode>();
@@ -247,35 +248,64 @@ public class JetFlowInformationProvider {
// } // }
// } // }
private <D> Map<Instruction, D> traverseInstructionGraphUntilFactsStabilization(Collection<Instruction> instructions, InstructionsMergeHandler<D> instructionsMergeHandler, D initialDataValue, boolean straightDirection) { private <D> Map<Instruction, D> traverseInstructionGraphUntilFactsStabilization(Pseudocode pseudocode, InstructionsMergeHandler<D> instructionsMergeHandler, D initialDataValue, boolean straightDirection) {
Map<Instruction, D> dataMap = Maps.newHashMap(); Map<Instruction, D> dataMap = Maps.newHashMap();
for (Instruction instruction : instructions) { initializeDataMap(dataMap, pseudocode, initialDataValue);
dataMap.put(instruction, initialDataValue);
}
boolean changed = true; boolean[] changed = new boolean[1];
while (changed) { changed[0] = true;
changed = false; while (changed[0]) {
for (Instruction instruction : instructions) { changed[0] = false;
D previousDataValue = dataMap.get(instruction); traverseSubGraph(pseudocode, instructionsMergeHandler, Collections.<Instruction>emptyList(), straightDirection, dataMap, changed);
Collection<Instruction> previousInstructions = straightDirection
? instruction.getPreviousInstructions()
: instruction.getNextInstructions();
Collection<D> incomingEdgesData = Sets.newHashSet();
for (Instruction previousInstruction : previousInstructions) {
incomingEdgesData.add(dataMap.get(previousInstruction));
}
D mergedData = instructionsMergeHandler.merge(instruction, previousDataValue, incomingEdgesData);
if (!mergedData.equals(previousDataValue)) {
changed = true;
dataMap.put(instruction, mergedData);
}
}
} }
return dataMap; return dataMap;
} }
private <D> void initializeDataMap(Map<Instruction, D> dataMap, Pseudocode pseudocode, D initialDataValue) {
List<Instruction> instructions = pseudocode.getInstructions();
for (Instruction instruction : instructions) {
dataMap.put(instruction, initialDataValue);
if (instruction instanceof LocalDeclarationInstruction) {
initializeDataMap(dataMap, ((LocalDeclarationInstruction) instruction).getBody(), initialDataValue);
}
}
}
private <D> void traverseSubGraph(Pseudocode pseudocode, InstructionsMergeHandler<D> instructionsMergeHandler, Collection<Instruction> previousSubGraphInstructions, boolean straightDirection, Map<Instruction, D> dataMap, boolean[] changed) {
List<Instruction> instructions = pseudocode.getInstructions();
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
for (Instruction instruction : instructions) {
Collection<Instruction> allPreviousInstructions;
Collection<Instruction> previousInstructions = straightDirection
? instruction.getPreviousInstructions()
: instruction.getNextInstructions();
if (instruction == enterInstruction && !previousSubGraphInstructions.isEmpty()) {
allPreviousInstructions = Lists.newArrayList(previousInstructions);
allPreviousInstructions.addAll(previousSubGraphInstructions);
}
else {
allPreviousInstructions = previousInstructions;
}
if (instruction instanceof LocalDeclarationInstruction) {
Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
traverseSubGraph(subroutinePseudocode, instructionsMergeHandler, previousInstructions, straightDirection, dataMap, changed);
}
D previousDataValue = dataMap.get(instruction);
Collection<D> incomingEdgesData = Sets.newHashSet();
for (Instruction previousInstruction : allPreviousInstructions) {
incomingEdgesData.add(dataMap.get(previousInstruction));
}
D mergedData = instructionsMergeHandler.merge(instruction, previousDataValue, incomingEdgesData);
if (!mergedData.equals(previousDataValue)) {
changed[0] = true;
dataMap.put(instruction, mergedData);
}
}
}
public void markUninitializedVariables(@NotNull JetElement subroutine) { public void markUninitializedVariables(@NotNull JetElement subroutine) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine); Pseudocode pseudocode = pseudocodeMap.get(subroutine);
@@ -292,16 +322,16 @@ public class JetFlowInformationProvider {
} }
if (instruction instanceof WriteValueInstruction) { if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
DeclarationDescriptor descriptor = null; DeclarationDescriptor descriptor = null;
if (element instanceof JetBinaryExpression) { JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
JetExpression left = ((JetBinaryExpression) element).getLeft(); if (lValue instanceof JetProperty) {
if (left instanceof JetSimpleNameExpression) { descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) left);
}
} }
else if (element instanceof JetProperty) { else if (lValue instanceof JetSimpleNameExpression) {
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element); descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) lValue);
}
else if (lValue instanceof JetParameter) {
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
} }
if (descriptor instanceof LocalVariableDescriptor) { if (descriptor instanceof LocalVariableDescriptor) {
initializedVariables.add((LocalVariableDescriptor) descriptor); initializedVariables.add((LocalVariableDescriptor) descriptor);
@@ -310,36 +340,52 @@ public class JetFlowInformationProvider {
return initializedVariables; return initializedVariables;
} }
}; };
Map<Instruction, Set<LocalVariableDescriptor>> dataMap = traverseInstructionGraphUntilFactsStabilization(instructions, instructionsMergeHandler, Collections.<LocalVariableDescriptor>emptySet(), true); Map<Instruction, Set<LocalVariableDescriptor>> dataMap = traverseInstructionGraphUntilFactsStabilization(pseudocode, instructionsMergeHandler, Collections.<LocalVariableDescriptor>emptySet(), true);
InstructionDataAnalyzer instructionDataAnalyzer = new InstructionDataAnalyzer<Set<LocalVariableDescriptor>>() {
@Override
public void analyze(Instruction instruction, Map<Instruction, Set<LocalVariableDescriptor>> dataMap) {
Set<LocalVariableDescriptor> initializedVariables = dataMap.get(instruction);
if (instruction instanceof ReadValueInstruction) {
JetElement element = ((ReadValueInstruction) instruction).getElement();
if (element instanceof JetSimpleNameExpression) {
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
if (descriptor instanceof LocalVariableDescriptor) {
if (!initializedVariables.contains(descriptor)) {
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, descriptor));
}
}
}
}
else if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
if (element instanceof JetSimpleNameExpression) {
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
if (descriptor instanceof LocalVariableDescriptor) {
if (initializedVariables.contains(descriptor) && ((LocalVariableDescriptor) descriptor).isVar()) {
trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, descriptor));
}
}
}
}
}
};
traverseInstructionGraphAndReportErrors(instructions, dataMap, instructionDataAnalyzer);
}
private void traverseInstructionGraphAndReportErrors(Collection<Instruction> instructions, Map<Instruction, Set<LocalVariableDescriptor>> dataMap, InstructionDataAnalyzer<Set<LocalVariableDescriptor>> instructionDataAnalyzer) {
for (Instruction instruction : instructions) { for (Instruction instruction : instructions) {
Set<LocalVariableDescriptor> initializedVariables = dataMap.get(instruction); if (instruction instanceof LocalDeclarationInstruction) {
if (instruction instanceof ReadValueInstruction) { traverseInstructionGraphAndReportErrors(((LocalDeclarationInstruction) instruction).getBody().getInstructions(), dataMap, instructionDataAnalyzer);
JetElement element = ((ReadValueInstruction) instruction).getElement();
if (element instanceof JetSimpleNameExpression) {
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
if (descriptor instanceof LocalVariableDescriptor) {
if (!initializedVariables.contains(descriptor)) {
trace.report(Errors.UNINITIALIZED_VARIABLE.on((JetSimpleNameExpression) element, descriptor));
}
}
}
}
else if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
if (element instanceof JetSimpleNameExpression) {
DeclarationDescriptor descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) element);
if (descriptor instanceof LocalVariableDescriptor) {
if (initializedVariables.contains(descriptor) && ((LocalVariableDescriptor) descriptor).isVar()) {
trace.report(Errors.VAL_REASSIGNMENT.on((JetSimpleNameExpression) element, descriptor));
}
}
}
} }
instructionDataAnalyzer.analyze(instruction, dataMap);
} }
} }
interface InstructionsMergeHandler<D> { interface InstructionsMergeHandler<D> {
D merge(Instruction instruction, D previousDataValue, Collection<D> incomingEdgesData); D merge(Instruction instruction, D previousDataValue, Collection<D> incomingEdgesData);
} }
interface InstructionDataAnalyzer<D> {
void analyze(Instruction instruction, Map<Instruction, D> dataMap);
}
} }
@@ -16,6 +16,11 @@ public class WriteValueInstruction extends InstructionWithNext {
this.lValue = lValue; this.lValue = lValue;
} }
@NotNull
public JetElement getlValue() {
return lValue;
}
@Override @Override
public void accept(InstructionVisitor visitor) { public void accept(InstructionVisitor visitor) {
visitor.visitWriteValue(this); visitor.visitWriteValue(this);
@@ -142,7 +142,7 @@ public interface Errors {
}; };
PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, CallableMemberDescriptor, CallableMemberDescriptor, DeclarationDescriptor> VIRTUAL_MEMBER_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "''{0}'' hides ''{1}'' in class {2} and needs 'override' modifier", DescriptorRenderer.TEXT); PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, CallableMemberDescriptor, CallableMemberDescriptor, DeclarationDescriptor> VIRTUAL_MEMBER_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "''{0}'' hides ''{1}'' in class {2} and needs 'override' modifier", DescriptorRenderer.TEXT);
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 uninitialized", NAME);
PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME); PsiElementOnlyDiagnosticFactory1<JetSimpleNameExpression, DeclarationDescriptor> VAL_REASSIGNMENT = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Val can not be reassigned", NAME);
SimpleDiagnosticFactory UNREACHABLE_CODE = SimpleDiagnosticFactory.create(ERROR, "Unreachable code"); SimpleDiagnosticFactory UNREACHABLE_CODE = SimpleDiagnosticFactory.create(ERROR, "Unreachable code");
+2 -4
View File
@@ -6,8 +6,7 @@ class B() : A() {
fun bar() {} fun bar() {}
} }
fun f9() { fun f9(a : A?) {
val a : A?
a?.foo() a?.foo()
a?.<error descr="Unresolved reference: bar">bar</error>() a?.<error descr="Unresolved reference: bar">bar</error>()
if (a is B) { if (a is B) {
@@ -30,8 +29,7 @@ fun f9() {
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to A">a</info>.foo()
} }
fun f10() { fun f10(a : A?) {
val a : A?
if (!(a is B)) { if (!(a is B)) {
return; return;
} }
@@ -1,16 +1,16 @@
fun box() { fun box() {
val a : C val a : C = C()
a.foo() a.foo()
} }
open class A { open class A() {
open fun foo() {} open fun foo() {}
} }
open class B : A { open class B() : A() {
override fun foo() {} override fun foo() {}
} }
open class C : B { open class C() : B() {
override fun foo() {} override fun foo() {}
} }