| /******************************************************************************* |
| * Copyright (c) 2000, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Microsoft Corporation - copied to jdt.core.manipulation |
| *******************************************************************************/ |
| 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.LambdaExpression; |
| 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.NameQualifiedType; |
| 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.SwitchExpression; |
| 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<>(4); |
| private List<FlowInfo> fInfos= new ArrayList<>(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 HashMap<ASTNode, FlowInfo> fData = new HashMap<>(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); |
| for (Iterator<Expression> iterator= node.resources().iterator(); iterator.hasNext();) { |
| iterator.next().accept(this); |
| } |
| 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) { |
| return createSwitchData(node.statements()); |
| } |
| |
| protected SwitchData createSwitchData(SwitchExpression node) { |
| return createSwitchData(node.statements()); |
| } |
| |
| protected SwitchData createSwitchData(List<Statement> statements) { |
| SwitchData result= new SwitchData(); |
| if (statements == null || 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; |
| } |
| result.add(new Region(start, end - start + 1), info); |
| return result; |
| } |
| |
| protected void endVisit(SwitchStatement node, SwitchData data) { |
| preEndVisit(node, node.getExpression(), data); |
| } |
| |
| protected void endVisit(SwitchExpression node, SwitchData data) { |
| preEndVisit(node, node.getExpression(), data); |
| } |
| |
| private void preEndVisit(ASTNode node, Expression expression, SwitchData data) { |
| SwitchFlowInfo switchFlowInfo= createSwitch(); |
| setFlowInfo(node, switchFlowInfo); |
| switchFlowInfo.mergeTest(getFlowInfo(expression), 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); |
| } |
| |
| //---- 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. |
| info.merge(rhs, fFlowContext); |
| info.merge(lhs, fFlowContext); |
| } |
| |
| @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(LambdaExpression node) { |
| if (skipNode(node)) |
| return; |
| GenericSequentialFlowInfo info= createSequential(node); |
| process(info, node.parameters()); |
| process(info, node.getBody()); |
| info.setNoReturn(); |
| } |
| |
| @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. |
| info.merge(value, fFlowContext); |
| info.merge(name, fFlowContext); |
| |
| } |
| |
| @Override |
| public void endVisit(MethodDeclaration node) { |
| if (skipNode(node)) |
| return; |
| GenericSequentialFlowInfo info= processSequential(node, node.getReturnType2()); |
| process(info, node.parameters()); |
| process(info, node.thrownExceptionTypes()); |
| process(info, node.getBody()); |
| } |
| |
| @Override |
| public void endVisit(MethodInvocation node) { |
| endVisitMethodInvocation(node, node.getExpression(), node.arguments(), getMethodBinding(node.getName())); |
| } |
| |
| @Override |
| public void endVisit(NameQualifiedType node) { |
| if (skipNode(node)) |
| return; |
| processSequential(node, node.getQualifier(), 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(SwitchExpression 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); |
| for (Iterator<Expression> iterator= node.resources().iterator(); iterator.hasNext();) { |
| info.mergeResources(getFlowInfo(iterator.next()), fFlowContext); |
| } |
| 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); |
| result.merge( |
| new LocalFlowInfo((LocalFlowInfo)info, FlowInfo.WRITE, fFlowContext), |
| fFlowContext); |
| } 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; |
| } |
| } |