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;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
@@ -22,7 +23,7 @@ public class JetFlowInformationProvider {
private final Map<JetElement, Pseudocode> pseudocodeMap;
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;
final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration);
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();
for (Instruction instruction : instructions) {
dataMap.put(instruction, initialDataValue);
}
initializeDataMap(dataMap, pseudocode, initialDataValue);
boolean changed = true;
while (changed) {
changed = false;
for (Instruction instruction : instructions) {
D previousDataValue = dataMap.get(instruction);
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);
}
}
boolean[] changed = new boolean[1];
changed[0] = true;
while (changed[0]) {
changed[0] = false;
traverseSubGraph(pseudocode, instructionsMergeHandler, Collections.<Instruction>emptyList(), straightDirection, dataMap, changed);
}
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) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
@@ -292,16 +322,16 @@ public class JetFlowInformationProvider {
}
if (instruction instanceof WriteValueInstruction) {
JetElement element = ((WriteValueInstruction) instruction).getElement();
DeclarationDescriptor descriptor = null;
if (element instanceof JetBinaryExpression) {
JetExpression left = ((JetBinaryExpression) element).getLeft();
if (left instanceof JetSimpleNameExpression) {
descriptor = trace.get(BindingContext.REFERENCE_TARGET, (JetSimpleNameExpression) left);
}
JetElement lValue = ((WriteValueInstruction) instruction).getlValue();
if (lValue instanceof JetProperty) {
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, lValue);
}
else if (element instanceof JetProperty) {
descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
else if (lValue instanceof JetSimpleNameExpression) {
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) {
initializedVariables.add((LocalVariableDescriptor) descriptor);
@@ -310,36 +340,52 @@ public class JetFlowInformationProvider {
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) {
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));
}
}
}
if (instruction instanceof LocalDeclarationInstruction) {
traverseInstructionGraphAndReportErrors(((LocalDeclarationInstruction) instruction).getBody().getInstructions(), dataMap, instructionDataAnalyzer);
}
instructionDataAnalyzer.analyze(instruction, dataMap);
}
}
interface InstructionsMergeHandler<D> {
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;
}
@NotNull
public JetElement getlValue() {
return lValue;
}
@Override
public void accept(InstructionVisitor visitor) {
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);
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);
SimpleDiagnosticFactory UNREACHABLE_CODE = SimpleDiagnosticFactory.create(ERROR, "Unreachable code");
+2 -4
View File
@@ -6,8 +6,7 @@ class B() : A() {
fun bar() {}
}
fun f9() {
val a : A?
fun f9(a : A?) {
a?.foo()
a?.<error descr="Unresolved reference: bar">bar</error>()
if (a is B) {
@@ -30,8 +29,7 @@ fun f9() {
<info descr="Automatically cast to A">a</info>.foo()
}
fun f10() {
val a : A?
fun f10(a : A?) {
if (!(a is B)) {
return;
}
@@ -1,16 +1,16 @@
fun box() {
val a : C
val a : C = C()
a.foo()
}
open class A {
open class A() {
open fun foo() {}
}
open class B : A {
open class B() : A() {
override fun foo() {}
}
open class C : B {
open class C() : B() {
override fun foo() {}
}