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;
51 import static com.sun.tools.javac.code.TypeTag.VOID;
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.CaseTree.CaseKind;
35 import com.sun.source.tree.LambdaExpressionTree.BodyKind;
36 import com.sun.tools.javac.code.*;
37 import com.sun.tools.javac.code.Scope.WriteableScope;
38 import com.sun.tools.javac.code.Source.Feature;
39 import com.sun.tools.javac.resources.CompilerProperties.Errors;
40 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
41 import com.sun.tools.javac.tree.*;
42 import com.sun.tools.javac.util.*;
43 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
44 import com.sun.tools.javac.util.JCDiagnostic.Error;
45 import com.sun.tools.javac.util.JCDiagnostic.Warning;
46
47 import com.sun.tools.javac.code.Symbol.*;
48 import com.sun.tools.javac.tree.JCTree.*;
49
50 import static com.sun.tools.javac.code.Flags.*;
51 import static com.sun.tools.javac.code.Flags.BLOCK;
52 import static com.sun.tools.javac.code.Kinds.Kind.*;
53 import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
54 import static com.sun.tools.javac.code.TypeTag.VOID;
197 private final Log log;
198 private final Symtab syms;
199 private final Types types;
200 private final Check chk;
201 private TreeMaker make;
202 private final Resolve rs;
203 private final JCDiagnostic.Factory diags;
204 private Env<AttrContext> attrEnv;
205 private Lint lint;
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 /**
260 * Definite assignment scan mode
261 */
262 enum FlowKind {
263 /**
264 * This is the normal DA/DU analysis mode
265 */
266 NORMAL("var.might.already.be.assigned", false),
267 /**
268 * This is the speculative DA/DU analysis mode used to speculatively
269 * derive assertions within loop bodies
270 */
382 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE);
383 }
384
385 /** Resolve all breaks of this statement. */
386 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) {
387 return resolveJump(tree, oldPendingExits, JumpKind.BREAK);
388 }
389
390 @Override
391 public void scan(JCTree tree) {
392 if (tree != null && (
393 tree.type == null ||
394 tree.type != Type.stuckType)) {
395 super.scan(tree);
396 }
397 }
398
399 public void visitPackageDef(JCPackageDecl tree) {
400 // Do nothing for PackageDecl
401 }
402
403 protected void scanSyntheticBreak(TreeMaker make, JCTree swtch) {
404 JCBreak brk = make.at(Position.NOPOS).Break(null);
405 brk.target = swtch;
406 scan(brk);
407 }
408 }
409
410 /**
411 * This pass implements the first step of the dataflow analysis, namely
412 * the liveness analysis check. This checks that every statement is reachable.
413 * The output of this analysis pass are used by other analyzers. This analyzer
414 * sets the 'finallyCanCompleteNormally' field in the JCTry class.
415 */
416 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
417
418 /** A flag that indicates whether the last statement could
419 * complete normally.
420 */
421 private boolean alive;
422
423 @Override
424 void markDead() {
425 alive = false;
426 }
427
588 alive |= resolveContinues(tree);
589 resolveBreaks(tree, prevPendingExits);
590 alive = true;
591 }
592
593 public void visitLabelled(JCLabeledStatement tree) {
594 ListBuffer<PendingExit> prevPendingExits = pendingExits;
595 pendingExits = new ListBuffer<>();
596 scanStat(tree.body);
597 alive |= resolveBreaks(tree, prevPendingExits);
598 }
599
600 public void visitSwitch(JCSwitch tree) {
601 ListBuffer<PendingExit> prevPendingExits = pendingExits;
602 pendingExits = new ListBuffer<>();
603 scan(tree.selector);
604 boolean hasDefault = false;
605 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
606 alive = true;
607 JCCase c = l.head;
608 if (c.pats.isEmpty())
609 hasDefault = true;
610 else {
611 for (JCExpression pat : c.pats) {
612 scan(pat);
613 }
614 }
615 scanStats(c.stats);
616 c.completesNormally = alive;
617 if (alive && c.caseKind == JCCase.RULE) {
618 scanSyntheticBreak(make, tree);
619 alive = false;
620 }
621 // Warn about fall-through if lint switch fallthrough enabled.
622 if (alive &&
623 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) &&
624 c.stats.nonEmpty() && l.tail.nonEmpty())
625 log.warning(Lint.LintCategory.FALLTHROUGH,
626 l.tail.head.pos(),
627 Warnings.PossibleFallThroughIntoCase);
628 }
629 if (!hasDefault) {
630 alive = true;
631 }
632 alive |= resolveBreaks(tree, prevPendingExits);
633 }
634
635 @Override
636 public void visitSwitchExpression(JCSwitchExpression tree) {
637 ListBuffer<PendingExit> prevPendingExits = pendingExits;
638 pendingExits = new ListBuffer<>();
639 scan(tree.selector);
640 Set<Object> constants = null;
641 if ((tree.selector.type.tsym.flags() & ENUM) != 0) {
642 constants = new HashSet<>();
643 for (Symbol s : tree.selector.type.tsym.members().getSymbols(s -> (s.flags() & ENUM) != 0)) {
644 constants.add(s.name);
645 }
646 }
647 boolean hasDefault = false;
648 boolean prevAlive = alive;
649 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
650 alive = true;
651 JCCase c = l.head;
652 if (c.pats.isEmpty())
653 hasDefault = true;
654 else {
655 for (JCExpression pat : c.pats) {
656 scan(pat);
657 if (constants != null) {
658 if (pat.hasTag(IDENT))
659 constants.remove(((JCIdent) pat).name);
660 if (pat.type != null)
661 constants.remove(pat.type.constValue());
662 }
663 }
664 }
665 scanStats(c.stats);
666 c.completesNormally = alive;
667 }
668 if ((constants == null || !constants.isEmpty()) && !hasDefault) {
669 log.error(tree, Errors.NotExhaustive);
670 }
671 alive = prevAlive;
672 alive |= resolveBreaks(tree, prevPendingExits);
673 }
674
675 public void visitTry(JCTry tree) {
676 ListBuffer<PendingExit> prevPendingExits = pendingExits;
677 pendingExits = new ListBuffer<>();
678 for (JCTree resource : tree.resources) {
679 if (resource instanceof JCVariableDecl) {
680 JCVariableDecl vdecl = (JCVariableDecl) resource;
681 visitVarDef(vdecl);
682 } else if (resource instanceof JCExpression) {
683 scan((JCExpression) resource);
684 } else {
685 throw new AssertionError(tree); // parser error
686 }
687 }
688
689 scanStat(tree.body);
690 boolean aliveEnd = alive;
691
692 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
693 alive = true;
694 JCVariableDecl param = l.head.param;
720 pendingExits = prevPendingExits;
721 while (exits.nonEmpty()) pendingExits.append(exits.next());
722 }
723 }
724
725 @Override
726 public void visitIf(JCIf tree) {
727 scan(tree.cond);
728 scanStat(tree.thenpart);
729 if (tree.elsepart != null) {
730 boolean aliveAfterThen = alive;
731 alive = true;
732 scanStat(tree.elsepart);
733 alive = alive | aliveAfterThen;
734 } else {
735 alive = true;
736 }
737 }
738
739 public void visitBreak(JCBreak tree) {
740 if (tree.isValueBreak())
741 scan(tree.value);
742 recordExit(new PendingExit(tree));
743 }
744
745 public void visitContinue(JCContinue tree) {
746 recordExit(new PendingExit(tree));
747 }
748
749 public void visitReturn(JCReturn tree) {
750 scan(tree.expr);
751 recordExit(new PendingExit(tree));
752 }
753
754 public void visitThrow(JCThrow tree) {
755 scan(tree.expr);
756 markDead();
757 }
758
759 public void visitApply(JCMethodInvocation tree) {
760 scan(tree.meth);
761 scan(tree.args);
1082 }
1083
1084 public void visitForeachLoop(JCEnhancedForLoop tree) {
1085 visitVarDef(tree.var);
1086 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1087 scan(tree.expr);
1088 pendingExits = new ListBuffer<>();
1089 scan(tree.body);
1090 resolveContinues(tree);
1091 resolveBreaks(tree, prevPendingExits);
1092 }
1093
1094 public void visitLabelled(JCLabeledStatement tree) {
1095 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1096 pendingExits = new ListBuffer<>();
1097 scan(tree.body);
1098 resolveBreaks(tree, prevPendingExits);
1099 }
1100
1101 public void visitSwitch(JCSwitch tree) {
1102 handleSwitch(tree, tree.selector, tree.cases);
1103 }
1104
1105 @Override
1106 public void visitSwitchExpression(JCSwitchExpression tree) {
1107 handleSwitch(tree, tree.selector, tree.cases);
1108 }
1109
1110 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
1111 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1112 pendingExits = new ListBuffer<>();
1113 scan(selector);
1114 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
1115 JCCase c = l.head;
1116 scan(c.pats);
1117 scan(c.stats);
1118 }
1119 resolveBreaks(tree, prevPendingExits);
1120 }
1121
1122 public void visitTry(JCTry tree) {
1123 List<Type> caughtPrev = caught;
1124 List<Type> thrownPrev = thrown;
1125 thrown = List.nil();
1126 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
1127 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
1128 ((JCTypeUnion)l.head.param.vartype).alternatives :
1129 List.of(l.head.param.vartype);
1130 for (JCExpression ct : subClauses) {
1131 caught = chk.incl(ct.type, caught);
1132 }
1133 }
1134
1135 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits;
1136 pendingExits = new ListBuffer<>();
1240 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an
1241 // unchecked exception, the result list would not be empty, as the augmented
1242 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked
1243 // exception, that would have been covered in the branch above
1244 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() &&
1245 !isExceptionOrThrowable(exc)) {
1246 Warning key = catchableThrownTypes.length() == 1 ?
1247 Warnings.UnreachableCatch(catchableThrownTypes) :
1248 Warnings.UnreachableCatch1(catchableThrownTypes);
1249 log.warning(pos, key);
1250 }
1251 }
1252 }
1253 //where
1254 private boolean isExceptionOrThrowable(Type exc) {
1255 return exc.tsym == syms.throwableType.tsym ||
1256 exc.tsym == syms.exceptionType.tsym;
1257 }
1258
1259 public void visitBreak(JCBreak tree) {
1260 if (tree.isValueBreak())
1261 scan(tree.value);
1262 recordExit(new FlowPendingExit(tree, null));
1263 }
1264
1265 public void visitContinue(JCContinue tree) {
1266 recordExit(new FlowPendingExit(tree, null));
1267 }
1268
1269 public void visitReturn(JCReturn tree) {
1270 scan(tree.expr);
1271 recordExit(new FlowPendingExit(tree, null));
1272 }
1273
1274 public void visitThrow(JCThrow tree) {
1275 scan(tree.expr);
1276 Symbol sym = TreeInfo.symbol(tree.expr);
1277 if (sym != null &&
1278 sym.kind == VAR &&
1279 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 &&
1280 preciseRethrowTypes.get(sym) != null) {
1281 for (Type t : preciseRethrowTypes.get(sym)) {
2156 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1)
2157 break;
2158 uninits.assign(uninitsEntry.andSet(uninits));
2159 flowKind = FlowKind.SPECULATIVE_LOOP;
2160 } while (true);
2161 flowKind = prevFlowKind;
2162 inits.assign(initsStart);
2163 uninits.assign(uninitsStart.andSet(uninits));
2164 resolveBreaks(tree, prevPendingExits);
2165 nextadr = nextadrPrev;
2166 }
2167
2168 public void visitLabelled(JCLabeledStatement tree) {
2169 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2170 pendingExits = new ListBuffer<>();
2171 scan(tree.body);
2172 resolveBreaks(tree, prevPendingExits);
2173 }
2174
2175 public void visitSwitch(JCSwitch tree) {
2176 handleSwitch(tree, tree.selector, tree.cases);
2177 }
2178
2179 public void visitSwitchExpression(JCSwitchExpression tree) {
2180 handleSwitch(tree, tree.selector, tree.cases);
2181 }
2182
2183 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) {
2184 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits;
2185 pendingExits = new ListBuffer<>();
2186 int nextadrPrev = nextadr;
2187 scanExpr(selector);
2188 final Bits initsSwitch = new Bits(inits);
2189 final Bits uninitsSwitch = new Bits(uninits);
2190 boolean hasDefault = false;
2191 for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
2192 inits.assign(initsSwitch);
2193 uninits.assign(uninits.andSet(uninitsSwitch));
2194 JCCase c = l.head;
2195 if (c.pats.isEmpty()) {
2196 hasDefault = true;
2197 } else {
2198 for (JCExpression pat : c.pats) {
2199 scanExpr(pat);
2200 }
2201 }
2202 if (hasDefault) {
2203 inits.assign(initsSwitch);
2204 uninits.assign(uninits.andSet(uninitsSwitch));
2205 }
2206 scan(c.stats);
2207 if (c.completesNormally && c.caseKind == JCCase.RULE) {
2208 scanSyntheticBreak(make, tree);
2209 }
2210 addVars(c.stats, initsSwitch, uninitsSwitch);
2211 if (!hasDefault) {
2212 inits.assign(initsSwitch);
2213 uninits.assign(uninits.andSet(uninitsSwitch));
2214 }
2215 // Warn about fall-through if lint switch fallthrough enabled.
2216 }
2217 if (!hasDefault) {
2218 inits.andSet(initsSwitch);
2219 }
2220 resolveBreaks(tree, prevPendingExits);
2221 nextadr = nextadrPrev;
2222 }
2223 // where
2224 /** Add any variables defined in stats to inits and uninits. */
2225 private void addVars(List<JCStatement> stats, final Bits inits,
2226 final Bits uninits) {
2227 for (;stats.nonEmpty(); stats = stats.tail) {
2228 JCTree stat = stats.head;
2229 if (stat.hasTag(VARDEF)) {
2365 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse);
2366 inits.assign(initsWhenTrue);
2367 uninits.assign(uninitsWhenTrue);
2368 scan(tree.thenpart);
2369 if (tree.elsepart != null) {
2370 final Bits initsAfterThen = new Bits(inits);
2371 final Bits uninitsAfterThen = new Bits(uninits);
2372 inits.assign(initsBeforeElse);
2373 uninits.assign(uninitsBeforeElse);
2374 scan(tree.elsepart);
2375 inits.andSet(initsAfterThen);
2376 uninits.andSet(uninitsAfterThen);
2377 } else {
2378 inits.andSet(initsBeforeElse);
2379 uninits.andSet(uninitsBeforeElse);
2380 }
2381 }
2382
2383 @Override
2384 public void visitBreak(JCBreak tree) {
2385 if (tree.isValueBreak())
2386 scan(tree.value);
2387 recordExit(new AssignPendingExit(tree, inits, uninits));
2388 }
2389
2390 @Override
2391 public void visitContinue(JCContinue tree) {
2392 recordExit(new AssignPendingExit(tree, inits, uninits));
2393 }
2394
2395 @Override
2396 public void visitReturn(JCReturn tree) {
2397 scanExpr(tree.expr);
2398 recordExit(new AssignPendingExit(tree, inits, uninits));
2399 }
2400
2401 public void visitThrow(JCThrow tree) {
2402 scanExpr(tree.expr);
2403 markDead();
2404 }
2405
2406 public void visitApply(JCMethodInvocation tree) {
2545
2546 void referenced(Symbol sym) {
2547 unrefdResources.remove(sym);
2548 }
2549
2550 public void visitAnnotatedType(JCAnnotatedType tree) {
2551 // annotations don't get scanned
2552 tree.underlyingType.accept(this);
2553 }
2554
2555 public void visitModuleDef(JCModuleDecl tree) {
2556 // Do nothing for modules
2557 }
2558
2559 /**************************************************************************
2560 * main method
2561 *************************************************************************/
2562
2563 /** Perform definite assignment/unassignment analysis on a tree.
2564 */
2565 public void analyzeTree(Env<?> env, TreeMaker make) {
2566 analyzeTree(env, env.tree, make);
2567 }
2568
2569 public void analyzeTree(Env<?> env, JCTree tree, TreeMaker make) {
2570 try {
2571 startPos = tree.pos().getStartPosition();
2572
2573 if (vardecls == null)
2574 vardecls = new JCVariableDecl[32];
2575 else
2576 for (int i=0; i<vardecls.length; i++)
2577 vardecls[i] = null;
2578 firstadr = 0;
2579 nextadr = 0;
2580 Flow.this.make = make;
2581 pendingExits = new ListBuffer<>();
2582 this.classDef = null;
2583 unrefdResources = WriteableScope.create(env.enclClass.sym);
2584 scan(tree);
2585 } finally {
2586 // note that recursive invocations of this method fail hard
2587 startPos = -1;
2588 resetBits(inits, uninits, uninitsTry, initsWhenTrue,
2589 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse);
2590 if (vardecls != null) {
2591 for (int i=0; i<vardecls.length; i++)
2592 vardecls[i] = null;
2593 }
2594 firstadr = 0;
2595 nextadr = 0;
2596 Flow.this.make = null;
2597 pendingExits = null;
2598 this.classDef = null;
2599 unrefdResources = null;
2600 }
2601 }
2602 }
2603
2604 /**
2605 * This pass implements the last step of the dataflow analysis, namely
2606 * the effectively-final analysis check. This checks that every local variable
2607 * reference from a lambda body/local inner class is either final or effectively final.
2608 * Additional this also checks that every variable that is used as an operand to
2609 * try-with-resources is final or effectively final.
2610 * As effectively final variables are marked as such during DA/DU, this pass must run after
2611 * AssignAnalyzer.
2612 */
2613 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> {
2614
2615 JCTree currentTree; //local class or lambda
2616
2727 case PREINC: case POSTINC:
2728 case PREDEC: case POSTDEC:
2729 scan(tree.arg);
2730 letInit(tree.arg);
2731 break;
2732 default:
2733 scan(tree.arg);
2734 }
2735 }
2736
2737 public void visitTry(JCTry tree) {
2738 for (JCTree resource : tree.resources) {
2739 if (!resource.hasTag(VARDEF)) {
2740 Symbol var = TreeInfo.symbol(resource);
2741 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) {
2742 log.error(resource.pos(), Errors.TryWithResourcesExprEffectivelyFinalVar(var));
2743 }
2744 }
2745 }
2746 super.visitTry(tree);
2747 }
2748
2749 @Override
2750 public void visitBreak(JCBreak tree) {
2751 if (tree.isValueBreak())
2752 scan(tree.value);
2753 }
2754
2755 public void visitModuleDef(JCModuleDecl tree) {
2756 // Do nothing for modules
2757 }
2758
2759 /**************************************************************************
2760 * main method
2761 *************************************************************************/
2762
2763 /** Perform definite assignment/unassignment analysis on a tree.
2764 */
2765 public void analyzeTree(Env<AttrContext> env, TreeMaker make) {
2766 analyzeTree(env, env.tree, make);
2767 }
2768 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) {
2769 try {
2770 attrEnv = env;
2771 Flow.this.make = make;
2772 pendingExits = new ListBuffer<>();
|