Added 'uninitialized variables' check
This commit is contained in:
@@ -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,36 +248,65 @@ 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);
|
||||||
assert pseudocode != null;
|
assert pseudocode != null;
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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() {}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user