Basic tests for dead code detection
This commit is contained in:
Generated
-1
@@ -16,7 +16,6 @@
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
|
||||
<option name="FORK_MODE" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.jetbrains.jet.lang.cfg;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetFunction;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -13,10 +12,16 @@ import java.util.Collection;
|
||||
public interface JetFlowInformationProvider {
|
||||
JetFlowInformationProvider ERROR = new JetFlowInformationProvider() {
|
||||
@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();
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.cfg.Label;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -21,6 +25,12 @@ public abstract class AbstractJumpInstruction extends Instruction {
|
||||
return resolvedTarget;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Instruction> getNextInstructions() {
|
||||
return Collections.singleton(getResolvedTarget());
|
||||
}
|
||||
|
||||
public void setResolvedTarget(Instruction resolvedTarget) {
|
||||
this.resolvedTarget = outgoingEdgeTo(resolvedTarget);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package org.jetbrains.jet.lang.cfg.pseudocode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.cfg.Label;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -35,6 +39,12 @@ public class ConditionalJumpInstruction extends AbstractJumpInstruction {
|
||||
this.nextOnFalse = outgoingEdgeTo(nextOnFalse);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Instruction> getNextInstructions() {
|
||||
return Arrays.asList(getNextOnFalse(), getNextOnTrue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(InstructionVisitor visitor) {
|
||||
visitor.visitConditionalJump(this);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.lang.cfg.pseudocode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -15,6 +16,9 @@ public abstract class Instruction {
|
||||
return previousInstructions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract Collection<Instruction> getNextInstructions();
|
||||
|
||||
@Nullable
|
||||
protected Instruction outgoingEdgeTo(@Nullable Instruction target) {
|
||||
if (target != null) {
|
||||
|
||||
@@ -3,6 +3,9 @@ package org.jetbrains.jet.lang.cfg.pseudocode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -17,6 +20,12 @@ public abstract class InstructionWithNext extends JetElementInstruction {
|
||||
return next;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Instruction> getNextInstructions() {
|
||||
return Collections.singleton(next);
|
||||
}
|
||||
|
||||
public void setNext(Instruction next) {
|
||||
this.next = outgoingEdgeTo(next);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package org.jetbrains.jet.lang.cfg.pseudocode;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.cfg.Label;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -26,6 +30,12 @@ public class NondeterministicJumpInstruction extends AbstractJumpInstruction {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Instruction> getNextInstructions() {
|
||||
return Arrays.asList(getResolvedTarget(), getNext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "jmp?(" + getTargetLabel().getName() + ")";
|
||||
|
||||
@@ -69,6 +69,11 @@ public class Pseudocode {
|
||||
return label;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<Instruction> getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public void addInstruction(Instruction instruction) {
|
||||
instructions.add(instruction);
|
||||
if (instruction instanceof SubroutineExitInstruction) {
|
||||
@@ -83,6 +88,11 @@ public class Pseudocode {
|
||||
return exitInstruction;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public SubroutineEnterInstruction getEnterInstruction() {
|
||||
return (SubroutineEnterInstruction) instructions.get(0);
|
||||
}
|
||||
|
||||
public void bindLabel(Label label) {
|
||||
((PseudocodeLabel) label).setTargetInstructionIndex(instructions.size());
|
||||
}
|
||||
|
||||
@@ -27,6 +27,6 @@ public class ReturnNoValueInstruction extends AbstractJumpInstruction {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ret";
|
||||
return "ret " + getTargetLabel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ public class ReturnValueInstruction extends AbstractJumpInstruction {
|
||||
|
||||
@Override
|
||||
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.jet.lang.psi.JetElement;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -17,6 +20,12 @@ public class SubroutineExitInstruction extends Instruction {
|
||||
return subroutine;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Instruction> getNextInstructions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(InstructionVisitor visitor) {
|
||||
visitor.visitSubroutineExit(this);
|
||||
|
||||
@@ -268,21 +268,27 @@ public class TopDownAnalyzer {
|
||||
JetFunction function = (JetFunction) declaration;
|
||||
FunctionDescriptorImpl functionDescriptorImpl = (FunctionDescriptorImpl) descriptor;
|
||||
if (bodyExpression != null) {
|
||||
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression);
|
||||
JetFlowInformationProvider flowInformationProvider = computeFlowData(function, bodyExpression);
|
||||
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, flowInformationProvider);
|
||||
|
||||
assert readyToProcessExpressions : "Must be ready collecting types";
|
||||
|
||||
if (function.getReturnTypeRef() != null) {
|
||||
typeInferrer.checkFunctionReturnType(declaringScope, function, descriptor);
|
||||
typeInferrer.checkFunctionReturnType(declaringScope, function, functionDescriptorImpl);
|
||||
}
|
||||
else {
|
||||
JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, descriptor);
|
||||
JetType returnType = typeInferrer.getFunctionReturnType(declaringScope, function, functionDescriptorImpl);
|
||||
if (returnType == null) {
|
||||
returnType = ErrorUtils.createErrorType("Unable to infer body type");
|
||||
}
|
||||
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 {
|
||||
if (function.getReturnTypeRef() == null) {
|
||||
@@ -327,17 +333,44 @@ public class TopDownAnalyzer {
|
||||
wrappedTrace.close();
|
||||
return new JetFlowInformationProvider() {
|
||||
@Override
|
||||
public void collectReturnedInformation(@NotNull JetFunction function, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
|
||||
Pseudocode pseudocode = pseudocodeMap.get(function);
|
||||
public void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
|
||||
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
|
||||
assert pseudocode != null;
|
||||
|
||||
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();
|
||||
InstructionVisitor visitor = new InstructionVisitor() {
|
||||
@Override
|
||||
@@ -347,7 +380,7 @@ public class TopDownAnalyzer {
|
||||
|
||||
@Override
|
||||
public void visitReturnValue(ReturnValueInstruction instruction) {
|
||||
processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit);
|
||||
processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -372,7 +405,7 @@ public class TopDownAnalyzer {
|
||||
|
||||
@Override
|
||||
public void visitJump(AbstractJumpInstruction instruction) {
|
||||
processPreviousInstructions(instruction, returnedExpressions, elementsReturningUnit);
|
||||
processPreviousInstructions(instruction, visited, returnedExpressions, elementsReturningUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -348,7 +348,9 @@ public class JetTypeInferrer {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ l6:
|
||||
r(b)
|
||||
jf(l8)
|
||||
r(1)
|
||||
ret(*)
|
||||
ret(*) l5
|
||||
jmp(l9)
|
||||
l8:
|
||||
read (Unit)
|
||||
@@ -32,7 +32,7 @@ l4:
|
||||
<START>
|
||||
r(2)
|
||||
r(5)
|
||||
ret(*)
|
||||
ret(*) l5
|
||||
l5:
|
||||
<END>
|
||||
=====================
|
||||
@@ -60,7 +60,7 @@ l0:
|
||||
r(a)
|
||||
jf(l2)
|
||||
r(1)
|
||||
ret(*)
|
||||
ret(*) l1
|
||||
jmp(l3)
|
||||
l2:
|
||||
read (Unit)
|
||||
@@ -84,7 +84,7 @@ l4:
|
||||
<START>
|
||||
r(2)
|
||||
r(5)
|
||||
ret(*)
|
||||
ret(*) l5
|
||||
l5:
|
||||
<END>
|
||||
=====================
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
== blockAndAndMismatch ==
|
||||
fun blockAndAndMismatch() : Boolean {
|
||||
(return true) || (return false)
|
||||
false || (return false)
|
||||
}
|
||||
---------------------
|
||||
l0:
|
||||
<START>
|
||||
r(true)
|
||||
ret(*)
|
||||
r(false)
|
||||
jt(l2)
|
||||
r(false)
|
||||
ret(*)
|
||||
ret(*) l1
|
||||
l2:
|
||||
r((return true) || (return false))
|
||||
r(false || (return false))
|
||||
l1:
|
||||
<END>
|
||||
=====================
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fun blockAndAndMismatch() : Boolean {
|
||||
(return true) || (return false)
|
||||
false || (return false)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
//}
|
||||
Reference in New Issue
Block a user