blob: d0c90173bfd4d722f86a108526497df8f483e0c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.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.jface.text.IRegion;
import org.eclipse.jface.text.Region;
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 fRanges= new ArrayList(4);
private List 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 (IRegion[]) fRanges.toArray(new IRegion[fRanges.size()]);
}
public FlowInfo[] getInfos() {
return (FlowInfo[]) fInfos.toArray(new FlowInfo[fInfos.size()]);
}
public FlowInfo getInfo(int index) {
return (FlowInfo)fInfos.get(index);
}
}
private HashMap 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);
}
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 (FlowInfo)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 (FlowInfo)fData.get(node);
}
//---- Helpers to process sequential flow infos -------------------------------------
protected GenericSequentialFlowInfo processSequential(ASTNode parent, List 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 nodes) {
GenericSequentialFlowInfo result= createSequential();
process(result, nodes);
return result;
}
//---- Generic merge methods --------------------------------------------------------
protected void process(GenericSequentialFlowInfo info, List nodes) {
if (nodes == null)
return;
for (Iterator iter= nodes.iterator(); iter.hasNext();) {
info.merge(getFlowInfo((ASTNode)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 -------------------------------------------------------
public boolean visit(EmptyStatement node) {
// Empty statements aren't of any interest.
return false;
}
public boolean visit(TryStatement node) {
if (traverseNode(node)) {
fFlowContext.pushExcptions(node);
node.getBody().accept(this);
fFlowContext.popExceptions();
List catchClauses= node.catchClauses();
for (Iterator iter= catchClauses.iterator(); iter.hasNext();) {
((CatchClause)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 statements= node.statements();
if (statements.isEmpty())
return result;
int start= -1, end= -1;
GenericSequentialFlowInfo info= null;
for (Iterator iter= statements.iterator(); iter.hasNext(); ) {
Statement 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) {
SwitchFlowInfo switchFlowInfo= createSwitch();
setFlowInfo(node, switchFlowInfo);
switchFlowInfo.mergeTest(getFlowInfo(node.getExpression()), 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 ---------------------------------------------------
public void endVisit(AnnotationTypeDeclaration node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.bodyDeclarations());
info.setNoReturn();
}
public void endVisit(AnnotationTypeMemberDeclaration node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType(), node.getDefault());
info.setNoReturn();
}
public void endVisit(AnonymousClassDeclaration node) {
if (skipNode(node))
return;
FlowInfo info= processSequential(node, node.bodyDeclarations());
info.setNoReturn();
}
public void endVisit(ArrayAccess node) {
if (skipNode(node))
return;
processSequential(node, node.getArray(), node.getIndex());
}
public void endVisit(ArrayCreation node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType());
process(info, node.dimensions());
process(info, node.getInitializer());
}
public void endVisit(ArrayInitializer node) {
if (skipNode(node))
return;
processSequential(node, node.expressions());
}
public void endVisit(ArrayType node) {
if (skipNode(node))
return;
processSequential(node, node.getElementType());
}
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);
}
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);
}
public void endVisit(Block node) {
if (skipNode(node))
return;
BlockFlowInfo info= createBlock();
setFlowInfo(node, info);
process(info, node.statements());
}
public void endVisit(BooleanLiteral node) {
// Leaf node.
}
public void endVisit(BreakStatement node) {
if (skipNode(node))
return;
setFlowInfo(node, createBranch(node.getLabel()));
}
public void endVisit(CastExpression node) {
if (skipNode(node))
return;
processSequential(node, node.getType(), node.getExpression());
}
public void endVisit(CatchClause node) {
if (skipNode(node))
return;
processSequential(node, node.getException(), node.getBody());
}
public void endVisit(CharacterLiteral node) {
// Leaf node.
}
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());
}
public void endVisit(CompilationUnit node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.imports());
process(info, node.types());
}
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);
}
public void endVisit(ConstructorInvocation node) {
if (skipNode(node))
return;
processSequential(node, node.arguments());
}
public void endVisit(ContinueStatement node) {
if (skipNode(node))
return;
setFlowInfo(node, createBranch(node.getLabel()));
}
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);
}
public void endVisit(EmptyStatement node) {
// Leaf node.
}
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);
}
public void endVisit(EnumConstantDeclaration node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.arguments());
process(info, node.getAnonymousClassDeclaration());
}
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();
}
public void endVisit(ExpressionStatement node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getExpression());
}
public void endVisit(FieldAccess node) {
if (skipNode(node))
return;
processSequential(node, node.getExpression(), node.getName());
}
public void endVisit(FieldDeclaration node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType());
process(info, node.fragments());
}
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);
}
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);
}
public void endVisit(ImportDeclaration node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getName());
}
public void endVisit(InfixExpression node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getLeftOperand(), node.getRightOperand());
process(info, node.extendedOperands());
}
public void endVisit(InstanceofExpression node) {
if (skipNode(node))
return;
processSequential(node, node.getLeftOperand(), node.getRightOperand());
}
public void endVisit(Initializer node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getBody());
}
public void endVisit(Javadoc node) {
// no influence on flow analysis
}
public void endVisit(LabeledStatement node) {
if (skipNode(node))
return;
FlowInfo info= assignFlowInfo(node, node.getBody());
if (info != null)
info.removeLabel(node.getLabel());
}
public void endVisit(MarkerAnnotation node) {
// nothing to do for marker annotations;
}
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);
}
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());
}
public void endVisit(MethodInvocation node) {
endVisitMethodInvocation(node, node.getExpression(), node.arguments(), getMethodBinding(node.getName()));
}
public void endVisit(NormalAnnotation node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getTypeName());
process(info, node.values());
}
public void endVisit(NullLiteral node) {
// Leaf node.
}
public void endVisit(NumberLiteral node) {
// Leaf node.
}
public void endVisit(PackageDeclaration node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getName());
}
public void endVisit(ParameterizedType node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType());
process(info, node.typeArguments());
}
public void endVisit(ParenthesizedExpression node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getExpression());
}
public void endVisit(PostfixExpression node) {
endVisitIncDecOperation(node, node.getOperand());
}
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());
}
}
public void endVisit(PrimitiveType node) {
// Leaf node
}
public void endVisit(QualifiedName node) {
if (skipNode(node))
return;
processSequential(node, node.getQualifier(), node.getName());
}
public void endVisit(QualifiedType node) {
if (skipNode(node))
return;
processSequential(node, node.getQualifier(), node.getName());
}
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());
}
}
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));
}
}
}
public void endVisit(SimpleType node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getName());
}
public void endVisit(SingleMemberAnnotation node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getValue());
}
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);
}
public void endVisit(StringLiteral node) {
// Leaf node
}
public void endVisit(SuperConstructorInvocation node) {
endVisitMethodInvocation(node, node.getExpression(), node.arguments(), node.resolveConstructorBinding());
}
public void endVisit(SuperFieldAccess node) {
if (skipNode(node))
return;
processSequential(node, node.getQualifier(), node.getName());
}
public void endVisit(SuperMethodInvocation node) {
endVisitMethodInvocation(node, node.getQualifier(), node.arguments(), getMethodBinding(node.getName()));
}
public void endVisit(SwitchCase node) {
endVisitNode(node);
}
public void endVisit(SwitchStatement node) {
if (skipNode(node))
return;
endVisit(node, createSwitchData(node));
}
public void endVisit(SynchronizedStatement node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
process(info, node.getBody());
}
public void endVisit(ThisExpression node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getQualifier());
}
public void endVisit(ThrowStatement node) {
if (skipNode(node))
return;
ThrowFlowInfo info= createThrow();
setFlowInfo(node, info);
Expression expression= node.getExpression();
info.merge(getFlowInfo(expression), fFlowContext);
info.mergeException(expression.resolveTypeBinding(), fFlowContext);
}
public void endVisit(TryStatement node) {
if (skipNode(node))
return;
TryFlowInfo info= createTry();
setFlowInfo(node, info);
info.mergeTry(getFlowInfo(node.getBody()), fFlowContext);
info.removeExceptions(node);
for (Iterator iter= node.catchClauses().iterator(); iter.hasNext();) {
CatchClause element= (CatchClause)iter.next();
info.mergeCatch(getFlowInfo(element), fFlowContext);
}
info.mergeFinally(getFlowInfo(node.getFinally()), fFlowContext);
}
// TODO account for enums and annotations
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();
}
public void endVisit(TypeDeclarationStatement node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getDeclaration());
}
public void endVisit(TypeLiteral node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getType());
}
public void endVisit(TypeParameter node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getName());
process(info, node.typeBounds());
}
public void endVisit(VariableDeclarationExpression node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType());
process(info, node.fragments());
}
public void endVisit(VariableDeclarationStatement node) {
if (skipNode(node))
return;
GenericSequentialFlowInfo info= processSequential(node, node.getType());
process(info, node.fragments());
}
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);
}
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);
}
public void endVisit(WildcardType node) {
if (skipNode(node))
return;
assignFlowInfo(node, node.getBound());
}
private void endVisitMethodInvocation(ASTNode node, ASTNode receiver, List arguments, IMethodBinding binding) {
if (skipNode(node))
return;
MessageSendFlowInfo info= createMessageSendFlowInfo();
setFlowInfo(node, info);
for (Iterator iter= arguments.iterator(); iter.hasNext();) {
Expression arg= (Expression) iter.next();
info.mergeArgument(getFlowInfo(arg), fFlowContext);
}
info.mergeReceiver(getFlowInfo(receiver), fFlowContext);
info.mergeExceptions(binding, 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;
}
}