KT-776 Wrong detection of unreachable code
KT-1001 Argument 2 for @NotNull parameter of JetFlowInformationProvider.checkIsInitialized must not be null
This commit is contained in:
@@ -19,6 +19,7 @@ public interface JetControlFlowBuilder {
|
||||
|
||||
void bindLabel(@NotNull Label label);
|
||||
void allowDead();
|
||||
void stopAllowDead();
|
||||
|
||||
// Jumps
|
||||
void jump(@NotNull Label label);
|
||||
|
||||
@@ -43,6 +43,12 @@ public class JetControlFlowBuilderAdapter implements JetControlFlowBuilder {
|
||||
builder.allowDead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllowDead() {
|
||||
assert builder != null;
|
||||
builder.stopAllowDead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jump(@NotNull Label label) {
|
||||
assert builder != null;
|
||||
|
||||
@@ -8,10 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.cfg.pseudocode.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author svtk
|
||||
@@ -140,7 +137,6 @@ public class JetControlFlowGraphTraverser<D> {
|
||||
Collections.reverse(instructions);
|
||||
}
|
||||
for (Instruction instruction : instructions) {
|
||||
if (instruction.isDead()) continue;
|
||||
if (lookInside && instruction instanceof LocalDeclarationInstruction) {
|
||||
traverseAndAnalyzeInstructionGraph(((LocalDeclarationInstruction) instruction).getBody(), instructionDataAnalyzeStrategy);
|
||||
}
|
||||
|
||||
@@ -408,6 +408,7 @@ public class JetControlFlowProcessor {
|
||||
builder.exitTryFinally();
|
||||
value(finallyBlock.getFinalExpression(), inCondition);
|
||||
}
|
||||
builder.stopAllowDead();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -252,7 +252,10 @@ public class JetFlowInformationProvider {
|
||||
analyzeLocalDeclarations(processLocalDeclaration, pseudocode);
|
||||
}
|
||||
|
||||
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor, @NotNull JetElement element, @NotNull VariableInitializers variableInitializers, @NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated) {
|
||||
private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor,
|
||||
@NotNull JetElement element,
|
||||
@NotNull VariableInitializers variableInitializers,
|
||||
@NotNull Collection<VariableDescriptor> varWithUninitializedErrorGenerated) {
|
||||
if (!(element instanceof JetSimpleNameExpression)) return;
|
||||
|
||||
boolean isInitialized = variableInitializers.isInitialized();
|
||||
@@ -272,7 +275,10 @@ public class JetFlowInformationProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkValReassignment(@NotNull VariableDescriptor variableDescriptor, @NotNull JetExpression expression, @NotNull VariableInitializers enterInitializers, @NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated) {
|
||||
private boolean checkValReassignment(@NotNull VariableDescriptor variableDescriptor,
|
||||
@NotNull JetExpression expression,
|
||||
@NotNull VariableInitializers enterInitializers,
|
||||
@NotNull Collection<VariableDescriptor> varWithValReassignErrorGenerated) {
|
||||
boolean isInitializedNotHere = enterInitializers.isInitialized();
|
||||
Set<JetElement> possibleLocalInitializers = enterInitializers.getPossibleLocalInitializers();
|
||||
if (possibleLocalInitializers.size() == 1) {
|
||||
|
||||
@@ -20,6 +20,4 @@ public interface Instruction {
|
||||
Collection<Instruction> getNextInstructions();
|
||||
|
||||
void accept(InstructionVisitor visitor);
|
||||
|
||||
boolean isDead();
|
||||
}
|
||||
|
||||
+8
-1
@@ -190,7 +190,7 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
|
||||
bindLabel(getExitPoint(subroutine));
|
||||
pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, "<END>"));
|
||||
bindLabel(error);
|
||||
add(new SubroutineExitInstruction(subroutine, "<ERROR>"));
|
||||
pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, "<ERROR>"));
|
||||
bindLabel(sink);
|
||||
pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, "<SINK>"));
|
||||
elementToBlockInfo.remove(subroutine);
|
||||
@@ -266,6 +266,13 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
|
||||
pseudocode.allowDead(allowedDeadLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAllowDead() {
|
||||
Label allowedDeadLabel = createUnboundLabel();
|
||||
bindLabel(allowedDeadLabel);
|
||||
pseudocode.stopAllowDead(allowedDeadLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nondeterministicJump(Label label) {
|
||||
handleJumpInsideTryFinally(label);
|
||||
|
||||
@@ -56,12 +56,16 @@ public class Pseudocode {
|
||||
|
||||
private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>();
|
||||
private final List<Instruction> instructions = new ArrayList<Instruction>();
|
||||
private List<Instruction> deadInstructions;
|
||||
|
||||
private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
|
||||
private final List<PseudocodeLabel> allowedDeadLabels = new ArrayList<PseudocodeLabel>();
|
||||
private final List<PseudocodeLabel> stopAllowDeadLabels = new ArrayList<PseudocodeLabel>();
|
||||
|
||||
private final JetElement correspondingElement;
|
||||
private SubroutineExitInstruction exitInstruction;
|
||||
private SubroutineSinkInstruction sinkInstruction;
|
||||
private SubroutineExitInstruction errorInstruction;
|
||||
private boolean postPrecessed = false;
|
||||
|
||||
public Pseudocode(JetElement correspondingElement) {
|
||||
@@ -81,6 +85,10 @@ public class Pseudocode {
|
||||
public void allowDead(Label label) {
|
||||
allowedDeadLabels.add((PseudocodeLabel) label);
|
||||
}
|
||||
|
||||
public void stopAllowDead(Label label) {
|
||||
stopAllowDeadLabels.add((PseudocodeLabel) label);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Instruction> getInstructions() {
|
||||
@@ -95,10 +103,17 @@ public class Pseudocode {
|
||||
|
||||
@NotNull
|
||||
public List<Instruction> getDeadInstructions() {
|
||||
List<Instruction> deadInstructions = Lists.newArrayList();
|
||||
for (Instruction instruction : instructions) {
|
||||
if (instruction.isDead()) {
|
||||
deadInstructions.add(instruction);
|
||||
if (deadInstructions != null) {
|
||||
return deadInstructions;
|
||||
}
|
||||
deadInstructions = Lists.newArrayList();
|
||||
Collection<Instruction> allowedDeadInstructions = collectAllowedDeadInstructions();
|
||||
|
||||
for (Instruction instruction : mutableInstructionList) {
|
||||
if (((InstructionImpl)instruction).isDead()) {
|
||||
if (!allowedDeadInstructions.contains(instruction)) {
|
||||
deadInstructions.add(instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
return deadInstructions;
|
||||
@@ -122,6 +137,12 @@ public class Pseudocode {
|
||||
this.sinkInstruction = sinkInstruction;
|
||||
}
|
||||
|
||||
public void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
|
||||
addInstruction(errorInstruction);
|
||||
assert this.errorInstruction == null;
|
||||
this.errorInstruction = errorInstruction;
|
||||
}
|
||||
|
||||
public void addInstruction(Instruction instruction) {
|
||||
mutableInstructionList.add(instruction);
|
||||
instruction.setOwner(this);
|
||||
@@ -151,88 +172,107 @@ public class Pseudocode {
|
||||
if (postPrecessed) return;
|
||||
postPrecessed = true;
|
||||
for (int i = 0, instructionsSize = mutableInstructionList.size(); i < instructionsSize; i++) {
|
||||
Instruction instruction = mutableInstructionList.get(i);
|
||||
final int currentPosition = i;
|
||||
instruction.accept(new InstructionVisitor() {
|
||||
@Override
|
||||
public void visitInstructionWithNext(InstructionWithNext instruction) {
|
||||
instruction.setNext(getNextPosition(currentPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJump(AbstractJumpInstruction instruction) {
|
||||
instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
|
||||
instruction.setNext(getNextPosition(currentPosition));
|
||||
List<Label> targetLabels = instruction.getTargetLabels();
|
||||
for (Label targetLabel : targetLabels) {
|
||||
instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConditionalJump(ConditionalJumpInstruction instruction) {
|
||||
Instruction nextInstruction = getNextPosition(currentPosition);
|
||||
Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
|
||||
if (instruction.onTrue()) {
|
||||
instruction.setNextOnFalse(nextInstruction);
|
||||
instruction.setNextOnTrue(jumpTarget);
|
||||
}
|
||||
else {
|
||||
instruction.setNextOnFalse(jumpTarget);
|
||||
instruction.setNextOnTrue(nextInstruction);
|
||||
}
|
||||
visitJump(instruction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalDeclarationInstruction(LocalDeclarationInstruction instruction) {
|
||||
instruction.getBody().postProcess();
|
||||
instruction.setNext(getSinkInstruction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction) {
|
||||
throw new UnsupportedOperationException(instruction.toString());
|
||||
}
|
||||
});
|
||||
processInstruction(mutableInstructionList.get(i), i);
|
||||
}
|
||||
getExitInstruction().setSink(getSinkInstruction());
|
||||
Set<Instruction> allowedDeadStartInstructions = prepareAllowedDeadInstructions();
|
||||
Set<Instruction> reachableInstructions = collectReachableInstructions();
|
||||
for (Instruction instruction : mutableInstructionList) {
|
||||
if (reachableInstructions.contains(instruction)) {
|
||||
instructions.add(instruction);
|
||||
}
|
||||
}
|
||||
markDeadInstructions();
|
||||
Collection<Instruction> allowedDeadInstructions = collectAllowedDeadInstructions(allowedDeadStartInstructions);
|
||||
instructions.addAll(mutableInstructionList);
|
||||
instructions.removeAll(allowedDeadInstructions);
|
||||
}
|
||||
|
||||
private void processInstruction(Instruction instruction, final int currentPosition) {
|
||||
instruction.accept(new InstructionVisitor() {
|
||||
@Override
|
||||
public void visitInstructionWithNext(InstructionWithNext instruction) {
|
||||
instruction.setNext(getNextPosition(currentPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJump(AbstractJumpInstruction instruction) {
|
||||
instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
|
||||
instruction.setNext(getNextPosition(currentPosition));
|
||||
List<Label> targetLabels = instruction.getTargetLabels();
|
||||
for (Label targetLabel : targetLabels) {
|
||||
instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConditionalJump(ConditionalJumpInstruction instruction) {
|
||||
Instruction nextInstruction = getNextPosition(currentPosition);
|
||||
Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
|
||||
if (instruction.onTrue()) {
|
||||
instruction.setNextOnFalse(nextInstruction);
|
||||
instruction.setNextOnTrue(jumpTarget);
|
||||
}
|
||||
else {
|
||||
instruction.setNextOnFalse(jumpTarget);
|
||||
instruction.setNextOnTrue(nextInstruction);
|
||||
}
|
||||
visitJump(instruction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalDeclarationInstruction(LocalDeclarationInstruction instruction) {
|
||||
instruction.getBody().postProcess();
|
||||
instruction.setNext(getSinkInstruction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubroutineExit(SubroutineExitInstruction instruction) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInstruction(Instruction instruction) {
|
||||
throw new UnsupportedOperationException(instruction.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Instruction> collectReachableInstructions() {
|
||||
Set<Instruction> visited = Sets.newHashSet();
|
||||
traverseNextInstructions(getEnterInstruction(), visited);
|
||||
if (!visited.contains(getExitInstruction())) {
|
||||
visited.add(getExitInstruction());
|
||||
}
|
||||
if (!visited.contains(errorInstruction)) {
|
||||
visited.add(errorInstruction);
|
||||
}
|
||||
if (!visited.contains(getSinkInstruction())) {
|
||||
visited.add(getSinkInstruction());
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
private void traverseNextInstructions(Instruction instruction, Set<Instruction> visited) {
|
||||
if (visited.contains(instruction)) return;
|
||||
visited.add(instruction);
|
||||
for (Instruction nextInstruction : instruction.getNextInstructions()) {
|
||||
traverseNextInstructions(nextInstruction, visited);
|
||||
}
|
||||
}
|
||||
|
||||
private void markDeadInstructions() {
|
||||
boolean hasRemovedInstruction = true;
|
||||
Collection<Instruction> processedInstructions = Sets.newHashSet();
|
||||
while (hasRemovedInstruction) {
|
||||
hasRemovedInstruction = false;
|
||||
for (Instruction instruction : mutableInstructionList) {
|
||||
if (!(instruction instanceof SubroutineEnterInstruction || instruction instanceof SubroutineExitInstruction || instruction instanceof SubroutineSinkInstruction) &&
|
||||
instruction.getPreviousInstructions().isEmpty() && !processedInstructions.contains(instruction)) {
|
||||
hasRemovedInstruction = true;
|
||||
for (Instruction nextInstruction : instruction.getNextInstructions()) {
|
||||
nextInstruction.getPreviousInstructions().remove(instruction);
|
||||
}
|
||||
((InstructionImpl)instruction).die();
|
||||
processedInstructions.add(instruction);
|
||||
Set<Instruction> instructionSet = Sets.newHashSet(instructions);
|
||||
for (Instruction instruction : mutableInstructionList) {
|
||||
if (!instructionSet.contains(instruction)) {
|
||||
((InstructionImpl)instruction).die();
|
||||
for (Instruction nextInstruction : instruction.getNextInstructions()) {
|
||||
nextInstruction.getPreviousInstructions().remove(instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,27 +283,43 @@ public class Pseudocode {
|
||||
Set<Instruction> allowedDeadStartInstructions = Sets.newHashSet();
|
||||
for (PseudocodeLabel allowedDeadLabel : allowedDeadLabels) {
|
||||
Instruction allowedDeadInstruction = getJumpTarget(allowedDeadLabel);
|
||||
if (allowedDeadInstruction.getPreviousInstructions().isEmpty()) {
|
||||
if (((InstructionImpl)allowedDeadInstruction).isDead()) {
|
||||
allowedDeadStartInstructions.add(allowedDeadInstruction);
|
||||
}
|
||||
}
|
||||
return allowedDeadStartInstructions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<Instruction> prepareStopAllowedDeadInstructions() {
|
||||
Set<Instruction> stopAllowedDeadInstructions = Sets.newHashSet();
|
||||
for (PseudocodeLabel stopAllowedDeadLabel : stopAllowDeadLabels) {
|
||||
Instruction stopAllowDeadInsruction = getJumpTarget(stopAllowedDeadLabel);
|
||||
if (((InstructionImpl)stopAllowDeadInsruction).isDead()) {
|
||||
stopAllowedDeadInstructions.add(stopAllowDeadInsruction);
|
||||
}
|
||||
}
|
||||
return stopAllowedDeadInstructions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Collection<Instruction> collectAllowedDeadInstructions(@NotNull Set<Instruction> allowedDeadStartInstructions) {
|
||||
private Collection<Instruction> collectAllowedDeadInstructions() {
|
||||
Set<Instruction> allowedDeadStartInstructions = prepareAllowedDeadInstructions();
|
||||
Set<Instruction> stopAllowDeadInstructions = prepareStopAllowedDeadInstructions();
|
||||
Set<Instruction> allowedDeadInstructions = Sets.newHashSet();
|
||||
|
||||
for (Instruction allowedDeadStartInstruction : allowedDeadStartInstructions) {
|
||||
collectAllowedDeadInstructions(allowedDeadStartInstruction, allowedDeadInstructions);
|
||||
collectAllowedDeadInstructions(allowedDeadStartInstruction, allowedDeadInstructions, stopAllowDeadInstructions);
|
||||
}
|
||||
return allowedDeadInstructions;
|
||||
}
|
||||
|
||||
private void collectAllowedDeadInstructions(Instruction allowedDeadInstruction, Set<Instruction> instructionSet) {
|
||||
if (allowedDeadInstruction.isDead()) {
|
||||
private void collectAllowedDeadInstructions(Instruction allowedDeadInstruction, Set<Instruction> instructionSet, Set<Instruction> stopAllowDeadInstructions) {
|
||||
if (stopAllowDeadInstructions.contains(allowedDeadInstruction)) return;
|
||||
if (((InstructionImpl)allowedDeadInstruction).isDead()) {
|
||||
instructionSet.add(allowedDeadInstruction);
|
||||
for (Instruction instruction : allowedDeadInstruction.getNextInstructions()) {
|
||||
collectAllowedDeadInstructions(instruction, instructionSet);
|
||||
collectAllowedDeadInstructions(instruction, instructionSet, stopAllowDeadInstructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user