blob: 944020acffaefcf9165c84ca8ec6a95616d2f211 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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
* Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
* o inline call that is used in a field initializer
* (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.code.flow;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.internal.corext.Assert;
import org.eclipse.jdt.internal.corext.dom.Selection;
public class InputFlowAnalyzer extends FlowAnalyzer {
private static class LoopReentranceVisitor extends FlowAnalyzer {
private Selection fSelection;
private ASTNode fLoopNode;
public LoopReentranceVisitor(FlowContext context, Selection selection, ASTNode loopNode) {
super(context);
fSelection= selection;
fLoopNode= loopNode;
}
protected boolean traverseNode(ASTNode node) {
return true; // end <= fSelection.end || fSelection.enclosedBy(start, end);
}
protected boolean createReturnFlowInfo(ReturnStatement node) {
// Make sure that the whole return statement is selected or located before the selection.
return node.getStartPosition() + node.getLength() <= fSelection.getExclusiveEnd();
}
protected ASTNode getLoopNode() {
return fLoopNode;
}
public void process(ASTNode node) {
fFlowContext.setLoopReentranceMode(true);
node.accept(this);
fFlowContext.setLoopReentranceMode(false);
}
public void endVisit(DoStatement node) {
if (skipNode(node))
return;
DoWhileFlowInfo info= createDoWhile();
setFlowInfo(node, info);
info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
// No need to merge the condition. It was already considered by the InputFlowAnalyzer.
info.removeLabel(null);
}
public void endVisit(EnhancedForStatement node) {
if (skipNode(node))
return;
FlowInfo paramInfo= getFlowInfo(node.getParameter());
FlowInfo expressionInfo= getFlowInfo(node.getExpression());
FlowInfo actionInfo= getFlowInfo(node.getBody());
EnhancedForFlowInfo forInfo= createEnhancedFor();
setFlowInfo(node, forInfo);
// If the for statement is the outermost loop then we only have to consider
// the action. The parameter and expression are only evaluated once.
if (node == fLoopNode) {
forInfo.mergeAction(actionInfo, fFlowContext);
} else {
// Inner for loops are evaluated in the sequence expression, parameter,
// action.
forInfo.mergeExpression(expressionInfo, fFlowContext);
forInfo.mergeParameter(paramInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
}
forInfo.removeLabel(null);
}
public void endVisit(ForStatement node) {
if (skipNode(node))
return;
FlowInfo initInfo= createSequential(node.initializers());
FlowInfo conditionInfo= getFlowInfo(node.getExpression());
FlowInfo incrementInfo= createSequential(node.updaters());
FlowInfo actionInfo= getFlowInfo(node.getBody());
ForFlowInfo forInfo= createFor();
setFlowInfo(node, forInfo);
// the for statement is the outermost loop. In this case we only have
// to consider the increment, condition and action.
if (node == fLoopNode) {
forInfo.mergeIncrement(incrementInfo, fFlowContext);
forInfo.mergeCondition(conditionInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
} else {
// we have to merge two different cases. One if we reenter the for statement
// immediatelly (that means we have to consider increments, condition and action)
// and the other case if we reenter the for in the next loop of
// the outer loop. Then we have to consider initializations, condtion and action.
// For a conditional flow info that means:
// (initializations | increments) & condition & action.
GenericConditionalFlowInfo initIncr= new GenericConditionalFlowInfo();
initIncr.merge(initInfo, fFlowContext);
initIncr.merge(incrementInfo, fFlowContext);
forInfo.mergeAccessModeSequential(initIncr, fFlowContext);
forInfo.mergeCondition(conditionInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
}
forInfo.removeLabel(null);
}
}
private Selection fSelection;
private boolean fDoLoopReentrance;
private LoopReentranceVisitor fLoopReentranceVisitor;
public InputFlowAnalyzer(FlowContext context, Selection selection, boolean doLoopReentrance) {
super(context);
fSelection= selection;
Assert.isNotNull(fSelection);
fDoLoopReentrance= doLoopReentrance;
}
public FlowInfo perform(BodyDeclaration node) {
Assert.isTrue(!(node instanceof AbstractTypeDeclaration));
node.accept(this);
return getFlowInfo(node);
}
protected boolean traverseNode(ASTNode node) {
return node.getStartPosition() + node.getLength() > fSelection.getInclusiveEnd();
}
protected boolean createReturnFlowInfo(ReturnStatement node) {
// Make sure that the whole return statement is located after the selection. There can be cases like
// return i + [x + 10] * 10; In this case we must not create a return info node.
return node.getStartPosition() >= fSelection.getInclusiveEnd();
}
public boolean visit(DoStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
public boolean visit(EnhancedForStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
public boolean visit(ForStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
public boolean visit(WhileStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
private void createLoopReentranceVisitor(ASTNode node) {
if (fLoopReentranceVisitor == null && fDoLoopReentrance)
fLoopReentranceVisitor= new LoopReentranceVisitor(fFlowContext, fSelection, node);
}
public void endVisit(ConditionalExpression node) {
if (skipNode(node))
return;
Expression thenPart= node.getThenExpression();
Expression elsePart= node.getElseExpression();
if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
(elsePart != null && fSelection.coveredBy(elsePart))) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
endVisitConditional(info, node.getExpression(), new ASTNode[] {thenPart, elsePart});
} else {
super.endVisit(node);
}
}
public void endVisit(DoStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
public void endVisit(IfStatement node) {
if (skipNode(node))
return;
Statement thenPart= node.getThenStatement();
Statement elsePart= node.getElseStatement();
if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
(elsePart != null && fSelection.coveredBy(elsePart))) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
endVisitConditional(info, node.getExpression(), new ASTNode[] {thenPart, elsePart});
} else {
super.endVisit(node);
}
}
public void endVisit(EnhancedForStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
public void endVisit(ForStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
public void endVisit(SwitchStatement node) {
if (skipNode(node))
return;
SwitchData data= createSwitchData(node);
IRegion[] ranges= data.getRanges();
for (int i= 0; i < ranges.length; i++) {
IRegion range= ranges[i];
if (fSelection.coveredBy(range)) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
info.merge(getFlowInfo(node.getExpression()), fFlowContext);
info.merge(data.getInfo(i), fFlowContext);
info.removeLabel(null);
return;
}
}
super.endVisit(node, data);
}
public void endVisit(WhileStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
private void endVisitConditional(GenericSequentialFlowInfo info, ASTNode condition, ASTNode[] branches) {
info.merge(getFlowInfo(condition), fFlowContext);
for (int i= 0; i < branches.length; i++) {
ASTNode branch= branches[i];
if (branch != null && fSelection.coveredBy(branch)) {
info.merge(getFlowInfo(branch), fFlowContext);
break;
}
}
}
private void handleLoopReentrance(ASTNode node) {
if (!fSelection.enclosedBy(node) || fLoopReentranceVisitor == null || fLoopReentranceVisitor.getLoopNode() != node)
return;
fLoopReentranceVisitor.process(node);
GenericSequentialFlowInfo info= createSequential();
info.merge(getFlowInfo(node), fFlowContext);
info.merge(fLoopReentranceVisitor.getFlowInfo(node), fFlowContext);
setFlowInfo(node, info);
}
}