11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 //todo: one might eliminate uninits.andSets when monotonic
27
28 package com.sun.tools.javac.comp;
29
30 import java.util.HashMap;
31
32 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
33 import com.sun.tools.javac.code.*;
34 import com.sun.tools.javac.code.Scope.WriteableScope;
35 import com.sun.tools.javac.code.Source.Feature;
36 import com.sun.tools.javac.resources.CompilerProperties.Errors;
37 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
38 import com.sun.tools.javac.tree.*;
39 import com.sun.tools.javac.util.*;
40 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
41 import com.sun.tools.javac.util.JCDiagnostic.Error;
42 import com.sun.tools.javac.util.JCDiagnostic.Warning;
43
44 import com.sun.tools.javac.code.Symbol.*;
45 import com.sun.tools.javac.tree.JCTree.*;
46
47 import static com.sun.tools.javac.code.Flags.*;
48 import static com.sun.tools.javac.code.Flags.BLOCK;
49 import static com.sun.tools.javac.code.Kinds.Kind.*;
50 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
194 private final Log log;
195 private final Symtab syms;
196 private final Types types;
197 private final Check chk;
198 private TreeMaker make;
199 private final Resolve rs;
200 private final JCDiagnostic.Factory diags;
201 private Env<AttrContext> attrEnv;
202 private Lint lint;
203 private final boolean allowEffectivelyFinalInInnerClasses;
204
205 public static Flow instance(Context context) {
206 Flow instance = context.get(flowKey);
207 if (instance == null)
208 instance = new Flow(context);
209 return instance;
210 }
211
212 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
213 new AliveAnalyzer().analyzeTree(env, make);
214 new AssignAnalyzer().analyzeTree(env);
215 new FlowAnalyzer().analyzeTree(env, make);
216 new CaptureAnalyzer().analyzeTree(env, make);
217 }
218
219 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
220 Log.DiagnosticHandler diagHandler = null;
221 //we need to disable diagnostics temporarily; the problem is that if
222 //a lambda expression contains e.g. an unreachable statement, an error
223 //message will be reported and will cause compilation to skip the flow analyis
224 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
225 //related errors, which will allow for more errors to be detected
226 if (!speculative) {
227 diagHandler = new Log.DiscardDiagnosticHandler(log);
228 }
229 try {
230 new LambdaAliveAnalyzer().analyzeTree(env, that, make);
231 } finally {
232 if (!speculative) {
233 log.popDiagnosticHandler(diagHandler);
234 }
235 }
236 }
237
238 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
239 JCLambda that, TreeMaker make) {
240 //we need to disable diagnostics temporarily; the problem is that if
241 //a lambda expression contains e.g. an unreachable statement, an error
242 //message will be reported and will cause compilation to skip the flow analyis
243 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
244 //related errors, which will allow for more errors to be detected
245 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
246 try {
247 new LambdaAssignAnalyzer(env).analyzeTree(env, that);
248 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
249 flowAnalyzer.analyzeTree(env, that, make);
250 return flowAnalyzer.inferredThrownTypes;
251 } finally {
252 log.popDiagnosticHandler(diagHandler);
253 }
254 }
255
256 /**
257 * Definite assignment scan mode
258 */
259 enum FlowKind {
260 /**
261 * This is the normal DA/DU analysis mode
262 */
263 NORMAL("var.might.already.be.assigned", false),
264 /**
265 * This is the speculative DA/DU analysis mode used to speculatively
266 * derive assertions within loop bodies
267 */
379 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
380 }
381
382 /** Resolve all breaks of this statement. */
383 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
384 return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
385 }
386
387 @Override
388 public void scan(JCTree tree) {
389 if (tree != null && (
390 tree.type == null ||
391 tree.type != Type.stuckType)) {
392 super.scan(tree);
393 }
394 }
395
396 public void visitPackageDef(JCPackageDecl tree) {
397 // Do nothing for PackageDecl
398 }
399 }
400
401 /**
402 * This pass implements the first step of the dataflow analysis, namely
403 * the liveness analysis check. This checks that every statement is reachable.
404 * The output of this analysis pass are used by other analyzers. This analyzer
405 * sets the 'finallyCanCompleteNormally' field in the JCTry class.
406 */
407 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
408
409 /** A flag that indicates whether the last statement could
410 * complete normally.
411 */
412 private boolean alive;
413
414 @Override
415 void markDead() {
416 alive = false;
417 }
418
579 alive |= resolveContinues(tree);
580 resolveBreaks(tree, prevPendingExits);
581 alive = true;
582 }
583
584 public void visitLabelled(JCLabeledStatement tree) {
585 ListBuffer<PendingExit> prevPendingExits = pendingExits;
586 pendingExits = new ListBuffer<>();
587 scanStat(tree.body);
588 alive |= resolveBreaks(tree, prevPendingExits);
589 }
590
591 public void visitSwitch(JCSwitch tree) {
592 ListBuffer<PendingExit> prevPendingExits = pendingExits;
593 pendingExits = new ListBuffer<>();
594 scan(tree.selector);
595 boolean hasDefault = false;
596 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
597 alive = true;
598 JCCase c = l.head;
599 if (c.pat == null)
600 hasDefault = true;
601 else
602 scan(c.pat);
603 scanStats(c.stats);
604 // Warn about fall-through if lint switch fallthrough enabled.
605 if (alive &&
606 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
607 c.stats.nonEmpty() && l.tail.nonEmpty())
608 log.warning(Lint.LintCategory.FALLTHROUGH,
609 l.tail.head.pos(),
610 Warnings.PossibleFallThroughIntoCase);
611 }
612 if (!hasDefault) {
613 alive = true;
614 }
615 alive |= resolveBreaks(tree, prevPendingExits);
616 }
617
618 public void visitTry(JCTry tree) {
619 ListBuffer<PendingExit> prevPendingExits = pendingExits;
620 pendingExits = new ListBuffer<>();
621 for (JCTree resource : tree.resources) {
622 if (resource instanceof JCVariableDecl) {
623 JCVariableDecl vdecl = (JCVariableDecl) resource;
624 visitVarDef(vdecl);
625 } else if (resource instanceof JCExpression) {
626 scan((JCExpression) resource);
627 } else {
628 throw new AssertionError(tree); // parser error
629 }
630 }
631
632 scanStat(tree.body);
633 boolean aliveEnd = alive;
634
635 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
636 alive = true;
637 JCVariableDecl param = l.head.param;
663 pendingExits = prevPendingExits;
664 while (exits.nonEmpty()) pendingExits.append(exits.next());
665 }
666 }
667
668 @Override
669 public void visitIf(JCIf tree) {
670 scan(tree.cond);
671 scanStat(tree.thenpart);
672 if (tree.elsepart != null) {
673 boolean aliveAfterThen = alive;
674 alive = true;
675 scanStat(tree.elsepart);
676 alive = alive | aliveAfterThen;
677 } else {
678 alive = true;
679 }
680 }
681
682 public void visitBreak(JCBreak tree) {
683 recordExit(new PendingExit(tree));
684 }
685
686 public void visitContinue(JCContinue tree) {
687 recordExit(new PendingExit(tree));
688 }
689
690 public void visitReturn(JCReturn tree) {
691 scan(tree.expr);
692 recordExit(new PendingExit(tree));
693 }
694
695 public void visitThrow(JCThrow tree) {
696 scan(tree.expr);
697 markDead();
698 }
699
700 public void visitApply(JCMethodInvocation tree) {
701 scan(tree.meth);
702 scan(tree.args);
1023 }
1024
1025 public void visitForeachLoop(JCEnhancedForLoop tree) {
1026 visitVarDef(tree.var);
1027 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1028 scan(tree.expr);
1029 pendingExits = new ListBuffer<>();
1030 scan(tree.body);
1031 resolveContinues(tree);
1032 resolveBreaks(tree, prevPendingExits);
1033 }
1034
1035 public void visitLabelled(JCLabeledStatement tree) {
1036 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1037 pendingExits = new ListBuffer<>();
1038 scan(tree.body);
1039 resolveBreaks(tree, prevPendingExits);
1040 }
1041
1042 public void visitSwitch(JCSwitch tree) {
1043 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1044 pendingExits = new ListBuffer<>();
1045 scan(tree.selector);
1046 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
1047 JCCase c = l.head;
1048 if (c.pat != null) {
1049 scan(c.pat);
1050 }
1051 scan(c.stats);
1052 }
1053 resolveBreaks(tree, prevPendingExits);
1054 }
1055
1056 public void visitTry(JCTry tree) {
1057 List<Type> caughtPrev = caught;
1058 List<Type> thrownPrev = thrown;
1059 thrown = List.nil();
1060 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1061 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1062 ((JCTypeUnion)l.head.param.vartype).alternatives :
1063 List.of(l.head.param.vartype);
1064 for (JCExpression ct : subClauses) {
1065 caught = chk.incl(ct.type, caught);
1066 }
1067 }
1068
1069 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1070 pendingExits = new ListBuffer<>();
1174 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1175 // unchecked exception, the result list would not be empty, as the augmented
1176 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1177 // exception, that would have been covered in the branch above
1178 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1179 !isExceptionOrThrowable(exc)) {
1180 Warning key = catchableThrownTypes.length() == 1 ?
1181 Warnings.UnreachableCatch(catchableThrownTypes) :
1182 Warnings.UnreachableCatch1(catchableThrownTypes);
1183 log.warning(pos, key);
1184 }
1185 }
1186 }
1187 //where
1188 private boolean isExceptionOrThrowable(Type exc) {
1189 return exc.tsym == syms.throwableType.tsym ||
1190 exc.tsym == syms.exceptionType.tsym;
1191 }
1192
1193 public void visitBreak(JCBreak tree) {
1194 recordExit(new FlowPendingExit(tree, null));
1195 }
1196
1197 public void visitContinue(JCContinue tree) {
1198 recordExit(new FlowPendingExit(tree, null));
1199 }
1200
1201 public void visitReturn(JCReturn tree) {
1202 scan(tree.expr);
1203 recordExit(new FlowPendingExit(tree, null));
1204 }
1205
1206 public void visitThrow(JCThrow tree) {
1207 scan(tree.expr);
1208 Symbol sym = TreeInfo.symbol(tree.expr);
1209 if (sym != null &&
1210 sym.kind == VAR &&
1211 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1212 preciseRethrowTypes.get(sym) != null) {
1213 for (Type t : preciseRethrowTypes.get(sym)) {
2088 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
2089 break;
2090 uninits.assign(uninitsEntry.andSet(uninits));
2091 flowKind = FlowKind.SPECULATIVE_LOOP;
2092 } while (true);
2093 flowKind = prevFlowKind;
2094 inits.assign(initsStart);
2095 uninits.assign(uninitsStart.andSet(uninits));
2096 resolveBreaks(tree, prevPendingExits);
2097 nextadr = nextadrPrev;
2098 }
2099
2100 public void visitLabelled(JCLabeledStatement tree) {
2101 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2102 pendingExits = new ListBuffer<>();
2103 scan(tree.body);
2104 resolveBreaks(tree, prevPendingExits);
2105 }
2106
2107 public void visitSwitch(JCSwitch tree) {
2108 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2109 pendingExits = new ListBuffer<>();
2110 int nextadrPrev = nextadr;
2111 scanExpr(tree.selector);
2112 final Bits initsSwitch = new Bits(inits);
2113 final Bits uninitsSwitch = new Bits(uninits);
2114 boolean hasDefault = false;
2115 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
2116 inits.assign(initsSwitch);
2117 uninits.assign(uninits.andSet(uninitsSwitch));
2118 JCCase c = l.head;
2119 if (c.pat == null) {
2120 hasDefault = true;
2121 } else {
2122 scanExpr(c.pat);
2123 }
2124 if (hasDefault) {
2125 inits.assign(initsSwitch);
2126 uninits.assign(uninits.andSet(uninitsSwitch));
2127 }
2128 scan(c.stats);
2129 addVars(c.stats, initsSwitch, uninitsSwitch);
2130 if (!hasDefault) {
2131 inits.assign(initsSwitch);
2132 uninits.assign(uninits.andSet(uninitsSwitch));
2133 }
2134 // Warn about fall-through if lint switch fallthrough enabled.
2135 }
2136 if (!hasDefault) {
2137 inits.andSet(initsSwitch);
2138 }
2139 resolveBreaks(tree, prevPendingExits);
2140 nextadr = nextadrPrev;
2141 }
2142 // where
2143 /** Add any variables defined in stats to inits and uninits. */
2144 private void addVars(List<JCStatement> stats, final Bits inits,
2145 final Bits uninits) {
2146 for (;stats.nonEmpty(); stats = stats.tail) {
2147 JCTree stat = stats.head;
2148 if (stat.hasTag(VARDEF)) {
2284 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
2285 inits.assign(initsWhenTrue);
2286 uninits.assign(uninitsWhenTrue);
2287 scan(tree.thenpart);
2288 if (tree.elsepart != null) {
2289 final Bits initsAfterThen = new Bits(inits);
2290 final Bits uninitsAfterThen = new Bits(uninits);
2291 inits.assign(initsBeforeElse);
2292 uninits.assign(uninitsBeforeElse);
2293 scan(tree.elsepart);
2294 inits.andSet(initsAfterThen);
2295 uninits.andSet(uninitsAfterThen);
2296 } else {
2297 inits.andSet(initsBeforeElse);
2298 uninits.andSet(uninitsBeforeElse);
2299 }
2300 }
2301
2302 @Override
2303 public void visitBreak(JCBreak tree) {
2304 recordExit(new AssignPendingExit(tree, inits, uninits));
2305 }
2306
2307 @Override
2308 public void visitContinue(JCContinue tree) {
2309 recordExit(new AssignPendingExit(tree, inits, uninits));
2310 }
2311
2312 @Override
2313 public void visitReturn(JCReturn tree) {
2314 scanExpr(tree.expr);
2315 recordExit(new AssignPendingExit(tree, inits, uninits));
2316 }
2317
2318 public void visitThrow(JCThrow tree) {
2319 scanExpr(tree.expr);
2320 markDead();
2321 }
2322
2323 public void visitApply(JCMethodInvocation tree) {
2462
2463 void referenced(Symbol sym) {
2464 unrefdResources.remove(sym);
2465 }
2466
2467 public void visitAnnotatedType(JCAnnotatedType tree) {
2468 // annotations don't get scanned
2469 tree.underlyingType.accept(this);
2470 }
2471
2472 public void visitModuleDef(JCModuleDecl tree) {
2473 // Do nothing for modules
2474 }
2475
2476 /**************************************************************************
2477 * main method
2478 *************************************************************************/
2479
2480 /** Perform definite assignment/unassignment analysis on a tree.
2481 */
2482 public void analyzeTree(Env<?> env) {
2483 analyzeTree(env, env.tree);
2484 }
2485
2486 public void analyzeTree(Env<?> env, JCTree tree) {
2487 try {
2488 startPos = tree.pos().getStartPosition();
2489
2490 if (vardecls == null)
2491 vardecls = new JCVariableDecl[32];
2492 else
2493 for (int i=0; i<vardecls.length; i++)
2494 vardecls[i] = null;
2495 firstadr = 0;
2496 nextadr = 0;
2497 pendingExits = new ListBuffer<>();
2498 this.classDef = null;
2499 unrefdResources = WriteableScope.create(env.enclClass.sym);
2500 scan(tree);
2501 } finally {
2502 // note that recursive invocations of this method fail hard
2503 startPos = -1;
2504 resetBits(inits, uninits, uninitsTry, initsWhenTrue,
2505 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
2506 if (vardecls != null) {
2507 for (int i=0; i<vardecls.length; i++)
2508 vardecls[i] = null;
2509 }
2510 firstadr = 0;
2511 nextadr = 0;
2512 pendingExits = null;
2513 this.classDef = null;
2514 unrefdResources = null;
2515 }
2516 }
2517 }
2518
2519 /**
2520 * This pass implements the last step of the dataflow analysis, namely
2521 * the effectively-final analysis check. This checks that every local variable
2522 * reference from a lambda body/local inner class is either final or effectively final.
2523 * Additional this also checks that every variable that is used as an operand to
2524 * try-with-resources is final or effectively final.
2525 * As effectively final variables are marked as such during DA/DU, this pass must run after
2526 * AssignAnalyzer.
2527 */
2528 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
2529
2530 JCTree currentTree; //local class or lambda
2531
2642 case PREINC: case POSTINC:
2643 case PREDEC: case POSTDEC:
2644 scan(tree.arg);
2645 letInit(tree.arg);
2646 break;
2647 default:
2648 scan(tree.arg);
2649 }
2650 }
2651
2652 public void visitTry(JCTry tree) {
2653 for (JCTree resource : tree.resources) {
2654 if (!resource.hasTag(VARDEF)) {
2655 Symbol var = TreeInfo.symbol(resource);
2656 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) {
2657 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var));
2658 }
2659 }
2660 }
2661 super.visitTry(tree);
2662 }
2663
2664 public void visitModuleDef(JCModuleDecl tree) {
2665 // Do nothing for modules
2666 }
2667
2668 /**************************************************************************
2669 * main method
2670 *************************************************************************/
2671
2672 /** Perform definite assignment/unassignment analysis on a tree.
2673 */
2674 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
2675 analyzeTree(env, env.tree, make);
2676 }
2677 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
2678 try {
2679 attrEnv = env;
2680 Flow.this.make = make;
2681 pendingExits = new ListBuffer<>();
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 //todo: one might eliminate uninits.andSets when monotonic
27
28 package com.sun.tools.javac.comp;
29
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Set;
33
34 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
35 import com.sun.tools.javac.code.*;
36 import com.sun.tools.javac.code.Scope.WriteableScope;
37 import com.sun.tools.javac.code.Source.Feature;
38 import com.sun.tools.javac.resources.CompilerProperties.Errors;
39 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
40 import com.sun.tools.javac.tree.*;
41 import com.sun.tools.javac.util.*;
42 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
43 import com.sun.tools.javac.util.JCDiagnostic.Error;
44 import com.sun.tools.javac.util.JCDiagnostic.Warning;
45
46 import com.sun.tools.javac.code.Symbol.*;
47 import com.sun.tools.javac.tree.JCTree.*;
48
49 import static com.sun.tools.javac.code.Flags.*;
50 import static com.sun.tools.javac.code.Flags.BLOCK;
51 import static com.sun.tools.javac.code.Kinds.Kind.*;
52 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
196 private final Log log;
197 private final Symtab syms;
198 private final Types types;
199 private final Check chk;
200 private TreeMaker make;
201 private final Resolve rs;
202 private final JCDiagnostic.Factory diags;
203 private Env<AttrContext> attrEnv;
204 private Lint lint;
205 private final boolean allowEffectivelyFinalInInnerClasses;
206
207 public static Flow instance(Context context) {
208 Flow instance = context.get(flowKey);
209 if (instance == null)
210 instance = new Flow(context);
211 return instance;
212 }
213
214 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
215 new AliveAnalyzer().analyzeTree(env, make);
216 new AssignAnalyzer().analyzeTree(env, make);
217 new FlowAnalyzer().analyzeTree(env, make);
218 new CaptureAnalyzer().analyzeTree(env, make);
219 }
220
221 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) {
222 Log.DiagnosticHandler diagHandler = null;
223 //we need to disable diagnostics temporarily; the problem is that if
224 //a lambda expression contains e.g. an unreachable statement, an error
225 //message will be reported and will cause compilation to skip the flow analyis
226 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
227 //related errors, which will allow for more errors to be detected
228 if (!speculative) {
229 diagHandler = new Log.DiscardDiagnosticHandler(log);
230 }
231 try {
232 new LambdaAliveAnalyzer().analyzeTree(env, that, make);
233 } finally {
234 if (!speculative) {
235 log.popDiagnosticHandler(diagHandler);
236 }
237 }
238 }
239
240 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env,
241 JCLambda that, TreeMaker make) {
242 //we need to disable diagnostics temporarily; the problem is that if
243 //a lambda expression contains e.g. an unreachable statement, an error
244 //message will be reported and will cause compilation to skip the flow analyis
245 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
246 //related errors, which will allow for more errors to be detected
247 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
248 try {
249 new LambdaAssignAnalyzer(env).analyzeTree(env, that, make);
250 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
251 flowAnalyzer.analyzeTree(env, that, make);
252 return flowAnalyzer.inferredThrownTypes;
253 } finally {
254 log.popDiagnosticHandler(diagHandler);
255 }
256 }
257
258 /**
259 * Definite assignment scan mode
260 */
261 enum FlowKind {
262 /**
263 * This is the normal DA/DU analysis mode
264 */
265 NORMAL("var.might.already.be.assigned", false),
266 /**
267 * This is the speculative DA/DU analysis mode used to speculatively
268 * derive assertions within loop bodies
269 */
381 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
382 }
383
384 /** Resolve all breaks of this statement. */
385 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
386 return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
387 }
388
389 @Override
390 public void scan(JCTree tree) {
391 if (tree != null && (
392 tree.type == null ||
393 tree.type != Type.stuckType)) {
394 super.scan(tree);
395 }
396 }
397
398 public void visitPackageDef(JCPackageDecl tree) {
399 // Do nothing for PackageDecl
400 }
401
402 protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
403 JCBreak brk = make.at(Position.NOPOS).Break(null);
404 brk.target = swtch;
405 scan(brk);
406 }
407 }
408
409 /**
410 * This pass implements the first step of the dataflow analysis, namely
411 * the liveness analysis check. This checks that every statement is reachable.
412 * The output of this analysis pass are used by other analyzers. This analyzer
413 * sets the 'finallyCanCompleteNormally' field in the JCTry class.
414 */
415 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
416
417 /** A flag that indicates whether the last statement could
418 * complete normally.
419 */
420 private boolean alive;
421
422 @Override
423 void markDead() {
424 alive = false;
425 }
426
587 alive |= resolveContinues(tree);
588 resolveBreaks(tree, prevPendingExits);
589 alive = true;
590 }
591
592 public void visitLabelled(JCLabeledStatement tree) {
593 ListBuffer<PendingExit> prevPendingExits = pendingExits;
594 pendingExits = new ListBuffer<>();
595 scanStat(tree.body);
596 alive |= resolveBreaks(tree, prevPendingExits);
597 }
598
599 public void visitSwitch(JCSwitch tree) {
600 ListBuffer<PendingExit> prevPendingExits = pendingExits;
601 pendingExits = new ListBuffer<>();
602 scan(tree.selector);
603 boolean hasDefault = false;
604 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
605 alive = true;
606 JCCase c = l.head;
607 if (c.pats.isEmpty())
608 hasDefault = true;
609 else {
610 for (JCExpression pat : c.pats) {
611 scan(pat);
612 }
613 }
614 scanStats(c.stats);
615 c.completesNormally = alive;
616 if (alive && c.caseKind == JCCase.RULE) {
617 scanSyntheticBreak(make, tree);
618 alive = false;
619 }
620 // Warn about fall-through if lint switch fallthrough enabled.
621 if (alive &&
622 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
623 c.stats.nonEmpty() && l.tail.nonEmpty())
624 log.warning(Lint.LintCategory.FALLTHROUGH,
625 l.tail.head.pos(),
626 Warnings.PossibleFallThroughIntoCase);
627 }
628 if (!hasDefault) {
629 alive = true;
630 }
631 alive |= resolveBreaks(tree, prevPendingExits);
632 }
633
634 @Override
635 public void visitSwitchExpression(JCSwitchExpression tree) {
636 ListBuffer<PendingExit> prevPendingExits = pendingExits;
637 pendingExits = new ListBuffer<>();
638 scan(tree.selector);
639 Set<Object> constants = null;
640 if ((tree.selector.type.tsym.flags() & ENUM) != 0) {
641 constants = new HashSet<>();
642 for (Symbol s : tree.selector.type.tsym.members().getSymbols(s -> (s.flags() & ENUM) != 0)) {
643 constants.add(s.name);
644 }
645 }
646 boolean hasDefault = false;
647 boolean prevAlive = alive;
648 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
649 alive = true;
650 JCCase c = l.head;
651 if (c.pats.isEmpty())
652 hasDefault = true;
653 else {
654 for (JCExpression pat : c.pats) {
655 scan(pat);
656 if (constants != null) {
657 if (pat.hasTag(IDENT))
658 constants.remove(((JCIdent) pat).name);
659 if (pat.type != null)
660 constants.remove(pat.type.constValue());
661 }
662 }
663 }
664 scanStats(c.stats);
665 c.completesNormally = alive;
666 }
667 if ((constants == null || !constants.isEmpty()) && !hasDefault) {
668 log.error(tree, Errors.NotExhaustive);
669 }
670 alive = prevAlive;
671 alive |= resolveBreaks(tree, prevPendingExits);
672 }
673
674 public void visitTry(JCTry tree) {
675 ListBuffer<PendingExit> prevPendingExits = pendingExits;
676 pendingExits = new ListBuffer<>();
677 for (JCTree resource : tree.resources) {
678 if (resource instanceof JCVariableDecl) {
679 JCVariableDecl vdecl = (JCVariableDecl) resource;
680 visitVarDef(vdecl);
681 } else if (resource instanceof JCExpression) {
682 scan((JCExpression) resource);
683 } else {
684 throw new AssertionError(tree); // parser error
685 }
686 }
687
688 scanStat(tree.body);
689 boolean aliveEnd = alive;
690
691 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
692 alive = true;
693 JCVariableDecl param = l.head.param;
719 pendingExits = prevPendingExits;
720 while (exits.nonEmpty()) pendingExits.append(exits.next());
721 }
722 }
723
724 @Override
725 public void visitIf(JCIf tree) {
726 scan(tree.cond);
727 scanStat(tree.thenpart);
728 if (tree.elsepart != null) {
729 boolean aliveAfterThen = alive;
730 alive = true;
731 scanStat(tree.elsepart);
732 alive = alive | aliveAfterThen;
733 } else {
734 alive = true;
735 }
736 }
737
738 public void visitBreak(JCBreak tree) {
739 if (tree.isValueBreak())
740 scan(tree.value);
741 recordExit(new PendingExit(tree));
742 }
743
744 public void visitContinue(JCContinue tree) {
745 recordExit(new PendingExit(tree));
746 }
747
748 public void visitReturn(JCReturn tree) {
749 scan(tree.expr);
750 recordExit(new PendingExit(tree));
751 }
752
753 public void visitThrow(JCThrow tree) {
754 scan(tree.expr);
755 markDead();
756 }
757
758 public void visitApply(JCMethodInvocation tree) {
759 scan(tree.meth);
760 scan(tree.args);
1081 }
1082
1083 public void visitForeachLoop(JCEnhancedForLoop tree) {
1084 visitVarDef(tree.var);
1085 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1086 scan(tree.expr);
1087 pendingExits = new ListBuffer<>();
1088 scan(tree.body);
1089 resolveContinues(tree);
1090 resolveBreaks(tree, prevPendingExits);
1091 }
1092
1093 public void visitLabelled(JCLabeledStatement tree) {
1094 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1095 pendingExits = new ListBuffer<>();
1096 scan(tree.body);
1097 resolveBreaks(tree, prevPendingExits);
1098 }
1099
1100 public void visitSwitch(JCSwitch tree) {
1101 handleSwitch(tree, tree.selector, tree.cases);
1102 }
1103
1104 @Override
1105 public void visitSwitchExpression(JCSwitchExpression tree) {
1106 handleSwitch(tree, tree.selector, tree.cases);
1107 }
1108
1109 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
1110 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1111 pendingExits = new ListBuffer<>();
1112 scan(selector);
1113 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
1114 JCCase c = l.head;
1115 scan(c.pats);
1116 scan(c.stats);
1117 }
1118 resolveBreaks(tree, prevPendingExits);
1119 }
1120
1121 public void visitTry(JCTry tree) {
1122 List<Type> caughtPrev = caught;
1123 List<Type> thrownPrev = thrown;
1124 thrown = List.nil();
1125 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1126 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1127 ((JCTypeUnion)l.head.param.vartype).alternatives :
1128 List.of(l.head.param.vartype);
1129 for (JCExpression ct : subClauses) {
1130 caught = chk.incl(ct.type, caught);
1131 }
1132 }
1133
1134 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1135 pendingExits = new ListBuffer<>();
1239 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1240 // unchecked exception, the result list would not be empty, as the augmented
1241 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1242 // exception, that would have been covered in the branch above
1243 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1244 !isExceptionOrThrowable(exc)) {
1245 Warning key = catchableThrownTypes.length() == 1 ?
1246 Warnings.UnreachableCatch(catchableThrownTypes) :
1247 Warnings.UnreachableCatch1(catchableThrownTypes);
1248 log.warning(pos, key);
1249 }
1250 }
1251 }
1252 //where
1253 private boolean isExceptionOrThrowable(Type exc) {
1254 return exc.tsym == syms.throwableType.tsym ||
1255 exc.tsym == syms.exceptionType.tsym;
1256 }
1257
1258 public void visitBreak(JCBreak tree) {
1259 if (tree.isValueBreak())
1260 scan(tree.value);
1261 recordExit(new FlowPendingExit(tree, null));
1262 }
1263
1264 public void visitContinue(JCContinue tree) {
1265 recordExit(new FlowPendingExit(tree, null));
1266 }
1267
1268 public void visitReturn(JCReturn tree) {
1269 scan(tree.expr);
1270 recordExit(new FlowPendingExit(tree, null));
1271 }
1272
1273 public void visitThrow(JCThrow tree) {
1274 scan(tree.expr);
1275 Symbol sym = TreeInfo.symbol(tree.expr);
1276 if (sym != null &&
1277 sym.kind == VAR &&
1278 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1279 preciseRethrowTypes.get(sym) != null) {
1280 for (Type t : preciseRethrowTypes.get(sym)) {
2155 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
2156 break;
2157 uninits.assign(uninitsEntry.andSet(uninits));
2158 flowKind = FlowKind.SPECULATIVE_LOOP;
2159 } while (true);
2160 flowKind = prevFlowKind;
2161 inits.assign(initsStart);
2162 uninits.assign(uninitsStart.andSet(uninits));
2163 resolveBreaks(tree, prevPendingExits);
2164 nextadr = nextadrPrev;
2165 }
2166
2167 public void visitLabelled(JCLabeledStatement tree) {
2168 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2169 pendingExits = new ListBuffer<>();
2170 scan(tree.body);
2171 resolveBreaks(tree, prevPendingExits);
2172 }
2173
2174 public void visitSwitch(JCSwitch tree) {
2175 handleSwitch(tree, tree.selector, tree.cases);
2176 }
2177
2178 public void visitSwitchExpression(JCSwitchExpression tree) {
2179 handleSwitch(tree, tree.selector, tree.cases);
2180 }
2181
2182 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
2183 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2184 pendingExits = new ListBuffer<>();
2185 int nextadrPrev = nextadr;
2186 scanExpr(selector);
2187 final Bits initsSwitch = new Bits(inits);
2188 final Bits uninitsSwitch = new Bits(uninits);
2189 boolean hasDefault = false;
2190 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
2191 inits.assign(initsSwitch);
2192 uninits.assign(uninits.andSet(uninitsSwitch));
2193 JCCase c = l.head;
2194 if (c.pats.isEmpty()) {
2195 hasDefault = true;
2196 } else {
2197 for (JCExpression pat : c.pats) {
2198 scanExpr(pat);
2199 }
2200 }
2201 if (hasDefault) {
2202 inits.assign(initsSwitch);
2203 uninits.assign(uninits.andSet(uninitsSwitch));
2204 }
2205 scan(c.stats);
2206 if (c.completesNormally && c.caseKind == JCCase.RULE) {
2207 scanSyntheticBreak(make, tree);
2208 }
2209 addVars(c.stats, initsSwitch, uninitsSwitch);
2210 if (!hasDefault) {
2211 inits.assign(initsSwitch);
2212 uninits.assign(uninits.andSet(uninitsSwitch));
2213 }
2214 // Warn about fall-through if lint switch fallthrough enabled.
2215 }
2216 if (!hasDefault) {
2217 inits.andSet(initsSwitch);
2218 }
2219 resolveBreaks(tree, prevPendingExits);
2220 nextadr = nextadrPrev;
2221 }
2222 // where
2223 /** Add any variables defined in stats to inits and uninits. */
2224 private void addVars(List<JCStatement> stats, final Bits inits,
2225 final Bits uninits) {
2226 for (;stats.nonEmpty(); stats = stats.tail) {
2227 JCTree stat = stats.head;
2228 if (stat.hasTag(VARDEF)) {
2364 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
2365 inits.assign(initsWhenTrue);
2366 uninits.assign(uninitsWhenTrue);
2367 scan(tree.thenpart);
2368 if (tree.elsepart != null) {
2369 final Bits initsAfterThen = new Bits(inits);
2370 final Bits uninitsAfterThen = new Bits(uninits);
2371 inits.assign(initsBeforeElse);
2372 uninits.assign(uninitsBeforeElse);
2373 scan(tree.elsepart);
2374 inits.andSet(initsAfterThen);
2375 uninits.andSet(uninitsAfterThen);
2376 } else {
2377 inits.andSet(initsBeforeElse);
2378 uninits.andSet(uninitsBeforeElse);
2379 }
2380 }
2381
2382 @Override
2383 public void visitBreak(JCBreak tree) {
2384 if (tree.isValueBreak())
2385 scan(tree.value);
2386 recordExit(new AssignPendingExit(tree, inits, uninits));
2387 }
2388
2389 @Override
2390 public void visitContinue(JCContinue tree) {
2391 recordExit(new AssignPendingExit(tree, inits, uninits));
2392 }
2393
2394 @Override
2395 public void visitReturn(JCReturn tree) {
2396 scanExpr(tree.expr);
2397 recordExit(new AssignPendingExit(tree, inits, uninits));
2398 }
2399
2400 public void visitThrow(JCThrow tree) {
2401 scanExpr(tree.expr);
2402 markDead();
2403 }
2404
2405 public void visitApply(JCMethodInvocation tree) {
2544
2545 void referenced(Symbol sym) {
2546 unrefdResources.remove(sym);
2547 }
2548
2549 public void visitAnnotatedType(JCAnnotatedType tree) {
2550 // annotations don't get scanned
2551 tree.underlyingType.accept(this);
2552 }
2553
2554 public void visitModuleDef(JCModuleDecl tree) {
2555 // Do nothing for modules
2556 }
2557
2558 /**************************************************************************
2559 * main method
2560 *************************************************************************/
2561
2562 /** Perform definite assignment/unassignment analysis on a tree.
2563 */
2564 public void analyzeTree(Env<?> env, TreeMaker make) {
2565 analyzeTree(env, env.tree, make);
2566 }
2567
2568 public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) {
2569 try {
2570 startPos = tree.pos().getStartPosition();
2571
2572 if (vardecls == null)
2573 vardecls = new JCVariableDecl[32];
2574 else
2575 for (int i=0; i<vardecls.length; i++)
2576 vardecls[i] = null;
2577 firstadr = 0;
2578 nextadr = 0;
2579 Flow.this.make = make;
2580 pendingExits = new ListBuffer<>();
2581 this.classDef = null;
2582 unrefdResources = WriteableScope.create(env.enclClass.sym);
2583 scan(tree);
2584 } finally {
2585 // note that recursive invocations of this method fail hard
2586 startPos = -1;
2587 resetBits(inits, uninits, uninitsTry, initsWhenTrue,
2588 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
2589 if (vardecls != null) {
2590 for (int i=0; i<vardecls.length; i++)
2591 vardecls[i] = null;
2592 }
2593 firstadr = 0;
2594 nextadr = 0;
2595 Flow.this.make = null;
2596 pendingExits = null;
2597 this.classDef = null;
2598 unrefdResources = null;
2599 }
2600 }
2601 }
2602
2603 /**
2604 * This pass implements the last step of the dataflow analysis, namely
2605 * the effectively-final analysis check. This checks that every local variable
2606 * reference from a lambda body/local inner class is either final or effectively final.
2607 * Additional this also checks that every variable that is used as an operand to
2608 * try-with-resources is final or effectively final.
2609 * As effectively final variables are marked as such during DA/DU, this pass must run after
2610 * AssignAnalyzer.
2611 */
2612 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
2613
2614 JCTree currentTree; //local class or lambda
2615
2726 case PREINC: case POSTINC:
2727 case PREDEC: case POSTDEC:
2728 scan(tree.arg);
2729 letInit(tree.arg);
2730 break;
2731 default:
2732 scan(tree.arg);
2733 }
2734 }
2735
2736 public void visitTry(JCTry tree) {
2737 for (JCTree resource : tree.resources) {
2738 if (!resource.hasTag(VARDEF)) {
2739 Symbol var = TreeInfo.symbol(resource);
2740 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) {
2741 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var));
2742 }
2743 }
2744 }
2745 super.visitTry(tree);
2746 }
2747
2748 @Override
2749 public void visitBreak(JCBreak tree) {
2750 if (tree.isValueBreak())
2751 scan(tree.value);
2752 }
2753
2754 public void visitModuleDef(JCModuleDecl tree) {
2755 // Do nothing for modules
2756 }
2757
2758 /**************************************************************************
2759 * main method
2760 *************************************************************************/
2761
2762 /** Perform definite assignment/unassignment analysis on a tree.
2763 */
2764 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
2765 analyzeTree(env, env.tree, make);
2766 }
2767 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
2768 try {
2769 attrEnv = env;
2770 Flow.this.make = make;
2771 pendingExits = new ListBuffer<>();
|