/* * 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; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInspection.dataFlow.instructions.*; import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.*; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Stack; import com.siyeh.ig.numeric.UnnecessaryExplicitNumericCastInspection; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; import static com.intellij.psi.CommonClassNames.*; public class ControlFlowAnalyzer extends JavaElementVisitor { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer"); public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName(); private boolean myIgnoreAssertions; private static class CannotAnalyzeException extends RuntimeException { } private final DfaValueFactory myFactory; private ControlFlow myCurrentFlow; private Stack myCatchStack; private DfaValue myRuntimeException; private DfaValue myError; private DfaValue myString; private PsiType myNpe; private PsiType myAssertionError; private Stack myElementStack = new Stack(); /** * 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; } public ControlFlow buildControlFlow(@NotNull PsiElement codeFragment, boolean ignoreAssertions) { myIgnoreAssertions = ignoreAssertions; PsiManager manager = codeFragment.getManager(); GlobalSearchScope scope = codeFragment.getResolveScope(); 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); myAssertionError = createClassType(manager, scope, JAVA_LANG_ASSERTION_ERROR); myString = myFactory.createTypeValue(createClassType(manager, scope, JAVA_LANG_STRING), Nullness.NOT_NULL); PsiParameter mockVar = JavaPsiFacade.getElementFactory(manager.getProject()).createParameterFromText("java.lang.Object $exception$", null); myExceptionHolder = myFactory.getVarFactory().createVariableValue(mockVar, false); myCatchStack = new Stack(); myCurrentFlow = new ControlFlow(myFactory); try { codeFragment.accept(this); } catch (CannotAnalyzeException e) { return null; } PsiElement parent = codeFragment.getParent(); if (parent instanceof PsiLambdaExpression && codeFragment instanceof PsiExpression) { addInstruction(new CheckReturnValueInstruction(codeFragment)); } addInstruction(new ReturnInstruction(false, null)); return myCurrentFlow; } private static PsiClassType createClassType(PsiManager manager, GlobalSearchScope scope, String fqn) { PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(fqn, scope); if (aClass != null) return JavaPsiFacade.getElementFactory(manager.getProject()).createType(aClass); return JavaPsiFacade.getElementFactory(manager.getProject()).createTypeByFQClassName(fqn, scope); } private T addInstruction(T i) { myCurrentFlow.addInstruction(i); return i; } private ControlFlow.ControlFlowOffset getEndOffset(PsiElement element) { return myCurrentFlow.getEndOffset(element); } private ControlFlow.ControlFlowOffset getStartOffset(PsiElement element) { return myCurrentFlow.getStartOffset(element); } private void startElement(PsiElement element) { myCurrentFlow.startElement(element); myElementStack.push(element); } private void finishElement(PsiElement element) { myCurrentFlow.finishElement(element); PsiElement popped = myElementStack.pop(); if (element != popped) { throw new AssertionError("Expected " + element + ", popped " + popped); } } @Override public void visitErrorElement(PsiErrorElement element) { throw new CannotAnalyzeException(); } @Override public void visitAssignmentExpression(PsiAssignmentExpression expression) { PsiExpression lExpr = expression.getLExpression(); PsiExpression rExpr = expression.getRExpression(); startElement(expression); if (rExpr == null) { pushUnknown(); finishElement(expression); return; } IElementType op = expression.getOperationTokenType(); PsiType type = expression.getType(); boolean isBoolean = PsiType.BOOLEAN.equals(type); if (op == JavaTokenType.EQ) { lExpr.accept(this); rExpr.accept(this); generateBoxingUnboxingInstructionFor(rExpr, type); } else if (op == JavaTokenType.ANDEQ) { if (isBoolean) { generateBooleanAssignmentExpression(true, lExpr, rExpr, type); } else { generateDefaultAssignmentBinOp(lExpr, rExpr, type); } } else if (op == JavaTokenType.OREQ) { if (isBoolean) { generateBooleanAssignmentExpression(false, lExpr, rExpr, type); } else { generateDefaultAssignmentBinOp(lExpr, rExpr, type); } } else if (op == JavaTokenType.XOREQ) { if (isBoolean) { generateXorExpression(expression, new PsiExpression[]{lExpr, rExpr}, type, true); } else { generateDefaultAssignmentBinOp(lExpr, rExpr, type); } } else if (op == JavaTokenType.PLUSEQ && type != null && type.equalsToText(JAVA_LANG_STRING)) { lExpr.accept(this); addInstruction(new DupInstruction()); rExpr.accept(this); addInstruction(new BinopInstruction(JavaTokenType.PLUS, null, lExpr.getProject())); } else { generateDefaultAssignmentBinOp(lExpr, rExpr, type); } addInstruction(new AssignInstruction(rExpr)); flushArrayElementsOnUnknownIndexAssignment(lExpr); finishElement(expression); } private void flushArrayElementsOnUnknownIndexAssignment(PsiExpression lExpr) { if (lExpr instanceof PsiArrayAccessExpression && myFactory.createValue(lExpr) == null // check for unknown index, otherwise AssignInstruction will flush only that element ) { DfaValue arrayVar = myFactory.createValue(((PsiArrayAccessExpression)lExpr).getArrayExpression()); if (arrayVar instanceof DfaVariableValue) { addInstruction(new FlushVariableInstruction((DfaVariableValue)arrayVar)); } } } private void generateDefaultAssignmentBinOp(PsiExpression lExpr, PsiExpression rExpr, final PsiType exprType) { lExpr.accept(this); addInstruction(new DupInstruction()); generateBoxingUnboxingInstructionFor(lExpr,exprType); rExpr.accept(this); generateBoxingUnboxingInstructionFor(rExpr, exprType); addInstruction(new BinopInstruction(null, null, lExpr.getProject())); } @Override public void visitAssertStatement(PsiAssertStatement statement) { if (myIgnoreAssertions) { return; } startElement(statement); final PsiExpression condition = statement.getAssertCondition(); final PsiExpression description = statement.getAssertDescription(); if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), false, condition)); if (description != null) { description.accept(this); } initException(myAssertionError); addThrowCode(false, statement); } finishElement(statement); } @Override public void visitDeclarationStatement(PsiDeclarationStatement statement) { startElement(statement); PsiElement[] elements = statement.getDeclaredElements(); for (PsiElement element : elements) { if (element instanceof PsiClass) { addInstruction(new EmptyInstruction(element)); } else if (element instanceof PsiVariable) { PsiVariable variable = (PsiVariable)element; PsiExpression initializer = variable.getInitializer(); if (initializer != null) { initializeVariable(variable, initializer); } } } finishElement(statement); } @Override public void visitField(PsiField field) { PsiExpression initializer = field.getInitializer(); if (initializer != null) { initializeVariable(field, initializer); } } private void initializeVariable(PsiVariable variable, PsiExpression initializer) { DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(variable, false); addInstruction(new PushInstruction(dfaVariable, initializer)); initializer.accept(this); generateBoxingUnboxingInstructionFor(initializer, variable.getType()); addInstruction(new AssignInstruction(initializer)); addInstruction(new PopInstruction()); } @Override public void visitCodeFragment(JavaCodeFragment codeFragment) { startElement(codeFragment); if (codeFragment instanceof PsiExpressionCodeFragment) { PsiExpression expression = ((PsiExpressionCodeFragment)codeFragment).getExpression(); if (expression != null) { expression.accept(this); } } finishElement(codeFragment); } @Override public void visitCodeBlock(PsiCodeBlock block) { startElement(block); for (PsiStatement statement : block.getStatements()) { statement.accept(this); } flushCodeBlockVariables(block); finishElement(block); } private void flushCodeBlockVariables(PsiCodeBlock block) { for (PsiStatement statement : block.getStatements()) { if (statement instanceof PsiDeclarationStatement) { for (PsiElement declaration : ((PsiDeclarationStatement)statement).getDeclaredElements()) { if (declaration instanceof PsiVariable) { myCurrentFlow.removeVariable((PsiVariable)declaration); } } } } 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); } } } } else if (parent instanceof PsiTryStatement) { PsiResourceList list = ((PsiTryStatement)parent).getResourceList(); if (list != null) { for (PsiResourceVariable variable : list.getResourceVariables()) { myCurrentFlow.removeVariable(variable); } } } } @Override public void visitBlockStatement(PsiBlockStatement statement) { startElement(statement); statement.getCodeBlock().accept(this); finishElement(statement); } @Override public void visitBreakStatement(PsiBreakStatement statement) { startElement(statement); PsiStatement exitedStatement = statement.findExitedStatement(); if (exitedStatement != null) { flushVariablesOnControlTransfer(exitedStatement); addInstruction(new GotoInstruction(getEndOffset(exitedStatement))); } finishElement(statement); } @Override public void visitContinueStatement(PsiContinueStatement statement) { startElement(statement); PsiStatement continuedStatement = statement.findContinuedStatement(); if (continuedStatement instanceof PsiLoopStatement) { PsiStatement body = ((PsiLoopStatement)continuedStatement).getBody(); flushVariablesOnControlTransfer(body); addInstruction(new GotoInstruction(getEndOffset(body))); } else { addInstruction(new EmptyInstruction(null)); } finishElement(statement); } @Override public void visitDoWhileStatement(PsiDoWhileStatement statement) { startElement(statement); PsiStatement body = statement.getBody(); if (body != null) { body.accept(this); PsiExpression condition = statement.getCondition(); if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); addInstruction(new ConditionalGotoInstruction(getStartOffset(statement), false, condition)); } } finishElement(statement); } @Override public void visitEmptyStatement(PsiEmptyStatement statement) { startElement(statement); finishElement(statement); } @Override public void visitExpressionStatement(PsiExpressionStatement statement) { startElement(statement); final PsiExpression expr = statement.getExpression(); expr.accept(this); addInstruction(new PopInstruction()); finishElement(statement); } @Override public void visitExpressionListStatement(PsiExpressionListStatement statement) { startElement(statement); PsiExpression[] expressions = statement.getExpressionList().getExpressions(); for (PsiExpression expr : expressions) { expr.accept(this); addInstruction(new PopInstruction()); } finishElement(statement); } @Override public void visitForeachStatement(PsiForeachStatement statement) { startElement(statement); final PsiParameter parameter = statement.getIterationParameter(); final PsiExpression iteratedValue = statement.getIteratedValue(); if (iteratedValue != null) { iteratedValue.accept(this); addInstruction(new FieldReferenceInstruction(iteratedValue, "Collection iterator or array.length")); } ControlFlow.ControlFlowOffset offset = myCurrentFlow.getNextOffset(); DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(parameter, false); addInstruction(new FlushVariableInstruction(dfaVariable)); pushUnknown(); addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, null)); final PsiStatement body = statement.getBody(); if (body != null) { body.accept(this); } addInstruction(new GotoInstruction(offset)); finishElement(statement); myCurrentFlow.removeVariable(parameter); } @Override public void visitForStatement(PsiForStatement statement) { startElement(statement); final ArrayList declaredVariables = new ArrayList(); PsiStatement initialization = statement.getInitialization(); if (initialization != null) { initialization.accept(this); initialization.accept(new JavaRecursiveElementWalkingVisitor() { @Override public void visitReferenceExpression(PsiReferenceExpression expression) { visitElement(expression); } @Override public void visitDeclarationStatement(PsiDeclarationStatement statement) { PsiElement[] declaredElements = statement.getDeclaredElements(); for (PsiElement element : declaredElements) { if (element instanceof PsiVariable) { declaredVariables.add(element); } } } }); } PsiExpression condition = statement.getCondition(); if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); } else { addInstruction(new PushInstruction(statement.getRParenth() == null ? null : myFactory.getConstFactory().getTrue(), null)); } addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, condition)); PsiStatement body = statement.getBody(); if (body != null) { body.accept(this); } PsiStatement update = statement.getUpdate(); if (update != null) { update.accept(this); } ControlFlow.ControlFlowOffset offset = initialization != null ? getEndOffset(initialization) : getStartOffset(statement); addInstruction(new GotoInstruction(offset)); finishElement(statement); for (PsiElement declaredVariable : declaredVariables) { PsiVariable psiVariable = (PsiVariable)declaredVariable; myCurrentFlow.removeVariable(psiVariable); } } @Override public void visitIfStatement(PsiIfStatement statement) { startElement(statement); PsiExpression condition = statement.getCondition(); PsiStatement thenStatement = statement.getThenBranch(); PsiStatement elseStatement = statement.getElseBranch(); ControlFlow.ControlFlowOffset offset = elseStatement != null ? getStartOffset(elseStatement) : getEndOffset(statement); if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); addInstruction(new ConditionalGotoInstruction(offset, true, condition)); } if (thenStatement != null) { thenStatement.accept(this); } if (elseStatement != null) { offset = getEndOffset(statement); Instruction instruction = new GotoInstruction(offset); addInstruction(instruction); elseStatement.accept(this); } finishElement(statement); } // in case of JspTemplateStatement @Override public void visitStatement(PsiStatement statement) { startElement(statement); finishElement(statement); } @Override public void visitLabeledStatement(PsiLabeledStatement statement) { startElement(statement); PsiStatement childStatement = statement.getStatement(); if (childStatement != null) { childStatement.accept(this); } finishElement(statement); } @Override public void visitLambdaExpression(PsiLambdaExpression expression) { startElement(expression); DfaValue dfaValue = myFactory.createValue(expression); addInstruction(new PushInstruction(dfaValue, expression)); addInstruction(new LambdaInstruction(expression)); finishElement(expression); } @Override public void visitReturnStatement(PsiReturnStatement statement) { startElement(statement); PsiExpression returnValue = statement.getReturnValue(); if (returnValue != null) { returnValue.accept(this); PsiMethod method = PsiTreeUtil.getParentOfType(statement, PsiMethod.class, true, PsiMember.class); if (method != null) { generateBoxingUnboxingInstructionFor(returnValue, method.getReturnType()); } addInstruction(new CheckReturnValueInstruction(returnValue)); } returnCheckingFinally(false, statement); finishElement(statement); } private void returnCheckingFinally(boolean viaException, @NotNull PsiElement anchor) { ControlFlow.ControlFlowOffset finallyOffset = getFinallyOffset(); 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(viaException, anchor)); } } @Override public void visitSwitchLabelStatement(PsiSwitchLabelStatement statement) { startElement(statement); finishElement(statement); } @Override public void visitSwitchStatement(PsiSwitchStatement switchStmt) { startElement(switchStmt); PsiExpression caseExpression = switchStmt.getExpression(); Set enumValues = null; if (caseExpression != null /*&& !(caseExpression instanceof PsiReferenceExpression)*/) { caseExpression.accept(this); generateBoxingUnboxingInstructionFor(caseExpression, PsiType.INT); final PsiClass psiClass = PsiUtil.resolveClassInType(caseExpression.getType()); if (psiClass != null && psiClass.isEnum()) { addInstruction(new FieldReferenceInstruction(caseExpression, "switch statement expression")); enumValues = new HashSet(); for (PsiField f : psiClass.getFields()) { if (f instanceof PsiEnumConstant) { enumValues.add((PsiEnumConstant)f); } } } else { addInstruction(new PopInstruction()); } } PsiCodeBlock body = switchStmt.getBody(); if (body != null) { PsiStatement[] statements = body.getStatements(); PsiSwitchLabelStatement defaultLabel = null; for (PsiStatement statement : statements) { if (statement instanceof PsiSwitchLabelStatement) { PsiSwitchLabelStatement psiLabelStatement = (PsiSwitchLabelStatement)statement; if (psiLabelStatement.isDefaultCase()) { defaultLabel = psiLabelStatement; } else { try { ControlFlow.ControlFlowOffset offset = getStartOffset(statement); PsiExpression caseValue = psiLabelStatement.getCaseValue(); if (caseValue != null && caseExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression)caseExpression).getQualifierExpression() == null && JavaPsiFacade.getInstance(body.getProject()).getConstantEvaluationHelper().computeConstantExpression(caseValue) != null) { addInstruction(new PushInstruction(myFactory.createValue(caseExpression), caseExpression)); caseValue.accept(this); addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, caseExpression.getProject())); } else { pushUnknown(); } addInstruction(new ConditionalGotoInstruction(offset, false, statement)); if (enumValues != null) { if (caseValue instanceof PsiReferenceExpression) { //noinspection SuspiciousMethodCalls enumValues.remove(((PsiReferenceExpression)caseValue).resolve()); } } } catch (IncorrectOperationException e) { LOG.error(e); } } } } if (enumValues == null || !enumValues.isEmpty()) { ControlFlow.ControlFlowOffset offset = defaultLabel != null ? getStartOffset(defaultLabel) : getEndOffset(body); addInstruction(new GotoInstruction(offset)); } body.accept(this); } finishElement(switchStmt); } @Override public void visitSynchronizedStatement(PsiSynchronizedStatement statement) { startElement(statement); PsiExpression lock = statement.getLockExpression(); if (lock != null) { lock.accept(this); addInstruction(new FieldReferenceInstruction(lock, "Synchronized value")); } addInstruction(new FlushVariableInstruction(null)); PsiCodeBlock body = statement.getBody(); if (body != null) { body.accept(this); } finishElement(statement); } @Override public void visitThrowStatement(PsiThrowStatement statement) { startElement(statement); PsiExpression exception = statement.getException(); if (exception != null) { exception.accept(this); if (myCatchStack.isEmpty()) { addInstruction(new ReturnInstruction(true, statement)); 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()); initException(myNpe); addThrowCode(false, statement); gotoInstruction.setOffset(myCurrentFlow.getInstructionCount()); addInstruction(new PushInstruction(myExceptionHolder, null)); addInstruction(new SwapInstruction()); addInstruction(new AssignInstruction(null)); addInstruction(new PopInstruction()); addThrowCode(false, statement); } finishElement(statement); } private void addConditionalRuntimeThrow() { if (myCatchStack.isEmpty()) { return; } pushUnknown(); final ConditionalGotoInstruction ifNoException = addInstruction(new ConditionalGotoInstruction(null, false, null)); addInstruction(new EmptyStackInstruction()); addInstruction(new PushInstruction(myExceptionHolder, null)); 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, null); ifNoException.setOffset(myCurrentFlow.getInstructionCount()); } private void flushVariablesOnControlTransfer(PsiElement stopWhenAncestorOf) { for (int i = myElementStack.size() - 1; i >= 0; i--) { PsiElement scope = myElementStack.get(i); if (PsiTreeUtil.isAncestor(scope, stopWhenAncestorOf, true)) { break; } if (scope instanceof PsiCodeBlock) { flushCodeBlockVariables((PsiCodeBlock)scope); } } } // the exception object should be in $exception$ variable private void addThrowCode(boolean catchRethrow, @Nullable PsiElement explicitThrower) { if (myCatchStack.isEmpty()) { addInstruction(new ReturnInstruction(true, explicitThrower)); 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--; } if (i < 0) { addInstruction(new ReturnInstruction(true, explicitThrower)); return; } cd = myCatchStack.get(i); } 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 null; } private static class ApplyNotNullInstruction extends Instruction { private PsiMethodCallExpression myCall; private ApplyNotNullInstruction(PsiMethodCallExpression call) { myCall = call; } @Override public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState state, InstructionVisitor visitor) { DfaValue value = state.pop(); DfaValueFactory factory = runner.getFactory(); if (state.applyCondition( factory.getRelationFactory().createRelation(value, factory.getConstFactory().getNull(), JavaTokenType.EQEQ, true))) { return nextInstruction(runner, state); } if (visitor instanceof StandardInstructionVisitor) { ((StandardInstructionVisitor)visitor).skipConstantConditionReporting(myCall); } return DfaInstructionState.EMPTY_ARRAY; } } private static class CatchDescriptor { private final PsiType myType; private final PsiParameter myParameter; private final PsiCodeBlock myBlock; private final boolean myIsFinally; public CatchDescriptor(PsiCodeBlock finallyBlock) { myType = null; myParameter = null; myBlock = finallyBlock; myIsFinally = true; } public CatchDescriptor(PsiParameter parameter, PsiCodeBlock catchBlock) { myType = parameter.getType(); myParameter = parameter; myBlock = catchBlock; myIsFinally = false; } public PsiCodeBlock getBlock() { return myBlock; } public PsiTryStatement getTryStatement() { return (PsiTryStatement) (isFinally() ? myBlock.getParent() : myBlock.getParent().getParent()); } public PsiType getType() { return myType; } public boolean isFinally() { return myIsFinally; } public ControlFlow.ControlFlowOffset getJumpOffset(ControlFlowAnalyzer analyzer) { return analyzer.getStartOffset(isFinally() ? myBlock : myBlock.getParent()); } public PsiParameter getParameter() { return myParameter; } } @Override public void visitTryStatement(PsiTryStatement statement) { startElement(statement); PsiResourceList resourceList = statement.getResourceList(); PsiCodeBlock tryBlock = statement.getTryBlock(); PsiCodeBlock finallyBlock = statement.getFinallyBlock(); if (finallyBlock != null) { myCatchStack.push(new CatchDescriptor(finallyBlock)); } PsiCatchSection[] sections = statement.getCatchSections(); for (int i = sections.length - 1; i >= 0; i--) { PsiCatchSection section = sections[i]; PsiCodeBlock catchBlock = section.getCatchBlock(); PsiParameter parameter = section.getParameter(); if (parameter != null && catchBlock != null) { PsiType type = parameter.getType(); if (type instanceof PsiClassType || type instanceof PsiDisjunctionType) { myCatchStack.push(new CatchDescriptor(parameter, catchBlock)); continue; } } throw new CannotAnalyzeException(); } ControlFlow.ControlFlowOffset endOffset = finallyBlock == null ? getEndOffset(statement) : getStartOffset(finallyBlock); if (resourceList != null) { resourceList.accept(this); } if (tryBlock != null) { tryBlock.accept(this); } addInstruction(new GotoInstruction(endOffset)); for (PsiCatchSection section : sections) { section.accept(this); addInstruction(new GotoInstruction(endOffset)); myCatchStack.pop(); } if (finallyBlock != null) { myCatchStack.pop(); finallyBlock.accept(this); //if $exception$==null => continue normal execution 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, null); } finishElement(statement); } @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 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, null); // 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 public void visitResourceList(PsiResourceList resourceList) { for (PsiResourceVariable variable : resourceList.getResourceVariables()) { PsiExpression initializer = variable.getInitializer(); if (initializer != null) { initializeVariable(variable, initializer); } PsiMethod closer = PsiUtil.getResourceCloserMethod(variable); if (closer != null) { addMethodThrows(closer, null); } } } @Override public void visitWhileStatement(PsiWhileStatement statement) { startElement(statement); PsiExpression condition = statement.getCondition(); if (condition != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); addInstruction(new ConditionalGotoInstruction(getEndOffset(statement), true, condition)); } PsiStatement body = statement.getBody(); if (body != null) { body.accept(this); } if (condition != null) { addInstruction(new GotoInstruction(getStartOffset(statement))); } finishElement(statement); } @Override public void visitExpressionList(PsiExpressionList list) { startElement(list); PsiExpression[] expressions = list.getExpressions(); for (PsiExpression expression : expressions) { expression.accept(this); } finishElement(list); } @Override public void visitExpression(PsiExpression expression) { startElement(expression); DfaValue dfaValue = myFactory.createValue(expression); addInstruction(new PushInstruction(dfaValue, expression)); finishElement(expression); } @Override public void visitArrayAccessExpression(PsiArrayAccessExpression expression) { startElement(expression); PsiExpression arrayExpression = expression.getArrayExpression(); arrayExpression.accept(this); addInstruction(new FieldReferenceInstruction(expression, null)); PsiExpression indexExpression = expression.getIndexExpression(); if (indexExpression != null) { indexExpression.accept(this); generateBoxingUnboxingInstructionFor(indexExpression, PsiType.INT); addInstruction(new PopInstruction()); } DfaValue toPush = myFactory.createValue(expression); addInstruction(new PushInstruction(toPush != null ? toPush : myFactory.createTypeValue(expression.getType(), Nullness.UNKNOWN), null)); finishElement(expression); } @Override public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) { startElement(expression); PsiType type = expression.getType(); PsiExpression[] initializers = expression.getInitializers(); for (PsiExpression initializer : initializers) { initializer.accept(this); if (type instanceof PsiArrayType) { generateBoxingUnboxingInstructionFor(initializer, ((PsiArrayType)type).getComponentType()); } addInstruction(new PopInstruction()); } pushUnknown(); finishElement(expression); } @Override public void visitPolyadicExpression(PsiPolyadicExpression expression) { startElement(expression); DfaValue dfaValue = myFactory.createValue(expression); if (dfaValue != null) { addInstruction(new PushInstruction(dfaValue, expression)); finishElement(expression); return; } IElementType op = expression.getOperationTokenType(); PsiExpression[] operands = expression.getOperands(); if (operands.length <= 1) { pushUnknown(); finishElement(expression); return; } PsiType type = expression.getType(); if (op == JavaTokenType.ANDAND) { generateAndExpression(operands, type, true); } else if (op == JavaTokenType.OROR) { generateOrExpression(operands, type, true); } else if (op == JavaTokenType.XOR && PsiType.BOOLEAN.equals(type)) { generateXorExpression(expression, operands, type, false); } else if (op == JavaTokenType.AND && PsiType.BOOLEAN.equals(type)) { generateAndExpression(operands, type, false); } else if (op == JavaTokenType.OR && PsiType.BOOLEAN.equals(type)) { generateOrExpression(operands, type, false); } else { generateOther(expression, op, operands, type); } finishElement(expression); } private void generateOther(PsiPolyadicExpression expression, IElementType op, PsiExpression[] operands, PsiType type) { op = substituteBinaryOperation(op, type); PsiExpression lExpr = operands[0]; lExpr.accept(this); PsiType lType = lExpr.getType(); for (int i = 1; i < operands.length; i++) { PsiExpression rExpr = operands[i]; PsiType rType = rExpr.getType(); acceptBinaryRightOperand(op, type, lExpr, lType, rExpr, rType); addInstruction(new BinopInstruction(op, expression.isPhysical() ? expression : null, expression.getProject())); lExpr = rExpr; lType = rType; } } @Nullable private static IElementType substituteBinaryOperation(IElementType op, PsiType type) { if (JavaTokenType.PLUS == op && (type == null || !type.equalsToText(JAVA_LANG_STRING))) { return null; } return op; } private void acceptBinaryRightOperand(@Nullable IElementType op, PsiType type, PsiExpression lExpr, PsiType lType, PsiExpression rExpr, PsiType rType) { boolean comparing = op == JavaTokenType.EQEQ || op == JavaTokenType.NE; boolean comparingRef = comparing && !TypeConversionUtil.isPrimitiveAndNotNull(lType) && !TypeConversionUtil.isPrimitiveAndNotNull(rType); boolean comparingPrimitiveNumeric = comparing && TypeConversionUtil.isPrimitiveAndNotNull(lType) && TypeConversionUtil.isPrimitiveAndNotNull(rType) && TypeConversionUtil.isNumericType(lType) && TypeConversionUtil.isNumericType(rType); PsiType castType = comparingPrimitiveNumeric ? TypeConversionUtil.isFloatOrDoubleType(lType) ? PsiType.DOUBLE : PsiType.LONG : type; if (!comparingRef) { generateBoxingUnboxingInstructionFor(lExpr,castType); } rExpr.accept(this); if (!comparingRef) { generateBoxingUnboxingInstructionFor(rExpr, castType); } } private void generateBoxingUnboxingInstructionFor(PsiExpression expression, PsiType expectedType) { PsiType exprType = expression.getType(); if (TypeConversionUtil.isPrimitiveAndNotNull(expectedType) && TypeConversionUtil.isPrimitiveWrapper(exprType)) { addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.UNBOXING, expectedType)); } else if (TypeConversionUtil.isAssignableFromPrimitiveWrapper(expectedType) && TypeConversionUtil.isPrimitiveAndNotNull(exprType)) { addConditionalRuntimeThrow(); addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.BOXING, expectedType)); } else if (exprType != expectedType && TypeConversionUtil.isPrimitiveAndNotNull(exprType) && TypeConversionUtil.isPrimitiveAndNotNull(expectedType) && TypeConversionUtil.isNumericType(exprType) && TypeConversionUtil.isNumericType(expectedType)) { addInstruction(new MethodCallInstruction(expression, MethodCallInstruction.MethodType.CAST, expectedType) { @Override public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) { return visitor.visitCast(this, runner, stateBefore); } }); } } private void generateXorExpression(PsiExpression expression, PsiExpression[] operands, final PsiType exprType, boolean forAssignment) { PsiExpression operand = operands[0]; operand.accept(this); if (forAssignment) { addInstruction(new DupInstruction()); } generateBoxingUnboxingInstructionFor(operand, exprType); for (int i = 1; i < operands.length; i++) { operand = operands[i]; operand.accept(this); generateBoxingUnboxingInstructionFor(operand, exprType); PsiElement psiAnchor = expression.isPhysical() ? expression : null; addInstruction(new BinopInstruction(JavaTokenType.NE, psiAnchor, expression.getProject())); } } private void generateOrExpression(PsiExpression[] operands, final PsiType exprType, boolean shortCircuit) { for (int i = 0; i < operands.length; i++) { PsiExpression operand = operands[i]; operand.accept(this); generateBoxingUnboxingInstructionFor(operand, exprType); if (!shortCircuit) { if (i > 0) { combineStackBooleans(false, operand); } continue; } PsiExpression nextOperand = i == operands.length - 1 ? null : operands[i + 1]; if (nextOperand != null) { addInstruction(new ConditionalGotoInstruction(getStartOffset(nextOperand), true, operand)); addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1]))); } } } private void generateBooleanAssignmentExpression(boolean and, PsiExpression lExpression, PsiExpression rExpression, PsiType exprType) { lExpression.accept(this); generateBoxingUnboxingInstructionFor(lExpression, exprType); addInstruction(new DupInstruction()); rExpression.accept(this); generateBoxingUnboxingInstructionFor(rExpression, exprType); addInstruction(new SwapInstruction()); combineStackBooleans(and, lExpression); } private void combineStackBooleans(boolean and, PsiExpression anchor) { ConditionalGotoInstruction toPopAndPushSuccess = new ConditionalGotoInstruction(null, and, anchor); addInstruction(toPopAndPushSuccess); GotoInstruction overPushSuccess = new GotoInstruction(null); addInstruction(overPushSuccess); PopInstruction pop = new PopInstruction(); addInstruction(pop); DfaConstValue constValue = and ? myFactory.getConstFactory().getFalse() : myFactory.getConstFactory().getTrue(); PushInstruction pushSuccess = new PushInstruction(constValue, null); addInstruction(pushSuccess); toPopAndPushSuccess.setOffset(pop.getIndex()); overPushSuccess.setOffset(pushSuccess.getIndex() + 1); } private void generateAndExpression(PsiExpression[] operands, final PsiType exprType, boolean shortCircuit) { List branchToFail = new ArrayList(); for (int i = 0; i < operands.length; i++) { PsiExpression operand = operands[i]; operand.accept(this); generateBoxingUnboxingInstructionFor(operand, exprType); if (!shortCircuit) { if (i > 0) { combineStackBooleans(true, operand); } continue; } ConditionalGotoInstruction onFail = new ConditionalGotoInstruction(null, true, operand); branchToFail.add(onFail); addInstruction(onFail); } if (!shortCircuit) { return; } addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); GotoInstruction toSuccess = new GotoInstruction(null); addInstruction(toSuccess); PushInstruction pushFalse = new PushInstruction(myFactory.getConstFactory().getFalse(), null); addInstruction(pushFalse); for (ConditionalGotoInstruction toFail : branchToFail) { toFail.setOffset(pushFalse.getIndex()); } toSuccess.setOffset(pushFalse.getIndex()+1); } @Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { startElement(expression); PsiElement[] children = expression.getChildren(); for (PsiElement child : children) { child.accept(this); } pushUnknown(); finishElement(expression); } @Override public void visitConditionalExpression(PsiConditionalExpression expression) { startElement(expression); PsiExpression condition = expression.getCondition(); PsiExpression thenExpression = expression.getThenExpression(); PsiExpression elseExpression = expression.getElseExpression(); final ControlFlow.ControlFlowOffset elseOffset = elseExpression == null ? ControlFlow.deltaOffset(getEndOffset(expression), -1) : getStartOffset(elseExpression); if (thenExpression != null) { condition.accept(this); generateBoxingUnboxingInstructionFor(condition, PsiType.BOOLEAN); PsiType type = expression.getType(); addInstruction(new ConditionalGotoInstruction(elseOffset, true, condition)); thenExpression.accept(this); generateBoxingUnboxingInstructionFor(thenExpression,type); addInstruction(new GotoInstruction(getEndOffset(expression))); if (elseExpression != null) { elseExpression.accept(this); generateBoxingUnboxingInstructionFor(elseExpression,type); } else { pushUnknown(); } } else { pushUnknown(); } finishElement(expression); } private void pushUnknown() { addInstruction(new PushInstruction(DfaUnknownValue.getInstance(), null)); } @Override public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { startElement(expression); PsiExpression operand = expression.getOperand(); PsiTypeElement checkType = expression.getCheckType(); if (checkType != null) { operand.accept(this); PsiType type = checkType.getType(); if (type instanceof PsiClassType) { type = ((PsiClassType)type).rawType(); } addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.UNKNOWN), null)); addInstruction(new InstanceofInstruction(expression, expression.getProject(), operand, type)); } else { pushUnknown(); } finishElement(expression); } private void addMethodThrows(PsiMethod method, @Nullable PsiElement explicitCall) { if (method != null) { PsiClassType[] refs = method.getThrowsList().getReferencedTypes(); for (PsiClassType ref : refs) { pushUnknown(); ConditionalGotoInstruction cond = new ConditionalGotoInstruction(null, false, null); addInstruction(cond); addInstruction(new EmptyStackInstruction()); initException(ref); addThrowCode(false, explicitCall); cond.setOffset(myCurrentFlow.getInstructionCount()); } } } private void initException(PsiType ref) { addInstruction(new PushInstruction(myExceptionHolder, null)); addInstruction(new PushInstruction(myFactory.createTypeValue(ref, Nullness.NOT_NULL), null)); addInstruction(new AssignInstruction(null)); addInstruction(new PopInstruction()); } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { startElement(expression); PsiReferenceExpression methodExpression = expression.getMethodExpression(); PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (qualifierExpression != null) { qualifierExpression.accept(this); } else { pushUnknown(); } PsiExpression[] expressions = expression.getArgumentList().getExpressions(); PsiElement method = methodExpression.resolve(); PsiParameter[] parameters = method instanceof PsiMethod ? ((PsiMethod)method).getParameterList().getParameters() : null; boolean isEqualsCall = expressions.length == 1 && method instanceof PsiMethod && "equals".equals(((PsiMethod)method).getName()) && parameters.length == 1 && parameters[0].getType().equalsToText(JAVA_LANG_OBJECT) && PsiType.BOOLEAN.equals(((PsiMethod)method).getReturnType()); for (int i = 0; i < expressions.length; i++) { PsiExpression paramExpr = expressions[i]; paramExpr.accept(this); if (parameters != null && i < parameters.length) { generateBoxingUnboxingInstructionFor(paramExpr, parameters[i].getType()); } if (i == 0 && isEqualsCall) { // stack: .., qualifier, arg1 addInstruction(new SwapInstruction()); // stack: .., arg1, qualifier addInstruction(new DupInstruction(2, 1)); // stack: .., arg1, qualifier, arg1, qualifier addInstruction(new PopInstruction()); // stack: .., arg1, qualifier, arg1 } } addConditionalRuntimeThrow(); List contracts = method instanceof PsiMethod ? getMethodCallContracts((PsiMethod)method, expression) : Collections.emptyList(); addInstruction(new MethodCallInstruction(expression, myFactory.createValue(expression), contracts)); if (!contracts.isEmpty()) { // if a contract resulted in 'fail', handle it addInstruction(new DupInstruction()); addInstruction(new PushInstruction(myFactory.getConstFactory().getContractFail(), null)); addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject())); ConditionalGotoInstruction ifNotFail = new ConditionalGotoInstruction(null, true, null); addInstruction(ifNotFail); returnCheckingFinally(true, expression); ifNotFail.setOffset(myCurrentFlow.getInstructionCount()); } if (!myCatchStack.isEmpty()) { addMethodThrows(expression.resolveMethod(), expression); } if (isEqualsCall) { // assume equals argument must be not-null if the result is true // don't assume the call result to be false if arg1==null // stack: .., arg1, call-result ConditionalGotoInstruction ifFalse = addInstruction(new ConditionalGotoInstruction(null, true, null)); addInstruction(new ApplyNotNullInstruction(expression)); addInstruction(new PushInstruction(myFactory.getConstFactory().getTrue(), null)); addInstruction(new GotoInstruction(getEndOffset(expression))); ifFalse.setOffset(myCurrentFlow.getInstructionCount()); addInstruction(new PopInstruction()); addInstruction(new PushInstruction(myFactory.getConstFactory().getFalse(), null)); } finishElement(expression); } private static List getMethodCallContracts(@NotNull final PsiMethod method, @NotNull PsiMethodCallExpression call) { List contracts = HardcodedContracts.getHardcodedContracts(method, call); return !contracts.isEmpty() ? contracts : getMethodContracts(method); } static List getMethodContracts(@NotNull final PsiMethod method) { final PsiAnnotation contractAnno = findContractAnnotation(method); final int paramCount = method.getParameterList().getParametersCount(); if (contractAnno != null) { return CachedValuesManager.getCachedValue(contractAnno, new CachedValueProvider>() { @Nullable @Override public Result> compute() { String text = AnnotationUtil.getStringAttributeValue(contractAnno, null); if (text != null) { try { List applicable = ContainerUtil.filter(MethodContract.parseContract(text), new Condition() { @Override public boolean value(MethodContract contract) { return contract.arguments.length == paramCount; } }); return Result.create(applicable, contractAnno); } catch (Exception ignored) { } } return Result.create(Collections.emptyList(), contractAnno); } }); } return Collections.emptyList(); } @Nullable public static PsiAnnotation findContractAnnotation(PsiMethod method) { return AnnotationUtil.findAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); } @Override public void visitNewExpression(PsiNewExpression expression) { startElement(expression); pushUnknown(); if (expression.getType() instanceof PsiArrayType) { final PsiExpression[] dimensions = expression.getArrayDimensions(); for (final PsiExpression dimension : dimensions) { dimension.accept(this); } for (PsiExpression ignored : dimensions) { addInstruction(new PopInstruction()); } final PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer(); if (arrayInitializer != null) { for (final PsiExpression initializer : arrayInitializer.getInitializers()) { initializer.accept(this); addInstruction(new PopInstruction()); } } addConditionalRuntimeThrow(); addInstruction(new MethodCallInstruction(expression, null, Collections.emptyList())); } else { final PsiExpressionList args = expression.getArgumentList(); PsiMethod ctr = expression.resolveConstructor(); if (args != null) { PsiExpression[] params = args.getExpressions(); PsiParameter[] parameters = ctr == null ? null : ctr.getParameterList().getParameters(); for (int i = 0; i < params.length; i++) { PsiExpression param = params[i]; param.accept(this); if (parameters != null && i < parameters.length) { generateBoxingUnboxingInstructionFor(param, parameters[i].getType()); } } } addConditionalRuntimeThrow(); addInstruction(new MethodCallInstruction(expression, null, Collections.emptyList())); if (!myCatchStack.isEmpty()) { addMethodThrows(ctr, expression); } } finishElement(expression); } @Override public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { startElement(expression); PsiExpression inner = expression.getExpression(); if (inner != null) { inner.accept(this); } else { pushUnknown(); } finishElement(expression); } @Override public void visitPostfixExpression(PsiPostfixExpression expression) { startElement(expression); PsiExpression operand = expression.getOperand(); operand.accept(this); generateBoxingUnboxingInstructionFor(operand, PsiType.INT); addInstruction(new PopInstruction()); pushUnknown(); if (operand instanceof PsiReferenceExpression) { PsiVariable psiVariable = DfaValueFactory.resolveUnqualifiedVariable((PsiReferenceExpression)expression.getOperand()); if (psiVariable != null) { DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false); addInstruction(new FlushVariableInstruction(dfaVariable)); if (psiVariable instanceof PsiField) { addInstruction(new FlushVariableInstruction(null)); } } } finishElement(expression); } @Override public void visitPrefixExpression(PsiPrefixExpression expression) { startElement(expression); DfaValue dfaValue = myFactory.createValue(expression); if (dfaValue == null) { PsiExpression operand = expression.getOperand(); if (operand == null) { pushUnknown(); } else { operand.accept(this); PsiType type = expression.getType(); PsiPrimitiveType unboxed = PsiPrimitiveType.getUnboxedType(type); generateBoxingUnboxingInstructionFor(operand, unboxed == null ? type : unboxed); if (expression.getOperationTokenType() == JavaTokenType.EXCL) { addInstruction(new NotInstruction()); } else { addInstruction(new PopInstruction()); pushUnknown(); if (operand instanceof PsiReferenceExpression) { PsiVariable psiVariable = DfaValueFactory.resolveUnqualifiedVariable((PsiReferenceExpression)operand); if (psiVariable != null) { DfaVariableValue dfaVariable = myFactory.getVarFactory().createVariableValue(psiVariable, false); addInstruction(new FlushVariableInstruction(dfaVariable)); if (psiVariable instanceof PsiField) { addInstruction(new FlushVariableInstruction(null)); } } } } } } else { addInstruction(new PushInstruction(dfaValue, expression)); } finishElement(expression); } @Override public void visitReferenceExpression(PsiReferenceExpression expression) { startElement(expression); final PsiExpression qualifierExpression = expression.getQualifierExpression(); if (qualifierExpression != null) { qualifierExpression.accept(this); addInstruction(expression.resolve() instanceof PsiField ? new FieldReferenceInstruction(expression, null) : new PopInstruction()); } boolean referenceRead = PsiUtil.isAccessedForReading(expression) && !PsiUtil.isAccessedForWriting(expression); addInstruction(new PushInstruction(myFactory.createValue(expression), expression, referenceRead)); finishElement(expression); } @Override public void visitSuperExpression(PsiSuperExpression expression) { startElement(expression); addInstruction(new PushInstruction(myFactory.createTypeValue(expression.getType(), Nullness.NOT_NULL), null)); finishElement(expression); } @Override public void visitThisExpression(PsiThisExpression expression) { startElement(expression); addInstruction(new PushInstruction(myFactory.createTypeValue(expression.getType(), Nullness.NOT_NULL), null)); finishElement(expression); } @Override public void visitLiteralExpression(PsiLiteralExpression expression) { startElement(expression); DfaValue dfaValue = myFactory.createLiteralValue(expression); addInstruction(new PushInstruction(dfaValue, expression)); finishElement(expression); } @Override public void visitTypeCastExpression(PsiTypeCastExpression castExpression) { startElement(castExpression); PsiExpression operand = castExpression.getOperand(); if (operand != null) { operand.accept(this); generateBoxingUnboxingInstructionFor(operand, castExpression.getType()); } else { addInstruction(new PushInstruction(myFactory.createTypeValue(castExpression.getType(), Nullness.UNKNOWN), null)); } final PsiTypeElement typeElement = castExpression.getCastType(); if (typeElement != null && operand != null && operand.getType() != null) { if (typeElement.getType() instanceof PsiPrimitiveType && UnnecessaryExplicitNumericCastInspection.isPrimitiveNumericCastNecessary(castExpression)) { addInstruction(new PopInstruction()); pushUnknown(); } else { addInstruction(new TypeCastInstruction(castExpression, operand, typeElement.getType())); } } finishElement(castExpression); } @Override public void visitClass(PsiClass aClass) { } }