Basic tests for dead code detection

This commit is contained in:
Andrey Breslav
2011-04-13 17:48:51 +04:00
parent 69ef281d47
commit 96526603e4
17 changed files with 164 additions and 26 deletions
-1
View File
@@ -16,7 +16,6 @@
<option name="VM_PARAMETERS" value="" /> <option name="VM_PARAMETERS" value="" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" /> <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
<option name="FORK_MODE" />
<option name="ENV_VARIABLES" /> <option name="ENV_VARIABLES" />
<option name="PASS_PARENT_ENVS" value="true" /> <option name="PASS_PARENT_ENVS" value="true" />
<option name="TEST_SEARCH_SCOPE"> <option name="TEST_SEARCH_SCOPE">
@@ -3,7 +3,6 @@ package org.jetbrains.jet.lang.cfg;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFunction;
import java.util.Collection; import java.util.Collection;
@@ -13,10 +12,16 @@ import java.util.Collection;
public interface JetFlowInformationProvider { public interface JetFlowInformationProvider {
JetFlowInformationProvider ERROR = new JetFlowInformationProvider() { JetFlowInformationProvider ERROR = new JetFlowInformationProvider() {
@Override @Override
public void collectReturnedInformation(@NotNull JetFunction function, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) { public void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
throw new UnsupportedOperationException();
}
@Override
public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
}; };
void collectReturnedInformation(@NotNull JetFunction function, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit); void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit);
void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements);
} }
@@ -1,7 +1,11 @@
package org.jetbrains.jet.lang.cfg.pseudocode; package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.Label; import org.jetbrains.jet.lang.cfg.Label;
import java.util.Collection;
import java.util.Collections;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -21,6 +25,12 @@ public abstract class AbstractJumpInstruction extends Instruction {
return resolvedTarget; return resolvedTarget;
} }
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
return Collections.singleton(getResolvedTarget());
}
public void setResolvedTarget(Instruction resolvedTarget) { public void setResolvedTarget(Instruction resolvedTarget) {
this.resolvedTarget = outgoingEdgeTo(resolvedTarget); this.resolvedTarget = outgoingEdgeTo(resolvedTarget);
} }
@@ -1,7 +1,11 @@
package org.jetbrains.jet.lang.cfg.pseudocode; package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.Label; import org.jetbrains.jet.lang.cfg.Label;
import java.util.Arrays;
import java.util.Collection;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -35,6 +39,12 @@ public class ConditionalJumpInstruction extends AbstractJumpInstruction {
this.nextOnFalse = outgoingEdgeTo(nextOnFalse); this.nextOnFalse = outgoingEdgeTo(nextOnFalse);
} }
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
return Arrays.asList(getNextOnFalse(), getNextOnTrue());
}
@Override @Override
public void accept(InstructionVisitor visitor) { public void accept(InstructionVisitor visitor) {
visitor.visitConditionalJump(this); visitor.visitConditionalJump(this);
@@ -1,5 +1,6 @@
package org.jetbrains.jet.lang.cfg.pseudocode; package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
@@ -15,6 +16,9 @@ public abstract class Instruction {
return previousInstructions; return previousInstructions;
} }
@NotNull
public abstract Collection<Instruction> getNextInstructions();
@Nullable @Nullable
protected Instruction outgoingEdgeTo(@Nullable Instruction target) { protected Instruction outgoingEdgeTo(@Nullable Instruction target) {
if (target != null) { if (target != null) {
@@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetElement;
import java.util.Collection;
import java.util.Collections;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -17,6 +20,12 @@ public abstract class InstructionWithNext extends JetElementInstruction {
return next; return next;
} }
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
return Collections.singleton(next);
}
public void setNext(Instruction next) { public void setNext(Instruction next) {
this.next = outgoingEdgeTo(next); this.next = outgoingEdgeTo(next);
} }
@@ -1,7 +1,11 @@
package org.jetbrains.jet.lang.cfg.pseudocode; package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.Label; import org.jetbrains.jet.lang.cfg.Label;
import java.util.Arrays;
import java.util.Collection;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -26,6 +30,12 @@ public class NondeterministicJumpInstruction extends AbstractJumpInstruction {
this.next = next; this.next = next;
} }
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
return Arrays.asList(getResolvedTarget(), getNext());
}
@Override @Override
public String toString() { public String toString() {
return "jmp?(" + getTargetLabel().getName() + ")"; return "jmp?(" + getTargetLabel().getName() + ")";
@@ -69,6 +69,11 @@ public class Pseudocode {
return label; return label;
} }
@NotNull
public Collection<Instruction> getInstructions() {
return instructions;
}
public void addInstruction(Instruction instruction) { public void addInstruction(Instruction instruction) {
instructions.add(instruction); instructions.add(instruction);
if (instruction instanceof SubroutineExitInstruction) { if (instruction instanceof SubroutineExitInstruction) {
@@ -83,6 +88,11 @@ public class Pseudocode {
return exitInstruction; return exitInstruction;
} }
@NotNull
public SubroutineEnterInstruction getEnterInstruction() {
return (SubroutineEnterInstruction) instructions.get(0);
}
public void bindLabel(Label label) { public void bindLabel(Label label) {
((PseudocodeLabel) label).setTargetInstructionIndex(instructions.size()); ((PseudocodeLabel) label).setTargetInstructionIndex(instructions.size());
} }
@@ -27,6 +27,6 @@ public class ReturnNoValueInstruction extends AbstractJumpInstruction {
@Override @Override
public String toString() { public String toString() {
return "ret"; return "ret " + getTargetLabel();
} }
} }
@@ -18,6 +18,6 @@ public class ReturnValueInstruction extends AbstractJumpInstruction {
@Override @Override
public String toString() { public String toString() {
return "ret(*)"; return "ret(*) " + getTargetLabel();
} }
} }
@@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetElement;
import java.util.Collection;
import java.util.Collections;
/** /**
* @author abreslav * @author abreslav
*/ */
@@ -17,6 +20,12 @@ public class SubroutineExitInstruction extends Instruction {
return subroutine; return subroutine;
} }
@NotNull
@Override
public Collection<Instruction> getNextInstructions() {
return Collections.emptyList();
}
@Override @Override
public void accept(InstructionVisitor visitor) { public void accept(InstructionVisitor visitor) {
visitor.visitSubroutineExit(this); visitor.visitSubroutineExit(this);
@@ -268,21 +268,27 @@ public class TopDownAnalyzer {
JetFunction function = (JetFunction) declaration; JetFunction function = (JetFunction) declaration;
FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor; FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor;
if (bodyExpression != null) { if (bodyExpression != null) {
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression); JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression);
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider); JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
assert readyToProcessExpressions : "Must be ready collecting types"; assert readyToProcessExpressions : "Must be ready collecting types";
if (function.getReturnTypeRef() != null) { if (function.getReturnTypeRef() != null) {
typeInferrer.checkFunctionReturnType(declaringScope, function, descriptor); typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptorImpl);
} }
else { else {
JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, descriptor); JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, functionDescriptorImpl);
if (returnType == null) { if (returnType == null) {
returnType = ErrorUtils.createErrorType("Unable to infer body type"); returnType = ErrorUtils.createErrorType("Unable to infer body type");
} }
functionDescriptorImpl.setUnsubstitutedReturnType(returnType); functionDescriptorImpl.setUnsubstitutedReturnType(returnType);
} }
List<JetElement> unreachableElements = new ArrayList<JetElement>();
flowInformationProvider.collectUnreachableExpressions(function, unreachableElements);
for (JetElement unreachableElement : unreachableElements) {
semanticServices.getErrorHandler().genericError(unreachableElement.getNode(), "Unreachable code");
}
} }
else { else {
if (function.getReturnTypeRef() == null) { if (function.getReturnTypeRef() == null) {
@@ -327,17 +333,44 @@ public class TopDownAnalyzer {
wrappedTrace.close(); wrappedTrace.close();
return new JetFlowInformationProvider() { return new JetFlowInformationProvider() {
@Override @Override
public void collectReturnedInformation(@NotNull JetFunction function, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) { public void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
Pseudocode pseudocode = pseudocodeMap.get(function); Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null; assert pseudocode != null;
SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction(); SubroutineExitInstruction exitInstruction = pseudocode.getExitInstruction();
processPreviousInstructions(exitInstruction, returnedExpressions, elementsReturningUnit); processPreviousInstructions(exitInstruction, new HashSet<Instruction>(), returnedExpressions, elementsReturningUnit);
}
@Override
public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
SubroutineEnterInstruction enterInstruction = pseudocode.getEnterInstruction();
Set<Instruction> visited = new HashSet<Instruction>();
collectReachable(enterInstruction, visited);
for (Instruction instruction : pseudocode.getInstructions()) {
if (!visited.contains(instruction) && instruction instanceof JetElementInstruction) {
unreachableElements.add(((JetElementInstruction) instruction).getElement());
}
}
} }
}; };
} }
private void processPreviousInstructions(Instruction previousFor, final Collection<JetExpression> returnedExpressions, final Collection<JetElement> elementsReturningUnit) { private void collectReachable(Instruction current, Set<Instruction> visited) {
if (!visited.add(current)) return;
for (Instruction nextInstruction : current.getNextInstructions()) {
collectReachable(nextInstruction, visited);
}
}
private void processPreviousInstructions(Instruction previousFor, final Set<Instruction> visited, final Collection<JetExpression> returnedExpressions, final Collection<JetElement> elementsReturningUnit) {
if (!visited.add(previousFor)) return;
Collection<Instruction> previousInstructions = previousFor.getPreviousInstructions(); Collection<Instruction> previousInstructions = previousFor.getPreviousInstructions();
InstructionVisitor visitor = new InstructionVisitor() { InstructionVisitor visitor = new InstructionVisitor() {
@Override @Override
@@ -347,7 +380,7 @@ public class TopDownAnalyzer {
@Override @Override
public void visitReturnValue(ReturnValueInstruction instruction) { public void visitReturnValue(ReturnValueInstruction instruction) {
processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit);
} }
@Override @Override
@@ -372,7 +405,7 @@ public class TopDownAnalyzer {
@Override @Override
public void visitJump(AbstractJumpInstruction instruction) { public void visitJump(AbstractJumpInstruction instruction) {
processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit); processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit);
} }
@Override @Override
@@ -348,7 +348,9 @@ public class JetTypeInferrer {
} }
private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block) { private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block) {
assert !block.isEmpty(); if (block.isEmpty()) {
return JetStandardClasses.getUnitType();
}
TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true); TypeInferrerVisitorWithWritableScope blockLevelVisitor = new TypeInferrerVisitorWithWritableScope(scope, true);
+4 -4
View File
@@ -9,7 +9,7 @@ l6:
r(b) r(b)
jf(l8) jf(l8)
r(1) r(1)
ret(*) ret(*) l5
jmp(l9) jmp(l9)
l8: l8:
read (Unit) read (Unit)
@@ -32,7 +32,7 @@ l4:
<START> <START>
r(2) r(2)
r(5) r(5)
ret(*) ret(*) l5
l5: l5:
<END> <END>
===================== =====================
@@ -60,7 +60,7 @@ l0:
r(a) r(a)
jf(l2) jf(l2)
r(1) r(1)
ret(*) ret(*) l1
jmp(l3) jmp(l3)
l2: l2:
read (Unit) read (Unit)
@@ -84,7 +84,7 @@ l4:
<START> <START>
r(2) r(2)
r(5) r(5)
ret(*) ret(*) l5
l5: l5:
<END> <END>
===================== =====================
@@ -1,17 +1,16 @@
== blockAndAndMismatch == == blockAndAndMismatch ==
fun blockAndAndMismatch() : Boolean { fun blockAndAndMismatch() : Boolean {
(return true) || (return false) false || (return false)
} }
--------------------- ---------------------
l0: l0:
<START> <START>
r(true) r(false)
ret(*)
jt(l2) jt(l2)
r(false) r(false)
ret(*) ret(*) l1
l2: l2:
r((return true) || (return false)) r(false || (return false))
l1: l1:
<END> <END>
===================== =====================
+1 -1
View File
@@ -1,3 +1,3 @@
fun blockAndAndMismatch() : Boolean { fun blockAndAndMismatch() : Boolean {
(return true) || (return false) false || (return false)
} }
+38
View File
@@ -0,0 +1,38 @@
fun t1() {
return
<error>1</error>
}
fun t2() {
if (1 > 2)
return
else return
<error>1</error>
}
fun t3() {
if (1 > 2)
return 2
else return ""
<error>1</error>
}
fun t4(a : Boolean) {
do {
return
}
while (<error>a</error>)
<error>1</error>
}
//fun t5() {
// do {
// return
// }
// while (1 > 2)
// 1
//}
//fun blockAndAndMismatch() : Boolean {
// (return true) || (return false)
//}