120 * continue" iff any intervening finally cannot complete normally or V
121 * is DU at the end of every intervening finally block. This "due to
122 * the continue" concept is then used in the spec for the loops.
123 *
124 * <p>Similarly, break statements must consider intervening finally
125 * blocks. For liveness analysis, a break statement for which any
126 * intervening finally cannot complete normally is not considered to
127 * cause the target statement to be able to complete normally. Then
128 * we say V is DA "due to the break" iff V is DA before the break or
129 * V is DA at the end of any intervening finally block. V is DU "due
130 * to the break" iff any intervening finally cannot complete normally
131 * or V is DU at the break and at the end of every intervening
132 * finally block. (I suspect this latter condition can be
133 * simplified.) This "due to the break" is then used in the spec for
134 * all statements that can be "broken".
135 *
136 * <p>The return statement is treated similarly. V is DA "due to a
137 * return statement" iff V is DA before the return statement or V is
138 * DA at the end of any intervening finally block. Note that we
139 * don't have to worry about the return expression because this
140 * concept is only used for construcrors.
141 *
142 * <p>There is no spec in the JLS for when a variable is definitely
143 * assigned at the end of a constructor, which is needed for final
144 * fields (8.3.1.2). We implement the rule that V is DA at the end
145 * of the constructor iff it is DA and the end of the body of the
146 * constructor and V is DA "due to" every return of the constructor.
147 *
148 * <p>Intervening finally blocks similarly affect exception analysis. An
149 * intervening finally that cannot complete normally allows us to ignore
150 * an otherwise uncaught exception.
151 *
152 * <p>To implement the semantics of intervening finally clauses, all
153 * nonlocal transfers (break, continue, return, throw, method call that
154 * can throw a checked exception, and a constructor invocation that can
155 * thrown a checked exception) are recorded in a queue, and removed
156 * from the queue when we complete processing the target of the
157 * nonlocal transfer. This allows us to modify the queue in accordance
158 * with the above rules when we encounter a finally clause. The only
159 * exception to this [no pun intended] is that checked exceptions that
160 * are known to be caught or declared to be caught in the enclosing
206 private final boolean allowEffectivelyFinalInInnerClasses;
207
208 public static Flow instance(Context context) {
209 Flow instance = context.get(flowKey);
210 if (instance == null)
211 instance = new Flow(context);
212 return instance;
213 }
214
215 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
216 new AliveAnalyzer().analyzeTree(env, make);
217 new AssignAnalyzer().analyzeTree(env, make);
218 new FlowAnalyzer().analyzeTree(env, make);
219 new CaptureAnalyzer().analyzeTree(env, make);
220 }
221
222 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
223 Log.DiagnosticHandler diagHandler = null;
224 //we need to disable diagnostics temporarily; the problem is that if
225 //a lambda expression contains e.g. an unreachable statement, an error
226 //message will be reported and will cause compilation to skip the flow analyis
227 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
228 //related errors, which will allow for more errors to be detected
229 if (!speculative) {
230 diagHandler = new Log.DiscardDiagnosticHandler(log);
231 }
232 try {
233 new LambdaAliveAnalyzer().analyzeTree(env, that, make);
234 } finally {
235 if (!speculative) {
236 log.popDiagnosticHandler(diagHandler);
237 }
238 }
239 }
240
241 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
242 JCLambda that, TreeMaker make) {
243 //we need to disable diagnostics temporarily; the problem is that if
244 //a lambda expression contains e.g. an unreachable statement, an error
245 //message will be reported and will cause compilation to skip the flow analyis
246 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
247 //related errors, which will allow for more errors to be detected
248 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
249 try {
250 new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
251 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
252 flowAnalyzer.analyzeTree(env, that, make);
253 return flowAnalyzer.inferredThrownTypes;
254 } finally {
255 log.popDiagnosticHandler(diagHandler);
256 }
257 }
258
259 public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
260 //we need to disable diagnostics temporarily; the problem is that if
261 //"that" contains e.g. an unreachable statement, an error
262 //message will be reported and will cause compilation to skip the flow analyis
263 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
264 //related errors, which will allow for more errors to be detected
265 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
266 try {
267 SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer();
268
269 analyzer.analyzeTree(env, that, make);
270 return analyzer.isAlive();
271 } finally {
272 log.popDiagnosticHandler(diagHandler);
273 }
274 }
275
276 public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
277 //we need to disable diagnostics temporarily; the problem is that if
278 //"that" contains e.g. an unreachable statement, an error
279 //message will be reported and will cause compilation to skip the flow analyis
280 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
281 //related errors, which will allow for more errors to be detected
282 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
283 try {
284 SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer();
285
286 analyzer.analyzeTree(env, body, make);
287 return analyzer.breaksOut();
288 } finally {
289 log.popDiagnosticHandler(diagHandler);
290 }
291 }
292
293 /**
294 * Definite assignment scan mode
295 */
296 enum FlowKind {
297 /**
298 * This is the normal DA/DU analysis mode
299 */
1293 }
1294
1295 @Override
1296 public void visitIf(JCIf tree) {
1297 scan(tree.cond);
1298 scan(tree.thenpart);
1299 if (tree.elsepart != null) {
1300 scan(tree.elsepart);
1301 }
1302 }
1303
1304 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1305 if (chk.subset(exc, caughtInTry)) {
1306 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1307 } else if (!chk.isUnchecked(pos, exc) &&
1308 !isExceptionOrThrowable(exc) &&
1309 !chk.intersects(exc, thrownInTry)) {
1310 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1311 } else {
1312 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1313 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1314 // unchecked exception, the result list would not be empty, as the augmented
1315 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1316 // exception, that would have been covered in the branch above
1317 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1318 !isExceptionOrThrowable(exc)) {
1319 Warning key = catchableThrownTypes.length() == 1 ?
1320 Warnings.UnreachableCatch(catchableThrownTypes) :
1321 Warnings.UnreachableCatch1(catchableThrownTypes);
1322 log.warning(pos, key);
1323 }
1324 }
1325 }
1326 //where
1327 private boolean isExceptionOrThrowable(Type exc) {
1328 return exc.tsym == syms.throwableType.tsym ||
1329 exc.tsym == syms.exceptionType.tsym;
1330 }
1331
1332 public void visitBreak(JCBreak tree) {
1333 recordExit(new PendingExit(tree));
|
120 * continue" iff any intervening finally cannot complete normally or V
121 * is DU at the end of every intervening finally block. This "due to
122 * the continue" concept is then used in the spec for the loops.
123 *
124 * <p>Similarly, break statements must consider intervening finally
125 * blocks. For liveness analysis, a break statement for which any
126 * intervening finally cannot complete normally is not considered to
127 * cause the target statement to be able to complete normally. Then
128 * we say V is DA "due to the break" iff V is DA before the break or
129 * V is DA at the end of any intervening finally block. V is DU "due
130 * to the break" iff any intervening finally cannot complete normally
131 * or V is DU at the break and at the end of every intervening
132 * finally block. (I suspect this latter condition can be
133 * simplified.) This "due to the break" is then used in the spec for
134 * all statements that can be "broken".
135 *
136 * <p>The return statement is treated similarly. V is DA "due to a
137 * return statement" iff V is DA before the return statement or V is
138 * DA at the end of any intervening finally block. Note that we
139 * don't have to worry about the return expression because this
140 * concept is only used for constructors.
141 *
142 * <p>There is no spec in the JLS for when a variable is definitely
143 * assigned at the end of a constructor, which is needed for final
144 * fields (8.3.1.2). We implement the rule that V is DA at the end
145 * of the constructor iff it is DA and the end of the body of the
146 * constructor and V is DA "due to" every return of the constructor.
147 *
148 * <p>Intervening finally blocks similarly affect exception analysis. An
149 * intervening finally that cannot complete normally allows us to ignore
150 * an otherwise uncaught exception.
151 *
152 * <p>To implement the semantics of intervening finally clauses, all
153 * nonlocal transfers (break, continue, return, throw, method call that
154 * can throw a checked exception, and a constructor invocation that can
155 * thrown a checked exception) are recorded in a queue, and removed
156 * from the queue when we complete processing the target of the
157 * nonlocal transfer. This allows us to modify the queue in accordance
158 * with the above rules when we encounter a finally clause. The only
159 * exception to this [no pun intended] is that checked exceptions that
160 * are known to be caught or declared to be caught in the enclosing
206 private final boolean allowEffectivelyFinalInInnerClasses;
207
208 public static Flow instance(Context context) {
209 Flow instance = context.get(flowKey);
210 if (instance == null)
211 instance = new Flow(context);
212 return instance;
213 }
214
215 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
216 new AliveAnalyzer().analyzeTree(env, make);
217 new AssignAnalyzer().analyzeTree(env, make);
218 new FlowAnalyzer().analyzeTree(env, make);
219 new CaptureAnalyzer().analyzeTree(env, make);
220 }
221
222 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
223 Log.DiagnosticHandler diagHandler = null;
224 //we need to disable diagnostics temporarily; the problem is that if
225 //a lambda expression contains e.g. an unreachable statement, an error
226 //message will be reported and will cause compilation to skip the flow analysis
227 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
228 //related errors, which will allow for more errors to be detected
229 if (!speculative) {
230 diagHandler = new Log.DiscardDiagnosticHandler(log);
231 }
232 try {
233 new LambdaAliveAnalyzer().analyzeTree(env, that, make);
234 } finally {
235 if (!speculative) {
236 log.popDiagnosticHandler(diagHandler);
237 }
238 }
239 }
240
241 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
242 JCLambda that, TreeMaker make) {
243 //we need to disable diagnostics temporarily; the problem is that if
244 //a lambda expression contains e.g. an unreachable statement, an error
245 //message will be reported and will cause compilation to skip the flow analysis
246 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
247 //related errors, which will allow for more errors to be detected
248 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
249 try {
250 new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
251 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
252 flowAnalyzer.analyzeTree(env, that, make);
253 return flowAnalyzer.inferredThrownTypes;
254 } finally {
255 log.popDiagnosticHandler(diagHandler);
256 }
257 }
258
259 public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
260 //we need to disable diagnostics temporarily; the problem is that if
261 //"that" contains e.g. an unreachable statement, an error
262 //message will be reported and will cause compilation to skip the flow analysis
263 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
264 //related errors, which will allow for more errors to be detected
265 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
266 try {
267 SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer();
268
269 analyzer.analyzeTree(env, that, make);
270 return analyzer.isAlive();
271 } finally {
272 log.popDiagnosticHandler(diagHandler);
273 }
274 }
275
276 public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
277 //we need to disable diagnostics temporarily; the problem is that if
278 //"that" contains e.g. an unreachable statement, an error
279 //message will be reported and will cause compilation to skip the flow analysis
280 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
281 //related errors, which will allow for more errors to be detected
282 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
283 try {
284 SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer();
285
286 analyzer.analyzeTree(env, body, make);
287 return analyzer.breaksOut();
288 } finally {
289 log.popDiagnosticHandler(diagHandler);
290 }
291 }
292
293 /**
294 * Definite assignment scan mode
295 */
296 enum FlowKind {
297 /**
298 * This is the normal DA/DU analysis mode
299 */
1293 }
1294
1295 @Override
1296 public void visitIf(JCIf tree) {
1297 scan(tree.cond);
1298 scan(tree.thenpart);
1299 if (tree.elsepart != null) {
1300 scan(tree.elsepart);
1301 }
1302 }
1303
1304 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) {
1305 if (chk.subset(exc, caughtInTry)) {
1306 log.error(pos, Errors.ExceptAlreadyCaught(exc));
1307 } else if (!chk.isUnchecked(pos, exc) &&
1308 !isExceptionOrThrowable(exc) &&
1309 !chk.intersects(exc, thrownInTry)) {
1310 log.error(pos, Errors.ExceptNeverThrownInTry(exc));
1311 } else {
1312 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry);
1313 // 'catchableThrownTypes' cannot possibly be empty - if 'exc' was an
1314 // unchecked exception, the result list would not be empty, as the augmented
1315 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1316 // exception, that would have been covered in the branch above
1317 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1318 !isExceptionOrThrowable(exc)) {
1319 Warning key = catchableThrownTypes.length() == 1 ?
1320 Warnings.UnreachableCatch(catchableThrownTypes) :
1321 Warnings.UnreachableCatch1(catchableThrownTypes);
1322 log.warning(pos, key);
1323 }
1324 }
1325 }
1326 //where
1327 private boolean isExceptionOrThrowable(Type exc) {
1328 return exc.tsym == syms.throwableType.tsym ||
1329 exc.tsym == syms.exceptionType.tsym;
1330 }
1331
1332 public void visitBreak(JCBreak tree) {
1333 recordExit(new PendingExit(tree));
|