dfa: exception handling reworked

no more gosub-return causing equal states differing by offset stack only
  don't visit outer finally without visiting inner one
This commit is contained in:
peter
2013-10-12 23:00:40 +02:00
parent 3744d2ce50
commit 099ead0d64
11 changed files with 261 additions and 270 deletions
@@ -31,6 +31,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiVariable;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -77,9 +78,9 @@ public class ControlFlow {
myInstructions.add(instruction);
}
public void removeVariable(PsiVariable variable) {
DfaVariableValue var = myFactory.getVarFactory().createVariableValue(variable, false);
addInstruction(new FlushVariableInstruction(var));
public void removeVariable(@Nullable PsiVariable variable) {
if (variable == null) return;
addInstruction(new FlushVariableInstruction(myFactory.getVarFactory().createVariableValue(variable, false)));
}
public ControlFlowOffset getStartOffset(final PsiElement element) {
@@ -18,7 +18,6 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ConditionCheckManager;
import com.intellij.codeInsight.ConditionChecker;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.diagnostic.Logger;
@@ -45,12 +44,6 @@ import static com.intellij.psi.CommonClassNames.*;
class ControlFlowAnalyzer extends JavaElementVisitor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer");
private static final ControlFlow.ControlFlowOffset NOT_FOUND = new ControlFlow.ControlFlowOffset() {
@Override
public int getInstructionOffset() {
throw new UnsupportedOperationException("Not implemented");
}
};
public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName();
private boolean myIgnoreAssertions;
@@ -62,9 +55,15 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
private Stack<CatchDescriptor> myCatchStack;
private DfaValue myRuntimeException;
private DfaValue myError;
private DfaValue myString;
private PsiType myNpe;
private Stack<PsiElement> myElementStack = new Stack<PsiElement>();
/**
* A mock variable for try-related control transfers. Contains exceptions or an (Throwable-inconvertible) string to indicate return inside finally
*/
private DfaVariableValue myExceptionHolder;
ControlFlowAnalyzer(final DfaValueFactory valueFactory) {
myFactory = valueFactory;
}
@@ -76,6 +75,11 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
myRuntimeException = myFactory.createTypeValue(createClassType(manager, scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL);
myError = myFactory.createTypeValue(createClassType(manager, scope, JAVA_LANG_ERROR), Nullness.NOT_NULL);
myNpe = createClassType(manager, scope, JAVA_LANG_NULL_POINTER_EXCEPTION);
myString = myFactory.createTypeValue(createClassType(manager, scope, JAVA_LANG_STRING), Nullness.NOT_NULL);
PsiParameter mockVar = JavaPsiFacade.getElementFactory(manager.getProject()).createParameter("$exception$", createClassType(manager, scope, JAVA_LANG_OBJECT));
myExceptionHolder = myFactory.getVarFactory().createVariableValue(mockVar, false);
myFields = new HashSet<DfaVariableValue>();
myCatchStack = new Stack<CatchDescriptor>();
myCurrentFlow = new ControlFlow(myFactory);
@@ -192,7 +196,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
lExpr.accept(this);
generateBoxingUnboxingInstructionFor(lExpr,exprType);
rExpr.accept(this);
generateBoxingUnboxingInstructionFor(rExpr,exprType);
generateBoxingUnboxingInstructionFor(rExpr, exprType);
addInstruction(new BinopInstruction(null, null, lExpr.getProject()));
}
@@ -287,6 +291,23 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
}
PsiElement parent = block.getParent();
if (parent instanceof PsiCatchSection) {
myCurrentFlow.removeVariable(((PsiCatchSection)parent).getParameter());
}
else if (parent instanceof PsiForeachStatement) {
myCurrentFlow.removeVariable(((PsiForeachStatement)parent).getIterationParameter());
}
else if (parent instanceof PsiForStatement) {
PsiStatement statement = ((PsiForStatement)parent).getInitialization();
if (statement instanceof PsiDeclarationStatement) {
for (PsiElement declaration : ((PsiDeclarationStatement)statement).getDeclaredElements()) {
if (declaration instanceof PsiVariable) {
myCurrentFlow.removeVariable((PsiVariable)declaration);
}
}
}
}
}
@Override public void visitBlockStatement(PsiBlockStatement statement) {
@@ -397,11 +418,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (initialization != null) {
initialization.accept(this);
initialization.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
visitElement(expression);
}
@Override public void visitDeclarationStatement(PsiDeclarationStatement statement) {
@Override
public void visitDeclarationStatement(PsiDeclarationStatement statement) {
PsiElement[] declaredElements = statement.getDeclaredElements();
for (PsiElement element : declaredElements) {
if (element instanceof PsiVariable) {
@@ -511,10 +534,16 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
private void returnCheckingFinally() {
ControlFlow.ControlFlowOffset finallyOffset = getFinallyOffset();
if (finallyOffset != NOT_FOUND) {
addInstruction(new GosubInstruction(finallyOffset));
if (finallyOffset != null) {
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new PushInstruction(myString, null));
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addInstruction(new GotoInstruction(finallyOffset));
} else {
addInstruction(new ReturnInstruction());
}
addInstruction(new ReturnInstruction());
}
@Override public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) {
@@ -627,70 +656,62 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (exception != null) {
exception.accept(this);
if (myCatchStack.isEmpty()) {
addInstruction(new ReturnInstruction());
finishElement(statement);
return;
}
addConditionalRuntimeThrow();
addInstruction(new DupInstruction());
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, statement.getProject()));
ConditionalGotoInstruction gotoInstruction = new ConditionalGotoInstruction(null, true, null);
addInstruction(gotoInstruction);
addInstruction(new PopInstruction());
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new PushInstruction(myFactory.createTypeValue(myNpe, Nullness.NOT_NULL), null));
addThrowCode(myNpe);
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addThrowCode(false);
gotoInstruction.setOffset(myCurrentFlow.getInstructionCount());
addThrowCode(exception.getType());
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new SwapInstruction());
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addThrowCode(false);
}
finishElement(statement);
}
private void addConditionalRuntimeThrow() {
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
CatchDescriptor cd = myCatchStack.get(i);
if (cd.isFinally()) {
addConditionalRuntimeThrow(cd, false);
continue;
}
PsiType type = cd.getLubType();
if (type instanceof PsiClassType && ExceptionUtil.isUncheckedExceptionOrSuperclass((PsiClassType)type)) {
addConditionalRuntimeThrow(cd, true);
}
if (myCatchStack.isEmpty()) {
return;
}
}
private void addConditionalRuntimeThrow(CatchDescriptor cd, boolean forCatch) {
pushUnknown();
final ConditionalGotoInstruction branch = new ConditionalGotoInstruction(null, false, null);
addInstruction(branch);
final ConditionalGotoInstruction ifNoException = addInstruction(new ConditionalGotoInstruction(null, false, null));
addInstruction(new EmptyStackInstruction());
flushVariablesInsideTry(cd);
if (forCatch) {
PsiType type = cd.getLubType();
boolean isRuntime = InheritanceUtil.isInheritor(type, JAVA_LANG_RUNTIME_EXCEPTION) || ExceptionUtil.isGeneralExceptionType(type);
boolean isError = InheritanceUtil.isInheritor(type, JAVA_LANG_ERROR) || type.equalsToText(JAVA_LANG_THROWABLE);
if (isRuntime != isError) {
addInstruction(new PushInstruction(isRuntime ? myRuntimeException : myError, null));
addGotoCatch(cd);
} else {
pushUnknown();
final ConditionalGotoInstruction branch2 = new ConditionalGotoInstruction(null, false, null);
addInstruction(branch2);
addInstruction(new PushInstruction(myError, null));
addGotoCatch(cd);
branch2.setOffset(myCurrentFlow.getInstructionCount());
addInstruction(new PushInstruction(myRuntimeException, null));
addGotoCatch(cd);
}
}
else {
addInstruction(new GosubInstruction(cd.getJumpOffset(this)));
addInstruction(new ReturnInstruction());
}
branch.setOffset(myCurrentFlow.getInstructionCount());
}
addInstruction(new PushInstruction(myExceptionHolder, null));
private void flushVariablesInsideTry(CatchDescriptor cd) {
flushVariablesOnControlTransfer(cd.getBlock());
pushUnknown();
final ConditionalGotoInstruction ifError = addInstruction(new ConditionalGotoInstruction(null, false, null));
addInstruction(new PushInstruction(myRuntimeException, null));
GotoInstruction ifRuntime = addInstruction(new GotoInstruction(null));
ifError.setOffset(myCurrentFlow.getInstructionCount());
addInstruction(new PushInstruction(myError, null));
ifRuntime.setOffset(myCurrentFlow.getInstructionCount());
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addThrowCode(false);
ifNoException.setOffset(myCurrentFlow.getInstructionCount());
}
private void flushVariablesOnControlTransfer(PsiElement stopWhenAncestorOf) {
@@ -701,59 +722,44 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
if (scope instanceof PsiCodeBlock) {
flushCodeBlockVariables((PsiCodeBlock)scope);
if (scope.getParent() instanceof PsiTryStatement && scope == ((PsiTryStatement)scope.getParent()).getFinallyBlock()) {
addInstruction(new PopOffsetInstruction());
}
}
}
}
private void addThrowCode(PsiType exceptionClass) {
if (exceptionClass == null) return;
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
CatchDescriptor cd = myCatchStack.get(i);
if (cd.isFinally()) {
flushVariablesInsideTry(cd);
addInstruction(new GosubInstruction(cd.getJumpOffset(this)));
break;
// the exception object should be in $exception$ variable
private void addThrowCode(boolean catchRethrow) {
if (myCatchStack.isEmpty()) {
addInstruction(new ReturnInstruction());
return;
}
PsiElement currentElement = myElementStack.peek();
CatchDescriptor cd = myCatchStack.get(myCatchStack.size() - 1);
if (!cd.isFinally() && PsiTreeUtil.isAncestor(cd.getBlock().getParent(), currentElement, false)) {
int i = myCatchStack.size() - 2;
while (!catchRethrow && i >= 0 && !myCatchStack.get(i).isFinally() && myCatchStack.get(i).getTryStatement() == cd.getTryStatement()) {
i--;
}
else if (cd.getType().isAssignableFrom(exceptionClass)) { // Definite catch.
flushVariablesInsideTry(cd);
addGotoCatch(cd);
if (i < 0) {
addInstruction(new ReturnInstruction());
return;
}
else if (cd.getType().isConvertibleFrom(exceptionClass)) { // Probable catch
addInstruction(new DupInstruction());
pushUnknown();
final ConditionalGotoInstruction branch = new ConditionalGotoInstruction(null, false, null);
addInstruction(branch);
flushVariablesInsideTry(cd);
addGotoCatch(cd);
branch.setOffset(myCurrentFlow.getInstructionCount());
}
cd = myCatchStack.get(i);
}
addInstruction(new ReturnInstruction());
}
/**
* Exception is expected on the stack.
*/
private void addGotoCatch(CatchDescriptor cd) {
addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(cd.getParameter(), false), null));
addInstruction(new SwapInstruction());
myCurrentFlow.addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
flushVariablesOnControlTransfer(cd.getBlock());
addInstruction(new GotoInstruction(cd.getJumpOffset(this)));
}
@Nullable
private ControlFlow.ControlFlowOffset getFinallyOffset() {
for (int i = myCatchStack.size() - 1; i >= 0; i--) {
CatchDescriptor cd = myCatchStack.get(i);
if (cd.isFinally()) return cd.getJumpOffset(this);
}
return NOT_FOUND;
return null;
}
private static class ApplyNotNullInstruction extends Instruction {
@@ -801,25 +807,20 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
public PsiCodeBlock getBlock() {
return myBlock;
}
public PsiTryStatement getTryStatement() {
return (PsiTryStatement) (isFinally() ? myBlock.getParent() : myBlock.getParent().getParent());
}
public PsiType getType() {
return myType;
}
PsiType getLubType() {
PsiType type = myType;
if (type instanceof PsiDisjunctionType) {
return ((PsiDisjunctionType)type).getLeastUpperBound();
}
return type;
}
public boolean isFinally() {
return myIsFinally;
}
public ControlFlow.ControlFlowOffset getJumpOffset(ControlFlowAnalyzer analyzer) {
return analyzer.getStartOffset(myBlock);
return analyzer.getStartOffset(isFinally() ? myBlock : myBlock.getParent());
}
public PsiParameter getParameter() {
@@ -839,7 +840,6 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
myCatchStack.push(new CatchDescriptor(finallyBlock));
}
int catchesPushCount = 0;
PsiCatchSection[] sections = statement.getCatchSections();
for (int i = sections.length - 1; i >= 0; i--) {
PsiCatchSection section = sections[i];
@@ -849,14 +849,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
PsiType type = parameter.getType();
if (type instanceof PsiClassType || type instanceof PsiDisjunctionType) {
myCatchStack.push(new CatchDescriptor(parameter, catchBlock));
catchesPushCount++;
continue;
}
}
throw new CannotAnalyzeException();
}
ControlFlow.ControlFlowOffset endOffset = finallyBlock == null ? getEndOffset(statement) : ControlFlow.deltaOffset(getStartOffset(finallyBlock), -2);
ControlFlow.ControlFlowOffset endOffset = finallyBlock == null ? getEndOffset(statement) : getStartOffset(finallyBlock);
if (resourceList != null) {
resourceList.accept(this);
@@ -866,23 +865,26 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
tryBlock.accept(this);
}
for (int i = 0; i < catchesPushCount; i++) {
myCatchStack.pop();
}
addInstruction(new GotoInstruction(endOffset));
for (PsiCatchSection section : sections) {
section.accept(this);
addInstruction(new GotoInstruction(endOffset));
myCatchStack.pop();
}
if (finallyBlock != null) {
myCatchStack.pop();
addInstruction(new GosubInstruction(getStartOffset(finallyBlock)));
addInstruction(new GotoInstruction(getEndOffset(statement)));
finallyBlock.accept(this);
addInstruction(new ReturnFromSubInstruction());
//if $exception$==null => continue normal exectuion
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, statement.getProject()));
addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, null));
// else throw $exception$
addThrowCode(false);
}
finishElement(statement);
@@ -890,10 +892,36 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
@Override
public void visitCatchSection(PsiCatchSection section) {
startElement(section);
PsiCodeBlock catchBlock = section.getCatchBlock();
if (catchBlock != null) {
// exception is in myExceptionHolder mock variable
// check if it's assignable to catch parameter type
PsiType declaredType = section.getCatchType();
List<PsiType> flattened = declaredType instanceof PsiDisjunctionType ?
((PsiDisjunctionType)declaredType).getDisjunctions() :
ContainerUtil.createMaybeSingletonList(declaredType);
for (PsiType catchType : flattened) {
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new PushInstruction(myFactory.createTypeValue(catchType, Nullness.UNKNOWN), null));
addInstruction(new BinopInstruction(JavaTokenType.INSTANCEOF_KEYWORD, null, section.getProject()));
addInstruction(new ConditionalGotoInstruction(ControlFlow.deltaOffset(getStartOffset(catchBlock), -5), false, null));
}
// not assignable => rethrow
addThrowCode(true);
// e = $exception$
addInstruction(new PushInstruction(myFactory.getVarFactory().createVariableValue(section.getParameter(), false), null));
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addInstruction(new FlushVariableInstruction(myExceptionHolder));
catchBlock.accept(this);
}
finishElement(section);
}
@Override
@@ -1273,8 +1301,11 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null);
addInstruction(cond);
addInstruction(new EmptyStackInstruction());
addInstruction(new PushInstruction(myExceptionHolder, null));
addInstruction(new PushInstruction(myFactory.createTypeValue(ref, Nullness.NOT_NULL), null));
addThrowCode(ref);
addInstruction(new AssignInstruction(null));
addInstruction(new PopInstruction());
addThrowCode(false);
cond.setOffset(myCurrentFlow.getInstructionCount());
}
}
@@ -1414,11 +1445,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new GotoInstruction(exitPoint));
break;
case THROW_EXCEPTION:
ControlFlow.ControlFlowOffset finallyOffset = getFinallyOffset();
if (finallyOffset != NOT_FOUND) {
addInstruction(new GosubInstruction(finallyOffset));
}
addInstruction(new ReturnInstruction());
returnCheckingFinally();
break;
case SYSTEM_EXIT:
addInstruction(new ReturnInstruction());
@@ -114,19 +114,17 @@ public class DataFlowRunner {
joinInstructions.add(myInstructions[((GotoInstruction)instruction).getOffset()]);
} else if (instruction instanceof ConditionalGotoInstruction) {
joinInstructions.add(myInstructions[((ConditionalGotoInstruction)instruction).getOffset()]);
} else if (instruction instanceof GosubInstruction) {
joinInstructions.add(myInstructions[((GosubInstruction)instruction).getSubprogramOffset()]);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Analyzing code block: " + psiBlock.getText());
for (int i = 0; i < myInstructions.length; i++) {
Instruction instruction = myInstructions[i];
LOG.debug(i + ": " + instruction.toString());
LOG.debug(i + ": " + myInstructions[i].toString());
}
}
//for (int i = 0; i < myInstructions.length; i++) System.out.println(i + ": " + myInstructions[i].toString());
Integer tooExpensiveHash = psiBlock.getUserData(TOO_EXPENSIVE_HASH);
if (tooExpensiveHash != null && tooExpensiveHash == psiBlock.getText().hashCode()) {
LOG.debug("Too complex because hasn't changed since being too complex already");
@@ -196,13 +194,11 @@ public class DataFlowRunner {
return RunnerResult.OK;
}
catch (ArrayIndexOutOfBoundsException e) {
LOG.error(psiBlock.getText(), e); // TODO fix in better times
LOG.error(psiBlock.getText(), e);
return RunnerResult.ABORTED;
}
catch (EmptyStackException e) {
if (LOG.isDebugEnabled()) {
LOG.error(e); // TODO fix in better times
}
LOG.error(psiBlock.getText(), e);
return RunnerResult.ABORTED;
}
}
@@ -36,9 +36,6 @@ public interface DfaMemoryState {
DfaValue peek();
void push(@NotNull DfaValue value);
int popOffset();
void pushOffset(int offset);
void emptyStack();
void setVarValue(DfaVariableValue var, DfaValue value);
@@ -28,11 +28,16 @@ import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.UnorderedPair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import gnu.trove.*;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TLongArrayList;
import gnu.trove.TLongHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -44,7 +49,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
private final List<EqClass> myEqClasses;
private final Stack<DfaValue> myStack;
private TIntStack myOffsetStack;
private final TLongHashSet myDistinctClasses;
private final Map<DfaVariableValue,DfaVariableState> myVariableStates;
private final Map<DfaVariableValue,DfaVariableState> myDefaultVariableStates;
@@ -58,7 +62,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
myUnknownVariables = ContainerUtil.newTroveSet();
myVariableStates = ContainerUtil.newTroveMap();
myDistinctClasses = new TLongHashSet();
myOffsetStack = new TIntStack();
myStack = new Stack<DfaValue>();
}
@@ -70,7 +73,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
myStack = new Stack<DfaValue>(toCopy.myStack);
myDistinctClasses = new TLongHashSet(toCopy.myDistinctClasses.toArray());
myUnknownVariables = new THashSet<DfaVariableValue>(toCopy.myUnknownVariables);
myOffsetStack = toCopy.myOffsetStack;
myEqClasses = ContainerUtil.newArrayList(toCopy.myEqClasses);
myVariableStates = new THashMap<DfaVariableValue, DfaVariableState>(toCopy.myVariableStates);
@@ -100,7 +102,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
boolean equalsSuperficially(DfaMemoryStateImpl other) {
return myEphemeral == other.myEphemeral && myStack.equals(other.myStack) && myOffsetStack.equals(other.myOffsetStack);
return myEphemeral == other.myEphemeral && myStack.equals(other.myStack);
}
boolean equalsByRelations(DfaMemoryStateImpl that) {
@@ -205,18 +207,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
myStack.push(value);
}
@Override
public int popOffset() {
myOffsetStack = new TIntStack(myOffsetStack);
return myOffsetStack.pop();
}
@Override
public void pushOffset(int offset) {
myOffsetStack = new TIntStack(myOffsetStack);
myOffsetStack.push(offset);
}
@Override
public void emptyStack() {
myStack.clear();
@@ -1,45 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.codeInspection.dataFlow.*;
/**
* @author max
*/
public class GosubInstruction extends Instruction {
private final ControlFlow.ControlFlowOffset mySubprogramOffset;
public GosubInstruction(ControlFlow.ControlFlowOffset subprogramOffset) {
mySubprogramOffset = subprogramOffset;
}
public int getSubprogramOffset() {
return mySubprogramOffset.getInstructionOffset();
}
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
final int returnIndex = getIndex() + 1;
stateBefore.pushOffset(returnIndex);
Instruction nextInstruction = runner.getInstruction(getSubprogramOffset());
return new DfaInstructionState[] {new DfaInstructionState(nextInstruction, stateBefore)};
}
public String toString() {
return "GOSUB: " + getSubprogramOffset();
}
}
@@ -1,37 +0,0 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
/**
* @author peter
*/
public class PopOffsetInstruction extends Instruction{
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState memState, InstructionVisitor visitor) {
memState.popOffset();
return nextInstruction(runner, memState);
}
public String toString() {
return "POP_OFFSET";
}
}
@@ -1,37 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
/**
* @author max
*/
public class ReturnFromSubInstruction extends Instruction{
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState memState, InstructionVisitor visitor) {
int offset = memState.popOffset();
return new DfaInstructionState[] {new DfaInstructionState(runner.getInstruction(offset), memState)};
}
public String toString() {
return "RETURN_FROM_SUB";
}
}
@@ -0,0 +1,43 @@
public class Foo {
Foo getFoo() { return <warning descr="'null' is returned by the method which isn't declared as @Nullable">null</warning>; }
void foo() {
try {
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo().getFoo();
} finally {
if (hashCode() == 2) {
System.out.println();
}
}
}
}
@@ -0,0 +1,54 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Throwable;
import java.net.Socket;
public class Foo {
void unchecked() {
boolean b = true;
try {
try {
System.out.println();
} finally {
b = false;
}
} finally {
if (<warning descr="Condition 'b' is always 'false'">b</warning>) {
System.out.println();
}
}
}
void checked(boolean flag) throws Throwable {
Throwable throwable = new Throwable();
boolean b = true;
try {
if (flag) {
try {
throw throwable;
} finally {
b = false;
}
}
} finally {
if (b) {
System.out.println();
}
}
}
void justReturn(boolean flag) throws Throwable {
boolean b = true;
try {
try {
return;
} finally {
b = false;
}
} finally {
if (<warning descr="Condition 'b' is always 'false'">b</warning>) {
System.out.println();
}
}
}
}
@@ -78,6 +78,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testNestedTryInWhileNotComplex() throws Throwable { doTest(); }
public void testExceptionFromFinally() throws Throwable { doTest(); }
public void testExceptionFromFinallyNesting() throws Throwable { doTest(); }
public void testNestedFinally() { doTest(); }
public void testFieldChangedBetweenSynchronizedBlocks() throws Throwable { doTest(); }
public void testGeneratedEquals() throws Throwable { doTest(); }
@@ -291,6 +292,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testWhileNotComplex() { doTest(); }
public void testManyDisjunctiveFieldAssignmentsInLoopNotComplex() { doTest(); }
public void testManyContinuesNotComplex() { doTest(); }
public void testFinallyNotComplex() { doTest(); }
public void testVariablesDiverge() { doTest(); }
public void testMergeByNullability() { doTest(); }