--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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
+ *******************************************************************************/
+package org.eclipse.jdt.internal.corext.refactoring.code.flow;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayAccess;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.AssertStatement;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.BreakStatement;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.ContinueStatement;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EmptyStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.InstanceofExpression;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.MemberValuePair;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.NullLiteral;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Statement;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SwitchCase;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.SynchronizedStatement;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.ThrowStatement;
+import org.eclipse.jdt.core.dom.TryStatement;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.core.dom.WildcardType;
+
+import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
+
+/**
+ * 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 analyses the
+ * the first access to a variable (read or write) and if all execution paths
+ * return a value.
+ */
+abstract class FlowAnalyzer extends GenericVisitor {
+
+ static protected class SwitchData {
+ private boolean fHasDefaultCase;
+ private List<IRegion> fRanges= new ArrayList<IRegion>(4);
+ private 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);
+ }
+ public SwitchData generated_5568532044592214662(int start, int end, GenericSequentialFlowInfo info) {
+ add(new Region(start, end - start + 1), info);
+ return this;
+ }
+ }
+
+ private HashMap<ASTNode, FlowInfo> fData = new HashMap<ASTNode, FlowInfo>(100);
+ /* package */ FlowContext fFlowContext= null;
+
+ public FlowAnalyzer(FlowContext context) {
+ fFlowContext= context;
+ }
+
+ protected abstract boolean createReturnFlowInfo(ReturnStatement node);
+
+ protected abstract boolean traverseNode(ASTNode node);
+
+ protected boolean skipNode(ASTNode node) {
+ return !traverseNode(node);
+ }
+
+ @Override
+ protected final boolean visitNode(ASTNode node) {
+ return traverseNode(node);
+ }
+
+ //---- Hooks to create Flow info objects. User may introduce their own infos.
+
+ protected ReturnFlowInfo createReturn(ReturnStatement statement) {
+ return new ReturnFlowInfo(statement);
+ }
+
+ protected ThrowFlowInfo createThrow() {
+ return new ThrowFlowInfo();
+ }
+
+ protected BranchFlowInfo createBranch(SimpleName label) {
+ return new BranchFlowInfo(label, fFlowContext);
+ }
+
+ protected GenericSequentialFlowInfo createSequential() {
+ return new GenericSequentialFlowInfo();
+ }
+
+ protected ConditionalFlowInfo createConditional() {
+ return new ConditionalFlowInfo();
+ }
+
+ protected EnhancedForFlowInfo createEnhancedFor() {
+ return new EnhancedForFlowInfo();
+ }
+
+ 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 MessageSendFlowInfo createMessageSendFlowInfo() {
+ return new MessageSendFlowInfo();
+ }
+
+ protected FlowContext getFlowContext() {
+ return fFlowContext;
+ }
+
+ //---- Helpers to access flow analysis objects ----------------------------------------
+
+ protected FlowInfo getFlowInfo(ASTNode node) {
+ return fData.remove(node);
+ }
+
+ protected void setFlowInfo(ASTNode node, FlowInfo info) {
+ fData.put(node, info);
+ }
+
+ protected FlowInfo assignFlowInfo(ASTNode target, ASTNode source) {
+ FlowInfo result= getFlowInfo(source);
+ setFlowInfo(target, result);
+ return result;
+ }
+
+ protected FlowInfo accessFlowInfo(ASTNode node) {
+ return fData.get(node);
+ }
+
+ //---- Helpers to process sequential flow infos -------------------------------------
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, List<? extends ASTNode> nodes) {
+ GenericSequentialFlowInfo result= createSequential(parent);
+ process(result, nodes);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1) {
+ GenericSequentialFlowInfo result= createSequential(parent);
+ if (node1 != null)
+ result.merge(getFlowInfo(node1), fFlowContext);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1, ASTNode 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(ASTNode parent) {
+ GenericSequentialFlowInfo result= createSequential();
+ setFlowInfo(parent, result);
+ return result;
+ }
+
+ protected GenericSequentialFlowInfo createSequential(List<? extends ASTNode> nodes) {
+ GenericSequentialFlowInfo result= createSequential();
+ process(result, nodes);
+ return result;
+ }
+
+ //---- Generic merge methods --------------------------------------------------------
+
+ protected void process(GenericSequentialFlowInfo info, List<? extends ASTNode> nodes) {
+ if (nodes == null)
+ return;
+ for (Iterator<? extends ASTNode> iter= nodes.iterator(); iter.hasNext();) {
+ info.merge(getFlowInfo(iter.next()), fFlowContext);
+ }
+ }
+
+ protected void process(GenericSequentialFlowInfo info, ASTNode node) {
+ if (node != null)
+ info.merge(getFlowInfo(node), fFlowContext);
+ }
+
+ protected void process(GenericSequentialFlowInfo info, ASTNode node1, ASTNode node2) {
+ if (node1 != null)
+ info.merge(getFlowInfo(node1), fFlowContext);
+ if (node2 != null)
+ info.merge(getFlowInfo(node2), fFlowContext);
+ }
+
+ //---- special visit methods -------------------------------------------------------
+
+ @Override
+ public boolean visit(EmptyStatement node) {
+ // Empty statements aren't of any interest.
+ return false;
+ }
+
+ @Override
+ public boolean visit(TryStatement node) {
+ if (traverseNode(node)) {
+ fFlowContext.pushExcptions(node);
+ node.getBody().accept(this);
+ fFlowContext.popExceptions();
+ List<CatchClause> catchClauses= node.catchClauses();
+ for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
+ iter.next().accept(this);
+ }
+ Block finallyBlock= node.getFinally();
+ if (finallyBlock != null) {
+ finallyBlock.accept(this);
+ }
+ }
+ return false;
+ }
+
+ //---- Helper to process switch statement ----------------------------------------
+
+ protected SwitchData createSwitchData(SwitchStatement node) {
+ SwitchData result= new SwitchData();
+ List<Statement> statements= node.statements();
+ if (statements.isEmpty())
+ return result;
+
+ int start= -1, end= -1;
+ GenericSequentialFlowInfo info= null;
+
+ for (Iterator<Statement> iter= statements.iterator(); iter.hasNext(); ) {
+ Statement statement= iter.next();
+ if (statement instanceof SwitchCase) {
+ SwitchCase switchCase= (SwitchCase)statement;
+ if (switchCase.isDefault()) {
+ result.setHasDefaultCase();
+ }
+ if (info == null) {
+ info= createSequential();
+ start= statement.getStartPosition();
+ } else {
+ if (info.isReturn() || info.isPartialReturn() || info.branches()) {
+ result.add(new Region(start, end - start + 1), info);
+ info= createSequential();
+ start= statement.getStartPosition();
+ }
+ }
+ } else {
+ info.merge(getFlowInfo(statement), fFlowContext);
+ }
+ end= statement.getStartPosition() + statement.getLength() - 1;
+ }
+ return result.generated_5568532044592214662(start, end, info);
+ }
+
+ protected void endVisit(SwitchStatement node, SwitchData data) {
+ SwitchFlowInfo switchFlowInfo= createSwitch();
+ setFlowInfo(node, switchFlowInfo);
+ switchFlowInfo.mergeTest(getFlowInfo(node.getExpression()), fFlowContext);
+ FlowInfo[] cases= data.getInfos();
+ switchFlowInfo.generated_4041324263229548821(data, this, cases);
+ switchFlowInfo.removeLabel(null);
+ }
+
+
+
+ //---- concret endVisit methods ---------------------------------------------------
+
+ @Override
+ public void endVisit(AnnotationTypeDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(AnnotationTypeMemberDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType(), node.getDefault());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(AnonymousClassDeclaration node) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= processSequential(node, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(ArrayAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getArray(), node.getIndex());
+ }
+
+ @Override
+ public void endVisit(ArrayCreation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.dimensions());
+ process(info, node.getInitializer());
+ }
+
+ @Override
+ public void endVisit(ArrayInitializer node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.expressions());
+ }
+
+ @Override
+ public void endVisit(ArrayType node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getElementType());
+ }
+
+ @Override
+ public void endVisit(AssertStatement node) {
+ if (skipNode(node))
+ return;
+ IfFlowInfo info= new IfFlowInfo();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(getFlowInfo(node.getMessage()), null, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(Assignment node) {
+ if (skipNode(node))
+ return;
+ FlowInfo lhs= getFlowInfo(node.getLeftHandSide());
+ FlowInfo rhs= getFlowInfo(node.getRightHandSide());
+ if (lhs instanceof LocalFlowInfo) {
+ LocalFlowInfo llhs= (LocalFlowInfo)lhs;
+ llhs.setWriteAccess(fFlowContext);
+ if (node.getOperator() != Assignment.Operator.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.
+ fFlowContext.generated_3208184441203194166(lhs, rhs, info);
+ }
+
+ @Override
+ public void endVisit(Block node) {
+ if (skipNode(node))
+ return;
+ BlockFlowInfo info= createBlock();
+ setFlowInfo(node, info);
+ process(info, node.statements());
+ }
+
+ @Override
+ public void endVisit(BooleanLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(BreakStatement node) {
+ if (skipNode(node))
+ return;
+ setFlowInfo(node, createBranch(node.getLabel()));
+ }
+
+ @Override
+ public void endVisit(CastExpression node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getType(), node.getExpression());
+ }
+
+ @Override
+ public void endVisit(CatchClause node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getException(), node.getBody());
+ }
+
+ @Override
+ public void endVisit(CharacterLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(ClassInstanceCreation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
+ process(info, node.getType());
+ process(info, node.arguments());
+ process(info, node.getAnonymousClassDeclaration());
+ }
+
+ @Override
+ public void endVisit(CompilationUnit node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.imports());
+ process(info, node.types());
+ }
+
+ @Override
+ public void endVisit(ConditionalExpression node) {
+ if (skipNode(node))
+ return;
+ ConditionalFlowInfo info= createConditional();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(
+ getFlowInfo(node.getThenExpression()),
+ getFlowInfo(node.getElseExpression()),
+ fFlowContext);
+ }
+
+ @Override
+ public void endVisit(ConstructorInvocation node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.arguments());
+ }
+
+ @Override
+ public void endVisit(ContinueStatement node) {
+ if (skipNode(node))
+ return;
+ setFlowInfo(node, createBranch(node.getLabel()));
+ }
+
+ @Override
+ public void endVisit(DoStatement node) {
+ if (skipNode(node))
+ return;
+ DoWhileFlowInfo info= createDoWhile();
+ setFlowInfo(node, info);
+ info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(EmptyStatement node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ if (skipNode(node))
+ return;
+ EnhancedForFlowInfo forInfo= createEnhancedFor();
+ setFlowInfo(node, forInfo);
+ forInfo.mergeParameter(getFlowInfo(node.getParameter()), fFlowContext);
+ forInfo.mergeExpression(getFlowInfo(node.getExpression()), fFlowContext);
+ forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ forInfo.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(EnumConstantDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.arguments());
+ process(info, node.getAnonymousClassDeclaration());
+ }
+
+ @Override
+ public void endVisit(EnumDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.superInterfaceTypes());
+ process(info, node.enumConstants());
+ process(info, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(ExpressionStatement node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getExpression());
+ }
+
+ @Override
+ public void endVisit(FieldAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getExpression(), node.getName());
+ }
+
+ @Override
+ public void endVisit(FieldDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(ForStatement node) {
+ if (skipNode(node))
+ return;
+ ForFlowInfo forInfo= createFor();
+ setFlowInfo(node, forInfo);
+ forInfo.mergeInitializer(createSequential(node.initializers()), fFlowContext);
+ forInfo.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ // Increments are executed after the action.
+ forInfo.mergeIncrement(createSequential(node.updaters()), fFlowContext);
+ forInfo.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(IfStatement node) {
+ if (skipNode(node))
+ return;
+ IfFlowInfo info= createIf();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.merge(getFlowInfo(node.getThenStatement()), getFlowInfo(node.getElseStatement()), fFlowContext);
+ }
+
+ @Override
+ public void endVisit(ImportDeclaration node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(InfixExpression node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getLeftOperand(), node.getRightOperand());
+ process(info, node.extendedOperands());
+ }
+
+ @Override
+ public void endVisit(InstanceofExpression node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getLeftOperand(), node.getRightOperand());
+ }
+
+ @Override
+ public void endVisit(Initializer node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getBody());
+ }
+
+ @Override
+ public void endVisit(Javadoc node) {
+ // no influence on flow analysis
+ }
+
+ @Override
+ public void endVisit(LabeledStatement node) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= assignFlowInfo(node, node.getBody());
+ if (info != null)
+ info.removeLabel(node.getLabel());
+ }
+
+ @Override
+ public void endVisit(MarkerAnnotation node) {
+ // nothing to do for marker annotations;
+ }
+
+ @Override
+ public void endVisit(MemberValuePair node) {
+ if (skipNode(node))
+ return;
+
+ FlowInfo name= getFlowInfo(node.getName());
+ FlowInfo value= getFlowInfo(node.getValue());
+ if (name instanceof LocalFlowInfo) {
+ LocalFlowInfo llhs= (LocalFlowInfo)name;
+ llhs.setWriteAccess(fFlowContext);
+ }
+ GenericSequentialFlowInfo info= createSequential(node);
+ // first process value and then name.
+ fFlowContext.generated_9137196364451806567(name, value, info);
+
+ }
+
+ @Override
+ public void endVisit(MethodDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getReturnType2());
+ process(info, node.parameters());
+ process(info, node.thrownExceptions());
+ process(info, node.getBody());
+ }
+
+ @Override
+ public void endVisit(MethodInvocation node) {
+ endVisitMethodInvocation(node, node.getExpression(), node.arguments(), getMethodBinding(node.getName()));
+ }
+
+ @Override
+ public void endVisit(NormalAnnotation node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getTypeName());
+ process(info, node.values());
+ }
+
+ @Override
+ public void endVisit(NullLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(NumberLiteral node) {
+ // Leaf node.
+ }
+
+ @Override
+ public void endVisit(PackageDeclaration node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(ParameterizedType node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.typeArguments());
+ }
+
+ @Override
+ public void endVisit(ParenthesizedExpression node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getExpression());
+ }
+
+ @Override
+ public void endVisit(PostfixExpression node) {
+ endVisitIncDecOperation(node, node.getOperand());
+ }
+
+ @Override
+ public void endVisit(PrefixExpression node) {
+ PrefixExpression.Operator op= node.getOperator();
+ if (PrefixExpression.Operator.INCREMENT.equals(op) || PrefixExpression.Operator.DECREMENT.equals(op)) {
+ endVisitIncDecOperation(node, node.getOperand());
+ } else {
+ assignFlowInfo(node, node.getOperand());
+ }
+ }
+
+ @Override
+ public void endVisit(PrimitiveType node) {
+ // Leaf node
+ }
+
+ @Override
+ public void endVisit(QualifiedName node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(QualifiedType node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(ReturnStatement node) {
+ if (skipNode(node))
+ return;
+
+ if (createReturnFlowInfo(node)) {
+ ReturnFlowInfo info= createReturn(node);
+ setFlowInfo(node, info);
+ info.merge(getFlowInfo(node.getExpression()), fFlowContext);
+ } else {
+ assignFlowInfo(node, node.getExpression());
+ }
+ }
+
+ @Override
+ public void endVisit(SimpleName node) {
+ if (skipNode(node) || node.isDeclaration())
+ return;
+ IBinding binding= node.resolveBinding();
+ if (binding instanceof IVariableBinding) {
+ IVariableBinding variable= (IVariableBinding)binding;
+ if (!variable.isField()) {
+ setFlowInfo(node, new LocalFlowInfo(
+ variable,
+ FlowInfo.READ,
+ fFlowContext));
+ }
+ } else if (binding instanceof ITypeBinding) {
+ ITypeBinding type= (ITypeBinding)binding;
+ if (type.isTypeVariable()) {
+ setFlowInfo(node, new TypeVariableFlowInfo(type, fFlowContext));
+ }
+ }
+ }
+
+ @Override
+ public void endVisit(SimpleType node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getName());
+ }
+
+ @Override
+ public void endVisit(SingleMemberAnnotation node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getValue());
+ }
+
+ @Override
+ public void endVisit(SingleVariableDeclaration node) {
+ if (skipNode(node))
+ return;
+
+ IVariableBinding binding= node.resolveBinding();
+ LocalFlowInfo nameInfo= null;
+ Expression initializer= node.getInitializer();
+ if (binding != null && !binding.isField() && initializer != null) {
+ nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
+ }
+ GenericSequentialFlowInfo info= processSequential(node, node.getType(), initializer);
+ info.merge(nameInfo, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(StringLiteral node) {
+ // Leaf node
+ }
+
+ @Override
+ public void endVisit(SuperConstructorInvocation node) {
+ endVisitMethodInvocation(node, node.getExpression(), node.arguments(), node.resolveConstructorBinding());
+ }
+
+ @Override
+ public void endVisit(SuperFieldAccess node) {
+ if (skipNode(node))
+ return;
+ processSequential(node, node.getQualifier(), node.getName());
+ }
+
+ @Override
+ public void endVisit(SuperMethodInvocation node) {
+ endVisitMethodInvocation(node, node.getQualifier(), node.arguments(), getMethodBinding(node.getName()));
+ }
+
+ @Override
+ public void endVisit(SwitchCase node) {
+ endVisitNode(node);
+ }
+
+ @Override
+ public void endVisit(SwitchStatement node) {
+ if (skipNode(node))
+ return;
+ endVisit(node, createSwitchData(node));
+ }
+
+ @Override
+ public void endVisit(SynchronizedStatement node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
+ process(info, node.getBody());
+ }
+
+ @Override
+ public void endVisit(ThisExpression node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getQualifier());
+ }
+
+ @Override
+ public void endVisit(ThrowStatement node) {
+ if (skipNode(node))
+ return;
+ ThrowFlowInfo info= createThrow();
+ setFlowInfo(node, info);
+ Expression expression= node.getExpression();
+ info.merge(getFlowInfo(expression), fFlowContext);
+ }
+
+ @Override
+ public void endVisit(TryStatement node) {
+ if (skipNode(node))
+ return;
+ TryFlowInfo info= createTry();
+ setFlowInfo(node, info);
+ info.mergeTry(getFlowInfo(node.getBody()), fFlowContext);
+ for (Iterator<CatchClause> iter= node.catchClauses().iterator(); iter.hasNext();) {
+ CatchClause element= iter.next();
+ info.mergeCatch(getFlowInfo(element), fFlowContext);
+ }
+ info.mergeFinally(getFlowInfo(node.getFinally()), fFlowContext);
+ }
+
+ // TODO account for enums and annotations
+
+ @Override
+ public void endVisit(TypeDeclaration node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getSuperclassType());
+ process(info, node.superInterfaceTypes());
+ process(info, node.bodyDeclarations());
+ info.setNoReturn();
+ }
+
+ @Override
+ public void endVisit(TypeDeclarationStatement node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getDeclaration());
+ }
+
+ @Override
+ public void endVisit(TypeLiteral node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getType());
+ }
+
+ @Override
+ public void endVisit(TypeParameter node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getName());
+ process(info, node.typeBounds());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationExpression node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationStatement node) {
+ if (skipNode(node))
+ return;
+ GenericSequentialFlowInfo info= processSequential(node, node.getType());
+ process(info, node.fragments());
+ }
+
+ @Override
+ public void endVisit(VariableDeclarationFragment node) {
+ if (skipNode(node))
+ return;
+
+ IVariableBinding binding= node.resolveBinding();
+ LocalFlowInfo nameInfo= null;
+ Expression initializer= node.getInitializer();
+ if (binding != null && !binding.isField() && initializer != null) {
+ nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
+ }
+ GenericSequentialFlowInfo info= processSequential(node, initializer);
+ info.merge(nameInfo, fFlowContext);
+ }
+
+ @Override
+ public void endVisit(WhileStatement node) {
+ if (skipNode(node))
+ return;
+ WhileFlowInfo info= createWhile();
+ setFlowInfo(node, info);
+ info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
+ info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
+ info.removeLabel(null);
+ }
+
+ @Override
+ public void endVisit(WildcardType node) {
+ if (skipNode(node))
+ return;
+ assignFlowInfo(node, node.getBound());
+ }
+
+ private void endVisitMethodInvocation(ASTNode node, ASTNode receiver, List<Expression> arguments, IMethodBinding binding) {
+ if (skipNode(node))
+ return;
+ MessageSendFlowInfo info= createMessageSendFlowInfo();
+ setFlowInfo(node, info);
+ for (Iterator<Expression> iter= arguments.iterator(); iter.hasNext();) {
+ Expression arg= iter.next();
+ info.mergeArgument(getFlowInfo(arg), fFlowContext);
+ }
+ info.mergeReceiver(getFlowInfo(receiver), fFlowContext);
+ }
+
+ private void endVisitIncDecOperation(Expression node, Expression operand) {
+ if (skipNode(node))
+ return;
+ FlowInfo info= getFlowInfo(operand);
+ if (info instanceof LocalFlowInfo) {
+ // Normally we should do this in the parent node since the write access take place later.
+ // But I couldn't come up with a case where this influences the flow analysis. So I kept
+ // it here to simplify the code.
+ GenericSequentialFlowInfo result= createSequential(node);
+ result.merge(info, fFlowContext);
+ fFlowContext.generated_8531521875891119853(info, result);
+ } else {
+ setFlowInfo(node, info);
+ }
+ }
+
+ private IMethodBinding getMethodBinding(Name name) {
+ if (name == null)
+ return null;
+ IBinding binding= name.resolveBinding();
+ if (binding instanceof IMethodBinding)
+ return (IMethodBinding)binding;
+ return null;
+ }
+}