/******************************************************************************* * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.corext.refactoring.code.flow; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor; import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTBreakStatement; import org.eclipse.cdt.core.dom.ast.IASTCaseStatement; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; import org.eclipse.cdt.core.dom.ast.IASTContinueStatement; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTDefaultStatement; import org.eclipse.cdt.core.dom.ast.IASTDoStatement; import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTExpressionList; import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement; import org.eclipse.cdt.core.dom.ast.IASTFieldReference; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTForStatement; import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTGotoStatement; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; import org.eclipse.cdt.core.dom.ast.IASTIfStatement; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; import org.eclipse.cdt.core.dom.ast.IASTInitializerList; import org.eclipse.cdt.core.dom.ast.IASTLabelStatement; import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNullStatement; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTProblemExpression; import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression; import org.eclipse.cdt.core.dom.ast.IASTTypeIdInitializerExpression; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IField; import org.eclipse.cdt.core.dom.ast.IVariable; import org.eclipse.cdt.core.dom.ast.c.ICASTDesignatedInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTWhileStatement; import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVariableReadWriteFlags; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; import org.eclipse.cdt.internal.core.pdom.dom.PDOMName; /** * Special flow analyzer to determine the return value of the extracted method * and the variables which have to be passed to the method. * * Note: This analyzer doesn't do a full flow analysis. For example it doesn't * do dead code analysis or variable initialization analysis. It analyzes * the first access to a variable (read or write) and if all execution paths * return a value. */ abstract class FlowAnalyzer extends ASTGenericVisitor { static protected class SwitchData { private boolean fHasDefaultCase; private final List<IRegion> fRanges= new ArrayList<IRegion>(4); private final List<FlowInfo> fInfos= new ArrayList<FlowInfo>(4); public void setHasDefaultCase() { fHasDefaultCase= true; } public boolean hasDefaultCase() { return fHasDefaultCase; } public void add(IRegion range, FlowInfo info) { fRanges.add(range); fInfos.add(info); } public IRegion[] getRanges() { return fRanges.toArray(new IRegion[fRanges.size()]); } public FlowInfo[] getInfos() { return fInfos.toArray(new FlowInfo[fInfos.size()]); } public FlowInfo getInfo(int index) { return fInfos.get(index); } } private final HashMap<IASTNode, FlowInfo> fData = new HashMap<IASTNode, FlowInfo>(100); FlowContext fFlowContext; public FlowAnalyzer(FlowContext context) { super(true); fFlowContext= context; } protected abstract boolean shouldCreateReturnFlowInfo(IASTReturnStatement node); protected abstract boolean traverseNode(IASTNode node); protected boolean skipNode(IASTNode node) { return !traverseNode(node); } @Override protected final int genericVisit(IASTNode node) { return traverseNode(node) ? PROCESS_CONTINUE : PROCESS_SKIP; } //---- Hooks to create Flow info objects. User may introduce their own infos. protected ReturnFlowInfo createReturn(IASTReturnStatement statement) { return new ReturnFlowInfo(statement); } protected ThrowFlowInfo createThrow() { return new ThrowFlowInfo(); } protected BranchFlowInfo createBranch(IASTName label) { return new BranchFlowInfo(label, fFlowContext); } protected GenericSequentialFlowInfo createSequential() { return new GenericSequentialFlowInfo(); } protected ConditionalFlowInfo createConditional() { return new ConditionalFlowInfo(); } protected RangeBasedForFlowInfo createRangeBasedFor() { return new RangeBasedForFlowInfo(); } protected ForFlowInfo createFor() { return new ForFlowInfo(); } protected TryFlowInfo createTry() { return new TryFlowInfo(); } protected WhileFlowInfo createWhile() { return new WhileFlowInfo(); } protected IfFlowInfo createIf() { return new IfFlowInfo(); } protected DoWhileFlowInfo createDoWhile() { return new DoWhileFlowInfo(); } protected SwitchFlowInfo createSwitch() { return new SwitchFlowInfo(); } protected BlockFlowInfo createBlock() { return new BlockFlowInfo(); } protected FunctionCallFlowInfo createFunctionCallFlowInfo() { return new FunctionCallFlowInfo(); } protected FlowContext getFlowContext() { return fFlowContext; } //---- Helpers to access flow analysis objects ---------------------------------------- protected FlowInfo getFlowInfo(IASTNode node) { return fData.remove(node); } protected void setFlowInfo(IASTNode node, FlowInfo info) { fData.put(node, info); } protected FlowInfo assignFlowInfo(IASTNode target, IASTNode source) { FlowInfo result= getFlowInfo(source); setFlowInfo(target, result); return result; } protected FlowInfo accessFlowInfo(IASTNode node) { return fData.get(node); } //---- Helpers to process sequential flow infos ------------------------------------- protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode[] nodes) { GenericSequentialFlowInfo result= createSequential(parent); process(result, nodes); return result; } protected GenericSequentialFlowInfo processSequential(IASTNode parent, Iterable<IASTNode> nodes) { GenericSequentialFlowInfo result= createSequential(parent); process(result, nodes); return result; } protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode node) { GenericSequentialFlowInfo result= createSequential(parent); if (node != null) result.merge(getFlowInfo(node), fFlowContext); return result; } protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode node1, IASTNode node2) { GenericSequentialFlowInfo result= createSequential(parent); if (node1 != null) result.merge(getFlowInfo(node1), fFlowContext); if (node2 != null) result.merge(getFlowInfo(node2), fFlowContext); return result; } protected GenericSequentialFlowInfo createSequential(IASTNode parent) { GenericSequentialFlowInfo result= createSequential(); setFlowInfo(parent, result); return result; } protected GenericSequentialFlowInfo createSequential(IASTNode[] nodes) { GenericSequentialFlowInfo result= createSequential(); process(result, nodes); return result; } //---- Generic merge methods -------------------------------------------------------- protected void process(GenericSequentialFlowInfo info, IASTNode[] nodes) { if (nodes == null) return; for (IASTNode node : nodes) { info.merge(getFlowInfo(node), fFlowContext); } } protected void process(GenericSequentialFlowInfo info, Iterable<IASTNode> nodes) { if (nodes == null) return; for (IASTNode node : nodes) { info.merge(getFlowInfo(node), fFlowContext); } } protected void process(GenericSequentialFlowInfo info, IASTNode node) { if (node != null) info.merge(getFlowInfo(node), fFlowContext); } protected void process(GenericSequentialFlowInfo info, IASTNode node1, IASTNode node2) { if (node1 != null) info.merge(getFlowInfo(node1), fFlowContext); if (node2 != null) info.merge(getFlowInfo(node2), fFlowContext); } //---- special visit methods ------------------------------------------------------- @Override public int visit(IASTStatement node) { if (skipNode(node)) return PROCESS_SKIP; if (node instanceof IASTBreakStatement) { return visit((IASTBreakStatement) node); } else if (node instanceof IASTCaseStatement) { return visit((IASTCaseStatement) node); } else if (node instanceof IASTCompoundStatement) { return visit((IASTCompoundStatement) node); } else if (node instanceof IASTContinueStatement) { return visit((IASTContinueStatement) node); } else if (node instanceof IASTDeclarationStatement) { return visit((IASTDeclarationStatement) node); } else if (node instanceof IASTDefaultStatement) { return visit((IASTDefaultStatement) node); } else if (node instanceof IASTDoStatement) { return visit((IASTDoStatement) node); } else if (node instanceof IASTExpressionStatement) { return visit((IASTExpressionStatement) node); } else if (node instanceof IASTForStatement) { return visit((IASTForStatement) node); } else if (node instanceof IASTGotoStatement) { return visit((IASTGotoStatement) node); } else if (node instanceof IASTIfStatement) { return visit((IASTIfStatement) node); } else if (node instanceof IASTLabelStatement) { return visit((IASTLabelStatement) node); } else if (node instanceof IASTNullStatement) { return visit((IASTNullStatement) node); } else if (node instanceof IASTReturnStatement) { return visit((IASTReturnStatement) node); } else if (node instanceof IASTSwitchStatement) { return visit((IASTSwitchStatement) node); } else if (node instanceof IASTWhileStatement) { return visit((IASTWhileStatement) node); } else if (node instanceof ICPPASTCatchHandler) { return visit((ICPPASTCatchHandler) node); } else if (node instanceof ICPPASTRangeBasedForStatement) { return visit((ICPPASTRangeBasedForStatement) node); } else if (node instanceof ICPPASTTryBlockStatement) { return visit((ICPPASTTryBlockStatement) node); } return PROCESS_CONTINUE; } public int visit(IASTBreakStatement node) { return PROCESS_CONTINUE; } public int visit(IASTCaseStatement node) { return PROCESS_CONTINUE; } public int visit(IASTDefaultStatement node) { return PROCESS_CONTINUE; } public int visit(IASTCompoundStatement node) { return PROCESS_CONTINUE; } public int visit(IASTContinueStatement node) { return PROCESS_CONTINUE; } public int visit(IASTDeclarationStatement node) { return PROCESS_CONTINUE; } public int visit(IASTDoStatement node) { return PROCESS_CONTINUE; } public int visit(IASTExpressionStatement node) { return PROCESS_CONTINUE; } public int visit(IASTForStatement node) { return PROCESS_CONTINUE; } public int visit(IASTGotoStatement node) { return PROCESS_CONTINUE; } public int visit(IASTIfStatement node) { return PROCESS_CONTINUE; } public int visit(IASTLabelStatement node) { return PROCESS_CONTINUE; } public int visit(IASTNullStatement node) { // Null statements aren't of any interest. return PROCESS_SKIP; } public int visit(ICPPASTRangeBasedForStatement node) { return PROCESS_CONTINUE; } public int visit(ICPPASTTryBlockStatement node) { if (traverseNode(node)) { fFlowContext.pushExceptions(node); node.getTryBody().accept(this); fFlowContext.popExceptions(); for (ICPPASTCatchHandler catchHandler : node.getCatchHandlers()) { catchHandler.accept(this); } } return PROCESS_SKIP; } public int visit(ICPPASTCatchHandler node) { return PROCESS_CONTINUE; } public int visit(IASTReturnStatement node) { return PROCESS_CONTINUE; } public int visit(IASTSwitchStatement node) { return PROCESS_CONTINUE; } public int visit(IASTWhileStatement node) { return PROCESS_CONTINUE; } //---- Helper to process switch statement ---------------------------------------- protected SwitchData createSwitchData(IASTSwitchStatement node) { SwitchData result= new SwitchData(); IASTStatement[] statements; IASTStatement body = node.getBody(); if (body instanceof IASTCompoundStatement) { statements = ((IASTCompoundStatement) body).getStatements(); } else { statements = new IASTStatement[] { body }; } if (statements.length == 0) return result; int start= -1; int end= -1; GenericSequentialFlowInfo info= null; for (IASTStatement statement : statements) { IASTFileLocation location = statement.getFileLocation(); if (statement instanceof IASTCaseStatement || statement instanceof IASTDefaultStatement) { if (statement instanceof IASTDefaultStatement) { result.setHasDefaultCase(); } if (info == null) { info= createSequential(); start= location.getNodeOffset(); } else { if (info.isReturn() || info.isPartialReturn() || info.branches()) { result.add(new Region(start, end - start + 1), info); info= createSequential(); start= location.getNodeOffset(); } } } else { if (info == null) { info= createSequential(); start= location.getNodeOffset(); } else { info.merge(getFlowInfo(statement), fFlowContext); } } end= location.getNodeOffset() + location.getNodeLength() - 1; } result.add(new Region(start, end - start + 1), info); return result; } //---- Concrete leave methods --------------------------------------------------- @Override public int leave(IASTStatement node) { if (skipNode(node)) return PROCESS_SKIP; if (node instanceof IASTBreakStatement) { return leave((IASTBreakStatement) node); } else if (node instanceof IASTCaseStatement) { return leave((IASTCaseStatement) node); } else if (node instanceof IASTCompoundStatement) { return leave((IASTCompoundStatement) node); } else if (node instanceof IASTContinueStatement) { return leave((IASTContinueStatement) node); } else if (node instanceof IASTDeclarationStatement) { return leave((IASTDeclarationStatement) node); } else if (node instanceof IASTDefaultStatement) { return leave((IASTDefaultStatement) node); } else if (node instanceof IASTDoStatement) { return leave((IASTDoStatement) node); } else if (node instanceof IASTExpressionStatement) { return leave((IASTExpressionStatement) node); } else if (node instanceof IASTForStatement) { return leave((IASTForStatement) node); } else if (node instanceof IASTGotoStatement) { return leave((IASTGotoStatement) node); } else if (node instanceof IASTIfStatement) { return leave((IASTIfStatement) node); } else if (node instanceof IASTLabelStatement) { return leave((IASTLabelStatement) node); } else if (node instanceof IASTNullStatement) { return leave((IASTNullStatement) node); } else if (node instanceof IASTReturnStatement) { return leave((IASTReturnStatement) node); } else if (node instanceof IASTSwitchStatement) { return leave((IASTSwitchStatement) node); } else if (node instanceof IASTWhileStatement) { return leave((IASTWhileStatement) node); } else if (node instanceof ICPPASTCatchHandler) { return leave((ICPPASTCatchHandler) node); } else if (node instanceof ICPPASTRangeBasedForStatement) { return leave((ICPPASTRangeBasedForStatement) node); } else if (node instanceof ICPPASTTryBlockStatement) { return leave((ICPPASTTryBlockStatement) node); } return PROCESS_SKIP; } public int leave(IASTBreakStatement node) { setFlowInfo(node, createBranch(null)); return PROCESS_SKIP; } public int leave(IASTCaseStatement node) { // Nothing to do return PROCESS_SKIP; } public int leave(IASTDefaultStatement node) { // Nothing to do return PROCESS_SKIP; } public int leave(IASTCompoundStatement node) { BlockFlowInfo info= createBlock(); setFlowInfo(node, info); process(info, node.getStatements()); return PROCESS_SKIP; } public int leave(IASTContinueStatement node) { setFlowInfo(node, createBranch(null)); return PROCESS_SKIP; } public int leave(IASTDeclarationStatement node) { processSequential(node, node.getDeclaration()); return PROCESS_SKIP; } public int leave(IASTDoStatement node) { DoWhileFlowInfo info= createDoWhile(); setFlowInfo(node, info); info.mergeAction(getFlowInfo(node.getBody()), fFlowContext); info.mergeCondition(getFlowInfo(node.getCondition()), fFlowContext); info.removeLabel(null); return PROCESS_SKIP; } public int leave(IASTExpressionStatement node) { assignFlowInfo(node, node.getExpression()); return PROCESS_SKIP; } public int leave(IASTForStatement node) { ForFlowInfo forInfo= createFor(); setFlowInfo(node, forInfo); forInfo.mergeInitializer(createSequential(node.getInitializerStatement()), fFlowContext); forInfo.mergeCondition(getFlowInfo(node.getConditionExpression()), fFlowContext); forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext); // Increments are executed after the body. forInfo.mergeIncrement(createSequential(node.getIterationExpression()), fFlowContext); forInfo.removeLabel(null); return PROCESS_SKIP; } public int leave(IASTGotoStatement node) { setFlowInfo(node, createBranch(node.getName())); return PROCESS_SKIP; } public int leave(IASTIfStatement node) { IfFlowInfo info= createIf(); setFlowInfo(node, info); info.mergeCondition(getFlowInfo(node.getConditionExpression()), fFlowContext); info.merge(getFlowInfo(node.getThenClause()), getFlowInfo(node.getElseClause()), fFlowContext); return PROCESS_SKIP; } public int leave(IASTLabelStatement node) { assignFlowInfo(node, node.getNestedStatement()); return PROCESS_SKIP; } public int leave(IASTNullStatement node) { // Leaf node. return PROCESS_SKIP; } public int leave(ICPPASTRangeBasedForStatement node) { RangeBasedForFlowInfo forInfo= createRangeBasedFor(); setFlowInfo(node, forInfo); forInfo.mergeDeclaration(getFlowInfo(node.getDeclaration()), fFlowContext); forInfo.mergeInitializerClause(getFlowInfo(node.getInitializerClause()), fFlowContext); forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext); forInfo.removeLabel(null); return PROCESS_SKIP; } public int leave(ICPPASTTryBlockStatement node) { TryFlowInfo info= createTry(); setFlowInfo(node, info); info.mergeTry(getFlowInfo(node.getTryBody()), fFlowContext); for (ICPPASTCatchHandler catchHandler : node.getCatchHandlers()) { info.mergeCatch(getFlowInfo(catchHandler), fFlowContext); } return PROCESS_SKIP; } public int leave(ICPPASTCatchHandler node) { processSequential(node, node.getDeclaration(), node.getCatchBody()); return PROCESS_SKIP; } public int leave(IASTReturnStatement node) { if (shouldCreateReturnFlowInfo(node)) { ReturnFlowInfo info= createReturn(node); setFlowInfo(node, info); info.merge(getFlowInfo(node.getReturnArgument()), fFlowContext); } else { assignFlowInfo(node, node.getReturnArgument()); } return PROCESS_SKIP; } public int leave(IASTSwitchStatement node) { return leave(node, createSwitchData(node)); } protected int leave(IASTSwitchStatement node, SwitchData data) { SwitchFlowInfo switchFlowInfo= createSwitch(); setFlowInfo(node, switchFlowInfo); switchFlowInfo.mergeTest(getFlowInfo(node.getControllerExpression()), fFlowContext); FlowInfo[] cases= data.getInfos(); for (int i= 0; i < cases.length; i++) { switchFlowInfo.mergeCase(cases[i], fFlowContext); } switchFlowInfo.mergeDefault(data.hasDefaultCase(), fFlowContext); switchFlowInfo.removeLabel(null); return PROCESS_SKIP; } public int leave(IASTWhileStatement node) { WhileFlowInfo info= createWhile(); setFlowInfo(node, info); info.mergeCondition(getFlowInfo(node.getCondition()), fFlowContext); if (node instanceof ICPPASTWhileStatement) { info.mergeCondition(getFlowInfo(((ICPPASTWhileStatement) node).getConditionDeclaration()), fFlowContext); } info.mergeAction(getFlowInfo(node.getBody()), fFlowContext); info.removeLabel(null); return PROCESS_SKIP; } @Override public int leave(IASTExpression node) { if (skipNode(node)) return PROCESS_SKIP; if (node instanceof IASTArraySubscriptExpression) { return leave((IASTArraySubscriptExpression) node); } else if (node instanceof IASTConditionalExpression) { return leave((IASTConditionalExpression) node); } else if (node instanceof IASTFunctionCallExpression) { return leave((IASTFunctionCallExpression) node); } else if (node instanceof IASTExpressionList) { return leave((IASTExpressionList) node); } else if (node instanceof IASTTypeIdExpression) { return leave((IASTTypeIdExpression) node); } else if (node instanceof IASTBinaryExpression) { return leave((IASTBinaryExpression) node); } else if (node instanceof IASTLiteralExpression) { return leave((IASTLiteralExpression) node); } else if (node instanceof IASTIdExpression) { return leave((IASTIdExpression) node); } else if (node instanceof IASTCastExpression) { return leave((IASTCastExpression) node); } else if (node instanceof IASTUnaryExpression) { return leave((IASTUnaryExpression) node); } else if (node instanceof IASTFieldReference) { return leave((IASTFieldReference) node); } else if (node instanceof IASTTypeIdInitializerExpression) { return leave((IASTTypeIdInitializerExpression) node); } else if (node instanceof ICPPASTNewExpression) { return leave((ICPPASTNewExpression) node); } else if (node instanceof ICPPASTDeleteExpression) { return leave((ICPPASTDeleteExpression) node); } else if (node instanceof ICPPASTSimpleTypeConstructorExpression) { return leave((ICPPASTSimpleTypeConstructorExpression) node); } else if (node instanceof IASTProblemExpression) { return leave(node); } return PROCESS_SKIP; } public int leave(IASTArraySubscriptExpression node) { processSequential(node, node.getArrayExpression(), node.getArgument()); return PROCESS_SKIP; } public int leave(IASTConditionalExpression node) { ConditionalFlowInfo info= createConditional(); setFlowInfo(node, info); info.mergeCondition(getFlowInfo(node.getLogicalConditionExpression()), fFlowContext); info.merge(getFlowInfo(node.getPositiveResultExpression()), getFlowInfo(node.getNegativeResultExpression()), fFlowContext); return PROCESS_SKIP; } public int leave(IASTFunctionCallExpression node) { processFunctionCall(node, node.getFunctionNameExpression(), node.getArguments()); return PROCESS_SKIP; } @Override public int leave(IASTDeclaration node) { if (skipNode(node)) return PROCESS_SKIP; if (node instanceof IASTFunctionDefinition) { return leave((IASTFunctionDefinition) node); } else if (node instanceof IASTSimpleDeclaration) { return leave((IASTSimpleDeclaration) node); } return PROCESS_SKIP; } public int leave(IASTFunctionDefinition node) { GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); process(info, node.getDeclarator()); process(info, node.getBody()); if (node instanceof ICPPASTFunctionWithTryBlock) { process(info, ((ICPPASTFunctionWithTryBlock) node).getCatchHandlers()); } return PROCESS_SKIP; } public int leave(IASTSimpleDeclaration node) { GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); process(info, node.getDeclarators()); return PROCESS_SKIP; } @Override public int leave(IASTParameterDeclaration node) { if (skipNode(node)) return PROCESS_SKIP; GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); process(info, node.getDeclarator()); return PROCESS_SKIP; } @Override public int leave(IASTDeclarator node) { if (skipNode(node)) return PROCESS_SKIP; IASTNode nestedOrName = node.getNestedDeclarator(); if (nestedOrName == null) nestedOrName = node.getName(); GenericSequentialFlowInfo info= processSequential(node, nestedOrName); if (node instanceof IASTArrayDeclarator) process(info, ((IASTArrayDeclarator) node).getArrayModifiers()); IASTNode[] parameters = null; if (node instanceof IASTStandardFunctionDeclarator) { parameters = ((IASTStandardFunctionDeclarator) node).getParameters(); } else if (node instanceof ICASTKnRFunctionDeclarator) { parameters = ((ICASTKnRFunctionDeclarator) node).getParameterDeclarations(); } if (parameters != null) process(info, parameters); process(info, node.getInitializer()); return PROCESS_SKIP; } public int leave(IASTExpressionList node) { IASTExpression[] expressions = node.getExpressions(); processSequential(node, expressions); return PROCESS_SKIP; } public int leave(IASTTypeIdExpression node) { assignFlowInfo(node, node.getTypeId()); return PROCESS_SKIP; } public int leave(IASTBinaryExpression node) { int operator = node.getOperator(); switch (operator) { case IASTBinaryExpression.op_assign: case IASTBinaryExpression.op_binaryAndAssign: case IASTBinaryExpression.op_binaryOrAssign: case IASTBinaryExpression.op_binaryXorAssign: case IASTBinaryExpression.op_divideAssign: case IASTBinaryExpression.op_minusAssign: case IASTBinaryExpression.op_moduloAssign: case IASTBinaryExpression.op_multiplyAssign: case IASTBinaryExpression.op_plusAssign: case IASTBinaryExpression.op_shiftLeftAssign: case IASTBinaryExpression.op_shiftRightAssign: FlowInfo lhs= getFlowInfo(node.getOperand1()); FlowInfo rhs= getFlowInfo(node.getOperand2()); if (lhs instanceof LocalFlowInfo) { LocalFlowInfo llhs= (LocalFlowInfo) lhs; llhs.setWriteAccess(fFlowContext); if (operator != IASTBinaryExpression.op_assign) { GenericSequentialFlowInfo tmp= createSequential(); tmp.merge(new LocalFlowInfo(llhs, FlowInfo.READ, fFlowContext), fFlowContext); tmp.merge(rhs, fFlowContext); rhs= tmp; } } GenericSequentialFlowInfo info= createSequential(node); // First process right and side and then left hand side. info.merge(rhs, fFlowContext); info.merge(lhs, fFlowContext); break; default: IASTExpression[] operands = CPPVisitor.getOperandsOfMultiExpression(node); processSequential(node, operands); break; } return PROCESS_SKIP; } public int leave(IASTLiteralExpression node) { // Leaf node. return PROCESS_SKIP; } public int leave(IASTIdExpression node) { assignFlowInfo(node, node.getName()); return PROCESS_SKIP; } public int leave(IASTCastExpression node) { if (skipNode(node)) return PROCESS_SKIP; processSequential(node, node.getTypeId(), node.getOperand()); return PROCESS_SKIP; } public int leave(IASTUnaryExpression node) { if (skipNode(node)) return PROCESS_SKIP; int operator = node.getOperator(); switch (operator) { case IASTUnaryExpression.op_prefixIncr: case IASTUnaryExpression.op_prefixDecr: case IASTUnaryExpression.op_postFixIncr: case IASTUnaryExpression.op_postFixDecr: { FlowInfo info= getFlowInfo(node.getOperand()); if (info instanceof LocalFlowInfo) { // Normally this should be done in the parent node since the write access takes // place later. But there seems to be no case where this influences the flow // analysis. So it is kept here to simplify the code. GenericSequentialFlowInfo result= createSequential(node); result.merge(info, fFlowContext); result.merge(new LocalFlowInfo((LocalFlowInfo)info, FlowInfo.WRITE, fFlowContext), fFlowContext); } else { setFlowInfo(node, info); } break; } case IASTUnaryExpression.op_throw: { ThrowFlowInfo info= createThrow(); setFlowInfo(node, info); IASTExpression expression= node.getOperand(); info.merge(getFlowInfo(expression), fFlowContext); break; } case IASTUnaryExpression.op_alignOf: case IASTUnaryExpression.op_sizeof: case IASTUnaryExpression.op_sizeofParameterPack: case IASTUnaryExpression.op_typeid: break; default: assignFlowInfo(node, node.getOperand()); break; } return PROCESS_SKIP; } @Override public int leave(IASTName node) { if (skipNode(node) || node.isDeclaration() || node instanceof ICPPASTQualifiedName) return PROCESS_SKIP; IBinding binding= node.resolveBinding(); if (binding instanceof IVariable) { IVariable variable= (IVariable) binding; if (!(variable instanceof IField)) { int index = fFlowContext.getIndexFromLocal(variable); if (index >= 0) { int accessMode = CPPVariableReadWriteFlags.getReadWriteFlags(node); if (accessMode != 0) { int flowInfoMode = FlowInfo.UNUSED; switch (accessMode) { case PDOMName.READ_ACCESS: flowInfoMode = FlowInfo.READ; break; case PDOMName.WRITE_ACCESS: flowInfoMode = FlowInfo.WRITE; break; case PDOMName.READ_ACCESS | PDOMName.WRITE_ACCESS: flowInfoMode = FlowInfo.UNKNOWN; break; } setFlowInfo(node, new LocalFlowInfo(variable, index, flowInfoMode, fFlowContext)); } } } } return PROCESS_SKIP; } public int leave(IASTFieldReference node) { processSequential(node, node.getFieldOwner(), node.getFieldName()); return PROCESS_SKIP; } public int leave(IASTTypeIdInitializerExpression node) { processSequential(node, node.getInitializer()); return PROCESS_SKIP; } public int leave(ICPPASTNewExpression node) { GenericSequentialFlowInfo info= processSequential(node, node.getTypeId()); process(info, node.getPlacementArguments()); process(info, node.getInitializer()); return PROCESS_SKIP; } public int leave(ICPPASTDeleteExpression node) { assignFlowInfo(node, node.getOperand()); return PROCESS_SKIP; } public int leave(ICPPASTSimpleTypeConstructorExpression node) { processSequential(node, node.getDeclSpecifier(), node.getInitializer()); return PROCESS_SKIP; } @Override public int leave(IASTInitializer node) { if (skipNode(node)) return PROCESS_SKIP; if (node instanceof IASTEqualsInitializer) { return leave((IASTEqualsInitializer) node); } else if (node instanceof IASTInitializerList) { return leave((IASTInitializerList) node); } else if (node instanceof ICASTDesignatedInitializer) { return leave((ICASTDesignatedInitializer) node); } else if (node instanceof IASTInitializerList) { return leave((ICPPASTConstructorChainInitializer) node); } else if (node instanceof ICPPASTConstructorInitializer) { return leave((ICPPASTConstructorInitializer) node); } return PROCESS_SKIP; } public int leave(IASTEqualsInitializer node) { assignFlowInfo(node, node.getInitializerClause()); return PROCESS_SKIP; } public int leave(IASTInitializerList node) { processSequential(node, node.getClauses()); return PROCESS_SKIP; } public int leave(ICASTDesignatedInitializer node) { processSequential(node, node.getDesignators()); return PROCESS_SKIP; } public int leave(ICPPASTConstructorChainInitializer node) { processSequential(node, node.getMemberInitializerId(), node.getInitializer()); return PROCESS_SKIP; } public int leave(ICPPASTConstructorInitializer node) { processSequential(node, node.getArguments()); return PROCESS_SKIP; } @Override public int leave(IASTTranslationUnit node) { if (skipNode(node)) return PROCESS_SKIP; processSequential(node, node.getDeclarations()); return PROCESS_SKIP; } private void processFunctionCall(IASTFunctionCallExpression node, IASTExpression functionNameExpression, IASTInitializerClause[] arguments) { if (skipNode(node)) return; FunctionCallFlowInfo info= createFunctionCallFlowInfo(); setFlowInfo(node, info); info.mergeReceiver(getFlowInfo(functionNameExpression), fFlowContext); for (IASTInitializerClause arg : arguments) { info.mergeArgument(getFlowInfo(arg), fFlowContext); } } }