-
Notifications
You must be signed in to change notification settings - Fork 82
/
InputFlowAnalyzer.java
287 lines (264 loc) · 9.88 KB
/
InputFlowAnalyzer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*******************************************************************************
* Copyright (c) 2000, 2023 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
* 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)
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] Missing return value, while extracting code out of a loop - https://bugs.eclipse.org/bugs/show_bug.cgi?id=213519
* Microsoft Corporation - copied to jdt.core.manipulation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring.code.flow;
import java.util.List;
import org.eclipse.core.runtime.Assert;
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.BreakStatement;
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.SwitchExpression;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
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;
}
@Override
protected boolean traverseNode(ASTNode node) {
return true; // end <= fSelection.end || fSelection.enclosedBy(start, end);
}
@Override
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();
}
public void process(ASTNode node) {
try {
fFlowContext.setLoopReentranceMode(true);
node.accept(this);
} finally {
fFlowContext.setLoopReentranceMode(false);
}
}
@Override
public void endVisit(BreakStatement node) {
if (node.getStartPosition() + node.getLength() <= fSelection.getExclusiveEnd())
return;
super.endVisit(node);
}
@Override
public void endVisit(DoStatement node) {
if (skipNode(node))
return;
DoWhileFlowInfo info= createDoWhile();
setFlowInfo(node, info);
info.mergeAction(getFlowInfo(node.getBody()));
// No need to merge the condition. It was already considered by the InputFlowAnalyzer.
info.removeLabel(null);
}
@Override
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);
}
@Override
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;
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);
}
@Override
protected boolean traverseNode(ASTNode node) {
return node.getStartPosition() + node.getLength() > fSelection.getInclusiveEnd();
}
@Override
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();
}
@Override
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);
}
}
@Override
public void endVisit(DoStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
@Override
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);
}
}
@Override
public void endVisit(EnhancedForStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
@Override
public void endVisit(ForStatement node) {
super.endVisit(node);
handleLoopReentrance(node);
}
@Override
public void endVisit(SwitchStatement node) {
if (skipNode(node))
return;
SwitchData data= preEndVisit(node, node.statements(), node.getExpression());
if (data == null) {
return;
}
super.endVisit(node, data);
}
@Override
public void endVisit(SwitchExpression node) {
if (skipNode(node))
return;
SwitchData data= preEndVisit(node, node.statements(), node.getExpression());
if (data == null) {
return;
}
super.endVisit(node, data);
}
public SwitchData preEndVisit(ASTNode node, List<Statement> statements, Expression expression) {
SwitchData data= createSwitchData(statements);
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(expression), fFlowContext);
info.merge(data.getInfo(i), fFlowContext);
info.removeLabel(null);
return null;
}
}
return data;
}
@Override
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 (ASTNode branch : branches) {
if (branch != null && fSelection.coveredBy(branch)) {
info.merge(getFlowInfo(branch), fFlowContext);
break;
}
}
}
private void handleLoopReentrance(ASTNode node) {
if (fDoLoopReentrance && fSelection.coveredBy(node) && !fSelection.covers(node)) {
LoopReentranceVisitor loopReentranceVisitor= new LoopReentranceVisitor(fFlowContext, fSelection, node);
loopReentranceVisitor.process(node);
GenericSequentialFlowInfo info= createSequential();
info.merge(getFlowInfo(node), fFlowContext);
info.merge(loopReentranceVisitor.getFlowInfo(node), fFlowContext);
setFlowInfo(node, info);
}
}
}