提交 cf799137 编写于 作者: A Andrey Breslav

Unreachable code with Nothing-returning functions

上级 4d92f751
......@@ -2,6 +2,7 @@
<dictionary name="abreslav">
<words>
<w>accessor</w>
<w>dominator</w>
<w>inferrer</w>
<w>nondeterministic</w>
<w>nullable</w>
......
......@@ -265,9 +265,6 @@ public class JetControlFlowProcessor {
builder.readUnit(expression);
}
builder.bindLabel(resultLabel);
// if (!inCondition) {
// builder.readNode(expression);
// }
}
@Override
......
......@@ -10,18 +10,50 @@ import java.util.Collection;
* @author abreslav
*/
public interface JetFlowInformationProvider {
JetFlowInformationProvider ERROR = new JetFlowInformationProvider() {
JetFlowInformationProvider THROW_EXCEPTION = new JetFlowInformationProvider() {
@Override
public void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
public void collectReturnedInformation(@NotNull JetElement subroutine, @NotNull Collection<JetExpression> returnedExpressions, @NotNull Collection<JetElement> elementsReturningUnit) {
throw new UnsupportedOperationException();
}
@Override
public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements) {
public void collectUnreachableExpressions(@NotNull JetElement subroutine, @NotNull Collection<JetElement> unreachableElements) {
throw new UnsupportedOperationException();
}
@Override
public void collectDominatedExpressions(@NotNull JetExpression dominator, @NotNull Collection<JetElement> dominated) {
throw new UnsupportedOperationException();
}
};
void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit);
void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements);
JetFlowInformationProvider NONE = new JetFlowInformationProvider() {
@Override
public void collectReturnedInformation(@NotNull JetElement subroutine, @NotNull Collection<JetExpression> returnedExpressions, @NotNull Collection<JetElement> elementsReturningUnit) {
}
@Override
public void collectUnreachableExpressions(@NotNull JetElement subroutine, @NotNull Collection<JetElement> unreachableElements) {
}
@Override
public void collectDominatedExpressions(@NotNull JetExpression dominator, @NotNull Collection<JetElement> dominated) {
}
};
void collectReturnedInformation(
@NotNull JetElement subroutine,
@NotNull Collection<JetExpression> returnedExpressions,
@NotNull Collection<JetElement> elementsReturningUnit);
void collectUnreachableExpressions(
@NotNull JetElement subroutine,
@NotNull Collection<JetElement> unreachableElements);
void collectDominatedExpressions(
@NotNull JetExpression dominator,
@NotNull Collection<JetElement> dominated);
}
......@@ -9,7 +9,7 @@ import java.util.Collections;
/**
* @author abreslav
*/
public abstract class AbstractJumpInstruction extends Instruction {
public abstract class AbstractJumpInstruction extends InstructionImpl {
private final Label targetLabel;
private Instruction resolvedTarget;
......
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* @author abreslav
*/
public abstract class Instruction {
private Collection<Instruction> previousInstructions = new LinkedHashSet<Instruction>();
* @author abreslav
*/
public interface Instruction {
@NotNull
Pseudocode getOwner();
public Collection<Instruction> getPreviousInstructions() {
return previousInstructions;
}
void setOwner(@NotNull Pseudocode owner);
@NotNull
public abstract Collection<Instruction> getNextInstructions();
Collection<Instruction> getPreviousInstructions();
@Nullable
protected Instruction outgoingEdgeTo(@Nullable Instruction target) {
if (target != null) {
target.getPreviousInstructions().add(this);
}
return target;
}
@NotNull
Collection<Instruction> getNextInstructions();
public abstract void accept(InstructionVisitor visitor);
void accept(InstructionVisitor visitor);
}
package org.jetbrains.jet.lang.cfg.pseudocode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* @author abreslav
*/
public abstract class InstructionImpl implements Instruction {
private Pseudocode owner;
private final Collection<Instruction> previousInstructions = new LinkedHashSet<Instruction>();
protected InstructionImpl() {
}
@Override
@NotNull
public Pseudocode getOwner() {
return owner;
}
@Override
public void setOwner(@NotNull Pseudocode owner) {
this.owner = owner;
}
@Override
public Collection<Instruction> getPreviousInstructions() {
return previousInstructions;
}
@Nullable
protected Instruction outgoingEdgeTo(@Nullable Instruction target) {
if (target != null) {
target.getPreviousInstructions().add(this);
}
return target;
}
}
......@@ -83,8 +83,13 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd
return pseudocode;
}
private void add(Instruction instruction) {
private void add(@NotNull Instruction instruction) {
pseudocode.addInstruction(instruction);
instruction.setOwner(pseudocode);
if (instruction instanceof JetElementInstruction) {
JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
trace.recordRepresentativeInstruction(elementInstruction.getElement(), instruction);
}
}
@NotNull
......
......@@ -6,7 +6,7 @@ import org.jetbrains.jet.lang.psi.JetElement;
/**
* @author abreslav
*/
public interface JetElementInstruction {
public interface JetElementInstruction extends Instruction {
@NotNull
JetElement getElement();
}
......@@ -6,7 +6,7 @@ import org.jetbrains.jet.lang.psi.JetElement;
/**
* @author abreslav
*/
public abstract class JetElementInstructionImpl extends Instruction implements JetElementInstruction {
public abstract class JetElementInstructionImpl extends InstructionImpl implements JetElementInstruction {
protected final JetElement element;
public JetElementInstructionImpl(@NotNull JetElement element) {
......
......@@ -13,12 +13,18 @@ public interface JetPseudocodeTrace {
public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) {
}
@Override
public void recordRepresentativeInstruction(@NotNull JetElement element, @NotNull Instruction instruction) {
}
@Override
public void close() {
}
};
void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode);
void recordRepresentativeInstruction(@NotNull JetElement element, @NotNull Instruction instruction);
void close();
}
......@@ -9,7 +9,7 @@ import java.util.Collections;
/**
* @author abreslav
*/
public class SubroutineExitInstruction extends Instruction {
public class SubroutineExitInstruction extends InstructionImpl {
private final JetElement subroutine;
public SubroutineExitInstruction(@NotNull JetElement subroutine) {
......
......@@ -3,6 +3,10 @@ package org.jetbrains.jet.lang.psi;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author abreslav
*/
......@@ -20,4 +24,27 @@ public class JetPsiUtil {
public static String safeName(String name) {
return name == null ? "<no name provided>" : name;
}
@NotNull
public static Set<JetElement> findRootExpressions(@NotNull Collection<JetElement> unreachableElements) {
Set<JetElement> rootElements = new HashSet<JetElement>();
final Set<JetElement> shadowedElements = new HashSet<JetElement>();
JetVisitor shadowAllChildren = new JetVisitor() {
@Override
public void visitJetElement(JetElement elem) {
if (shadowedElements.add(elem)) {
elem.acceptChildren(this);
}
}
};
for (JetElement element : unreachableElements) {
if (shadowedElements.contains(element)) continue;
element.acceptChildren(shadowAllChildren);
rootElements.removeAll(shadowedElements);
rootElements.add(element);
}
return rootElements;
}
}
......@@ -27,4 +27,5 @@ public interface BindingContext {
PsiElement getDeclarationPsiElement(DeclarationDescriptor descriptor);
boolean isBlock(JetFunctionLiteralExpression expression);
boolean isStatement(JetExpression expression);
}
......@@ -21,6 +21,8 @@ public class BindingTraceContext extends BindingTrace implements BindingContext
private final Map<DeclarationDescriptor, PsiElement> descriptorToDeclarations = new HashMap<DeclarationDescriptor, PsiElement>();
private final Map<PsiElement, DeclarationDescriptor> declarationsToDescriptors = new HashMap<PsiElement, DeclarationDescriptor>();
private final Set<JetFunctionLiteralExpression> blocks = new HashSet<JetFunctionLiteralExpression>();
private final Set<JetElement> statements = new HashSet<JetElement>();
private JetScope toplevelScope;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
......@@ -61,6 +63,11 @@ public class BindingTraceContext extends BindingTrace implements BindingContext
blocks.add(expression);
}
@Override
public void recordStatement(@NotNull JetElement statement) {
statements.add(statement);
}
public void setToplevelScope(JetScope toplevelScope) {
this.toplevelScope = toplevelScope;
}
......@@ -135,4 +142,9 @@ public class BindingTraceContext extends BindingTrace implements BindingContext
public boolean isBlock(JetFunctionLiteralExpression expression) {
return !expression.hasParameterSpecification() && blocks.contains(expression);
}
@Override
public boolean isStatement(@NotNull JetExpression expression) {
return statements.contains(expression);
}
}
......@@ -309,7 +309,7 @@ public class ClassDescriptorResolver {
type = ErrorUtils.createErrorType("No type, no body");
} else {
// TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}"
type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.ERROR).getType(scope, initializer, false);
type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).getType(scope, initializer, false);
}
} else {
type = typeResolver.resolveType(scope, propertyTypeRef);
......
......@@ -96,7 +96,7 @@ public class TopDownAnalyzer {
if (importDirective.isAllUnder()) {
JetExpression importedReference = importDirective.getImportedReference();
if (importedReference != null) {
JetType type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.ERROR).getType(namespaceScope, importedReference, false);
JetType type = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.THROW_EXCEPTION).getType(namespaceScope, importedReference, false);
if (type != null) {
namespaceScope.importScope(type.getMemberScope());
}
......@@ -288,7 +288,7 @@ public class TopDownAnalyzer {
flowInformationProvider.collectUnreachableExpressions(function, unreachableElements);
// This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
Set<JetElement> rootElements = findRootExpressions(unreachableElements);
Set<JetElement> rootElements = JetPsiUtil.findRootExpressions(unreachableElements);
// TODO : (return 1) || (return 2) -- only || and right of it is unreachable
// TODO : try {return 1} finally {return 2}. Currently 'return 1' is reported as unreachable,
......@@ -318,31 +318,10 @@ public class TopDownAnalyzer {
}
}
private Set<JetElement> findRootExpressions(List<JetElement> unreachableElements) {
Set<JetElement> rootElements = new HashSet<JetElement>();
final Set<JetElement> shadowedElements = new HashSet<JetElement>();
JetVisitor shadowAllChildren = new JetVisitor() {
@Override
public void visitJetElement(JetElement elem) {
if (shadowedElements.add(elem)) {
elem.acceptChildren(this);
}
}
};
for (JetElement element : unreachableElements) {
if (shadowedElements.contains(element)) continue;
element.acceptChildren(shadowAllChildren);
rootElements.removeAll(shadowedElements);
rootElements.add(element);
}
return rootElements;
}
private JetFlowInformationProvider computeFlowData(@NotNull JetDeclaration declaration, @NotNull JetExpression bodyExpression) {
final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration);
final Map<JetElement, Pseudocode> pseudocodeMap = new HashMap<JetElement, Pseudocode>();
final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
JetPseudocodeTrace wrappedTrace = new JetPseudocodeTrace() {
@Override
public void recordControlFlowData(@NotNull JetElement element, @NotNull Pseudocode pseudocode) {
......@@ -350,6 +329,12 @@ public class TopDownAnalyzer {
pseudocodeMap.put(element, pseudocode);
}
@Override
public void recordRepresentativeInstruction(@NotNull JetElement element, @NotNull Instruction instruction) {
Instruction oldValue = representativeInstructions.put(element, instruction);
// assert oldValue == null : element.getText();
}
@Override
public void close() {
pseudocodeTrace.close();
......@@ -363,7 +348,7 @@ public class TopDownAnalyzer {
wrappedTrace.close();
return new JetFlowInformationProvider() {
@Override
public void collectReturnedInformation(@NotNull JetElement subroutine, Collection<JetExpression> returnedExpressions, Collection<JetElement> elementsReturningUnit) {
public void collectReturnedInformation(@NotNull JetElement subroutine, @NotNull Collection<JetExpression> returnedExpressions, @NotNull Collection<JetElement> elementsReturningUnit) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
......@@ -372,7 +357,7 @@ public class TopDownAnalyzer {
}
@Override
public void collectUnreachableExpressions(@NotNull JetElement subroutine, Collection<JetElement> unreachableElements) {
public void collectUnreachableExpressions(@NotNull JetElement subroutine, @NotNull Collection<JetElement> unreachableElements) {
Pseudocode pseudocode = pseudocodeMap.get(subroutine);
assert pseudocode != null;
......@@ -385,7 +370,38 @@ public class TopDownAnalyzer {
unreachableElements.add(((JetElementInstruction) instruction).getElement());
}
}
}
@Override
public void collectDominatedExpressions(@NotNull JetExpression dominator, @NotNull Collection<JetElement> dominated) {
Instruction dominatorInstruction = representativeInstructions.get(dominator);
if (dominatorInstruction == null) {
// assert
// dominator instanceof JetContinueExpression ||
// dominator instanceof JetBreakExpression ||
// dominator instanceof JetReturnExpression ||
// dominator instanceof JetBlockExpression ||
// dominator instanceof JetFunctionLiteralExpression
// : "No representative instruction for a Nothing-typed expression: " + dominator.getText();
return;
}
SubroutineEnterInstruction enterInstruction = dominatorInstruction.getOwner().getEnterInstruction();
Set<Instruction> reachable = new HashSet<Instruction>();
collectReachable(enterInstruction, reachable);
Set<Instruction> reachableWithDominatorProhibited = new HashSet<Instruction>();
reachableWithDominatorProhibited.add(dominatorInstruction);
collectReachable(enterInstruction, reachableWithDominatorProhibited);
for (Instruction instruction : reachable) {
if (instruction instanceof JetElementInstruction
&& reachable.contains(instruction)
&& !reachableWithDominatorProhibited.contains(instruction)) {
JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
dominated.add(elementInstruction.getElement());
}
}
}
};
}
......
......@@ -38,6 +38,10 @@ public class BindingTrace {
}
public void recordStatement(@NotNull JetElement statement) {
}
public void removeReferenceResolution(@NotNull JetReferenceExpression referenceExpression) {
}
......
......@@ -250,7 +250,8 @@ public class JetStandardClasses {
}
public static boolean isNothing(@NotNull JetType type) {
return type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
return !(type instanceof NamespaceType) &&
type.getConstructor() == NOTHING_CLASS.getTypeConstructor();
}
public static JetType getTupleType(List<Attribute> attributes, List<JetType> arguments) {
......
......@@ -356,7 +356,9 @@ public class JetTypeInferrer {
JetType result = null;
for (JetElement statement : block) {
result = blockLevelVisitor.getType((JetExpression) statement);
trace.recordStatement(statement);
JetExpression statementExpression = (JetExpression) statement;
result = blockLevelVisitor.getType(statementExpression);
blockLevelVisitor.resetResult(); // TODO : maybe it's better to recreate the visitors with the same scope?
}
return result;
......@@ -407,7 +409,11 @@ public class JetTypeInferrer {
expression.accept(this);
if (result != null) {
trace.recordExpressionType(expression, result);
if (JetStandardClasses.isNothing(result)) {
markDominatedExpressionsAsUnreachable(expression);
}
}
return result;
}
......@@ -415,6 +421,18 @@ public class JetTypeInferrer {
result = null;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void markDominatedExpressionsAsUnreachable(JetExpression expression) {
List<JetElement> dominated = new ArrayList<JetElement>();
flowInformationProvider.collectDominatedExpressions(expression, dominated);
Set<JetElement> rootExpressions = JetPsiUtil.findRootExpressions(dominated);
for (JetElement rootExpression : rootExpressions) {
semanticServices.getErrorHandler().genericError(rootExpression.getNode(),
"This code is unreachable, because '" + expression.getText() + "' never terminates normally");
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
......@@ -488,7 +506,7 @@ public class JetTypeInferrer {
}
JetType effectiveReceiverType = receiverTypeRef == null ? null : receiverType;
JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("<return type>") : returnType;
result = JetStandardClasses.getFunctionType(null, effectiveReceiverType, parameterTypes, safeReturnType);
result = JetStandardClasses.getFunctionType(Collections.<Attribute>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType);
}
@Override
......
package org.jetbrains.jet.plugin;
import com.intellij.lang.documentation.QuickDocumentationProvider;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.psi.JetFile;
......@@ -11,10 +12,13 @@ import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.DeclarationDescriptor;
import org.jetbrains.jet.resolve.DescriptorUtil;
import java.util.Collections;
import java.util.List;
/**
* @author abreslav
*/
public class JetQuickDocumentationProvider extends QuickDocumentationProvider {
public class JetQuickDocumentationProvider implements DocumentationProvider {
@Override
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
......@@ -51,5 +55,23 @@ public class JetQuickDocumentationProvider extends QuickDocumentationProvider {
return text;
}
@Override
public List<String> getUrlFor(PsiElement element, PsiElement originalElement) {
return Collections.emptyList();
}
@Override
public String generateDoc(PsiElement element, PsiElement originalElement) {
return "<no doc>";
}
@Override
public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {
return null;
}
@Override
public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
return null;
}
}
......@@ -435,3 +435,26 @@ l3:
l1:
<END>
=====================
== tf ==
fun tf() {
try {
return 1
}
finally {
return 2
}
}
---------------------
l0:
<START>
jmp?(l2)
r(1)
r(2)
ret(*) l1
ret(*) l1
l2:
r(2)
ret(*) l1
l1:
<END>
=====================
......@@ -127,10 +127,10 @@ fun t8() {
fun blockAndAndMismatch() : Boolean {
<error>(return true) || (return false)</error>
<error>1</error>
<error>true</error>
}
fun tf() {
try {return} finally{return}
try {<error>return</error>} finally{return}
<error>1</error>
}
\ No newline at end of file
......@@ -56,6 +56,10 @@ public class JetControlFlowTest extends JetTestCaseBase {
}
}
@Override
public void recordRepresentativeInstruction(@NotNull JetElement element, @NotNull Instruction instruction) {
}
};
AnalyzingUtils.analyzeNamespace(file.getRootNamespace(), ErrorHandler.DO_NOTHING, new JetControlFlowDataTraceFactory() {
......
......@@ -459,14 +459,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private void assertType(String expression, JetType expectedType) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).getType(classDefinitions.BASIC_SCOPE, jetExpression, false);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.NONE).getType(classDefinitions.BASIC_SCOPE, jetExpression, false);
assertTrue(type + " != " + expectedType, JetTypeImpl.equalTypes(type, expectedType));
}
private void assertErrorType(String expression) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.NONE).safeGetType(classDefinitions.BASIC_SCOPE, jetExpression, false);
assertTrue("Error type expected but " + type + " returned", ErrorUtils.isErrorType(type));
}
......@@ -489,7 +489,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private void assertType(JetScope scope, String expression, String expectedTypeStr) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.ERROR).getType(scope, jetExpression, false);
JetType type = semanticServices.getTypeInferrer(BindingTrace.DUMMY, JetFlowInformationProvider.NONE).getType(scope, jetExpression, false);
JetType expectedType = expectedTypeStr == null ? null : makeType(expectedTypeStr);
assertEquals(expectedType, type);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册