Flow.java revision 3722:aacff1ac75a2
1219019Sgabor/* 2219019Sgabor * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 3219019Sgabor * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4219019Sgabor * 5219019Sgabor * This code is free software; you can redistribute it and/or modify it 6219019Sgabor * under the terms of the GNU General Public License version 2 only, as 7219019Sgabor * published by the Free Software Foundation. Oracle designates this 8219019Sgabor * particular file as subject to the "Classpath" exception as provided 9219019Sgabor * by Oracle in the LICENSE file that accompanied this code. 10219019Sgabor * 11219019Sgabor * This code is distributed in the hope that it will be useful, but WITHOUT 12219019Sgabor * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13219019Sgabor * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14219019Sgabor * version 2 for more details (a copy is included in the LICENSE file that 15219019Sgabor * accompanied this code). 16219019Sgabor * 17219019Sgabor * You should have received a copy of the GNU General Public License version 18219019Sgabor * 2 along with this work; if not, write to the Free Software Foundation, 19219019Sgabor * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20219019Sgabor * 21219019Sgabor * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22219019Sgabor * or visit www.oracle.com if you need additional information or have any 23219019Sgabor * questions. 24219019Sgabor */ 25219019Sgabor 26219019Sgabor//todo: one might eliminate uninits.andSets when monotonic 27219019Sgabor 28219019Sgaborpackage com.sun.tools.javac.comp; 29219019Sgabor 30219019Sgaborimport java.util.HashMap; 31219019Sgabor 32219019Sgaborimport com.sun.source.tree.LambdaExpressionTree.BodyKind; 33219019Sgaborimport com.sun.tools.javac.code.*; 34219019Sgaborimport com.sun.tools.javac.code.Scope.WriteableScope; 35219019Sgaborimport com.sun.tools.javac.tree.*; 36219019Sgaborimport com.sun.tools.javac.util.*; 37219019Sgaborimport com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 38219019Sgabor 39219019Sgaborimport com.sun.tools.javac.code.Symbol.*; 40219019Sgaborimport com.sun.tools.javac.tree.JCTree.*; 41219019Sgabor 42219019Sgaborimport static com.sun.tools.javac.code.Flags.*; 43219019Sgaborimport static com.sun.tools.javac.code.Flags.BLOCK; 44219019Sgaborimport static com.sun.tools.javac.code.Kinds.Kind.*; 45219019Sgaborimport static com.sun.tools.javac.code.TypeTag.BOOLEAN; 46219019Sgaborimport static com.sun.tools.javac.code.TypeTag.VOID; 47219019Sgaborimport static com.sun.tools.javac.tree.JCTree.Tag.*; 48219019Sgabor 49219019Sgabor/** This pass implements dataflow analysis for Java programs though 50219019Sgabor * different AST visitor steps. Liveness analysis (see AliveAnalyzer) checks that 51219019Sgabor * every statement is reachable. Exception analysis (see FlowAnalyzer) ensures that 52219019Sgabor * every checked exception that is thrown is declared or caught. Definite assignment analysis 53219019Sgabor * (see AssignAnalyzer) ensures that each variable is assigned when used. Definite 54219019Sgabor * unassignment analysis (see AssignAnalyzer) in ensures that no final variable 55 * is assigned more than once. Finally, local variable capture analysis (see CaptureAnalyzer) 56 * determines that local variables accessed within the scope of an inner class/lambda 57 * are either final or effectively-final. 58 * 59 * <p>The JLS has a number of problems in the 60 * specification of these flow analysis problems. This implementation 61 * attempts to address those issues. 62 * 63 * <p>First, there is no accommodation for a finally clause that cannot 64 * complete normally. For liveness analysis, an intervening finally 65 * clause can cause a break, continue, or return not to reach its 66 * target. For exception analysis, an intervening finally clause can 67 * cause any exception to be "caught". For DA/DU analysis, the finally 68 * clause can prevent a transfer of control from propagating DA/DU 69 * state to the target. In addition, code in the finally clause can 70 * affect the DA/DU status of variables. 71 * 72 * <p>For try statements, we introduce the idea of a variable being 73 * definitely unassigned "everywhere" in a block. A variable V is 74 * "unassigned everywhere" in a block iff it is unassigned at the 75 * beginning of the block and there is no reachable assignment to V 76 * in the block. An assignment V=e is reachable iff V is not DA 77 * after e. Then we can say that V is DU at the beginning of the 78 * catch block iff V is DU everywhere in the try block. Similarly, V 79 * is DU at the beginning of the finally block iff V is DU everywhere 80 * in the try block and in every catch block. Specifically, the 81 * following bullet is added to 16.2.2 82 * <pre> 83 * V is <em>unassigned everywhere</em> in a block if it is 84 * unassigned before the block and there is no reachable 85 * assignment to V within the block. 86 * </pre> 87 * <p>In 16.2.15, the third bullet (and all of its sub-bullets) for all 88 * try blocks is changed to 89 * <pre> 90 * V is definitely unassigned before a catch block iff V is 91 * definitely unassigned everywhere in the try block. 92 * </pre> 93 * <p>The last bullet (and all of its sub-bullets) for try blocks that 94 * have a finally block is changed to 95 * <pre> 96 * V is definitely unassigned before the finally block iff 97 * V is definitely unassigned everywhere in the try block 98 * and everywhere in each catch block of the try statement. 99 * </pre> 100 * <p>In addition, 101 * <pre> 102 * V is definitely assigned at the end of a constructor iff 103 * V is definitely assigned after the block that is the body 104 * of the constructor and V is definitely assigned at every 105 * return that can return from the constructor. 106 * </pre> 107 * <p>In addition, each continue statement with the loop as its target 108 * is treated as a jump to the end of the loop body, and "intervening" 109 * finally clauses are treated as follows: V is DA "due to the 110 * continue" iff V is DA before the continue statement or V is DA at 111 * the end of any intervening finally block. V is DU "due to the 112 * continue" iff any intervening finally cannot complete normally or V 113 * is DU at the end of every intervening finally block. This "due to 114 * the continue" concept is then used in the spec for the loops. 115 * 116 * <p>Similarly, break statements must consider intervening finally 117 * blocks. For liveness analysis, a break statement for which any 118 * intervening finally cannot complete normally is not considered to 119 * cause the target statement to be able to complete normally. Then 120 * we say V is DA "due to the break" iff V is DA before the break or 121 * V is DA at the end of any intervening finally block. V is DU "due 122 * to the break" iff any intervening finally cannot complete normally 123 * or V is DU at the break and at the end of every intervening 124 * finally block. (I suspect this latter condition can be 125 * simplified.) This "due to the break" is then used in the spec for 126 * all statements that can be "broken". 127 * 128 * <p>The return statement is treated similarly. V is DA "due to a 129 * return statement" iff V is DA before the return statement or V is 130 * DA at the end of any intervening finally block. Note that we 131 * don't have to worry about the return expression because this 132 * concept is only used for construcrors. 133 * 134 * <p>There is no spec in the JLS for when a variable is definitely 135 * assigned at the end of a constructor, which is needed for final 136 * fields (8.3.1.2). We implement the rule that V is DA at the end 137 * of the constructor iff it is DA and the end of the body of the 138 * constructor and V is DA "due to" every return of the constructor. 139 * 140 * <p>Intervening finally blocks similarly affect exception analysis. An 141 * intervening finally that cannot complete normally allows us to ignore 142 * an otherwise uncaught exception. 143 * 144 * <p>To implement the semantics of intervening finally clauses, all 145 * nonlocal transfers (break, continue, return, throw, method call that 146 * can throw a checked exception, and a constructor invocation that can 147 * thrown a checked exception) are recorded in a queue, and removed 148 * from the queue when we complete processing the target of the 149 * nonlocal transfer. This allows us to modify the queue in accordance 150 * with the above rules when we encounter a finally clause. The only 151 * exception to this [no pun intended] is that checked exceptions that 152 * are known to be caught or declared to be caught in the enclosing 153 * method are not recorded in the queue, but instead are recorded in a 154 * global variable "{@code Set<Type> thrown}" that records the type of all 155 * exceptions that can be thrown. 156 * 157 * <p>Other minor issues the treatment of members of other classes 158 * (always considered DA except that within an anonymous class 159 * constructor, where DA status from the enclosing scope is 160 * preserved), treatment of the case expression (V is DA before the 161 * case expression iff V is DA after the switch expression), 162 * treatment of variables declared in a switch block (the implied 163 * DA/DU status after the switch expression is DU and not DA for 164 * variables defined in a switch block), the treatment of boolean ?: 165 * expressions (The JLS rules only handle b and c non-boolean; the 166 * new rule is that if b and c are boolean valued, then V is 167 * (un)assigned after a?b:c when true/false iff V is (un)assigned 168 * after b when true/false and V is (un)assigned after c when 169 * true/false). 170 * 171 * <p>There is the remaining question of what syntactic forms constitute a 172 * reference to a variable. It is conventional to allow this.x on the 173 * left-hand-side to initialize a final instance field named x, yet 174 * this.x isn't considered a "use" when appearing on a right-hand-side 175 * in most implementations. Should parentheses affect what is 176 * considered a variable reference? The simplest rule would be to 177 * allow unqualified forms only, parentheses optional, and phase out 178 * support for assigning to a final field via this.x. 179 * 180 * <p><b>This is NOT part of any supported API. 181 * If you write code that depends on this, you do so at your own risk. 182 * This code and its internal interfaces are subject to change or 183 * deletion without notice.</b> 184 */ 185public class Flow { 186 protected static final Context.Key<Flow> flowKey = new Context.Key<>(); 187 188 private final Names names; 189 private final Log log; 190 private final Symtab syms; 191 private final Types types; 192 private final Check chk; 193 private TreeMaker make; 194 private final Resolve rs; 195 private final JCDiagnostic.Factory diags; 196 private Env<AttrContext> attrEnv; 197 private Lint lint; 198 private final boolean allowImprovedRethrowAnalysis; 199 private final boolean allowImprovedCatchAnalysis; 200 private final boolean allowEffectivelyFinalInInnerClasses; 201 private final boolean enforceThisDotInit; 202 203 public static Flow instance(Context context) { 204 Flow instance = context.get(flowKey); 205 if (instance == null) 206 instance = new Flow(context); 207 return instance; 208 } 209 210 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 211 new AliveAnalyzer().analyzeTree(env, make); 212 new AssignAnalyzer().analyzeTree(env); 213 new FlowAnalyzer().analyzeTree(env, make); 214 new CaptureAnalyzer().analyzeTree(env, make); 215 } 216 217 public void analyzeLambda(Env<AttrContext> env, JCLambda that, TreeMaker make, boolean speculative) { 218 Log.DiagnosticHandler diagHandler = null; 219 //we need to disable diagnostics temporarily; the problem is that if 220 //a lambda expression contains e.g. an unreachable statement, an error 221 //message will be reported and will cause compilation to skip the flow analyis 222 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 223 //related errors, which will allow for more errors to be detected 224 if (!speculative) { 225 diagHandler = new Log.DiscardDiagnosticHandler(log); 226 } 227 try { 228 new LambdaAliveAnalyzer().analyzeTree(env, that, make); 229 } finally { 230 if (!speculative) { 231 log.popDiagnosticHandler(diagHandler); 232 } 233 } 234 } 235 236 public List<Type> analyzeLambdaThrownTypes(final Env<AttrContext> env, 237 JCLambda that, TreeMaker make) { 238 //we need to disable diagnostics temporarily; the problem is that if 239 //a lambda expression contains e.g. an unreachable statement, an error 240 //message will be reported and will cause compilation to skip the flow analyis 241 //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis 242 //related errors, which will allow for more errors to be detected 243 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 244 try { 245 new LambdaAssignAnalyzer(env).analyzeTree(env, that); 246 LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer(); 247 flowAnalyzer.analyzeTree(env, that, make); 248 return flowAnalyzer.inferredThrownTypes; 249 } finally { 250 log.popDiagnosticHandler(diagHandler); 251 } 252 } 253 254 /** 255 * Definite assignment scan mode 256 */ 257 enum FlowKind { 258 /** 259 * This is the normal DA/DU analysis mode 260 */ 261 NORMAL("var.might.already.be.assigned", false), 262 /** 263 * This is the speculative DA/DU analysis mode used to speculatively 264 * derive assertions within loop bodies 265 */ 266 SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); 267 268 final String errKey; 269 final boolean isFinal; 270 271 FlowKind(String errKey, boolean isFinal) { 272 this.errKey = errKey; 273 this.isFinal = isFinal; 274 } 275 276 boolean isFinal() { 277 return isFinal; 278 } 279 } 280 281 protected Flow(Context context) { 282 context.put(flowKey, this); 283 names = Names.instance(context); 284 log = Log.instance(context); 285 syms = Symtab.instance(context); 286 types = Types.instance(context); 287 chk = Check.instance(context); 288 lint = Lint.instance(context); 289 rs = Resolve.instance(context); 290 diags = JCDiagnostic.Factory.instance(context); 291 Source source = Source.instance(context); 292 allowImprovedRethrowAnalysis = source.allowImprovedRethrowAnalysis(); 293 allowImprovedCatchAnalysis = source.allowImprovedCatchAnalysis(); 294 allowEffectivelyFinalInInnerClasses = source.allowEffectivelyFinalInInnerClasses(); 295 enforceThisDotInit = source.enforceThisDotInit(); 296 } 297 298 /** 299 * Base visitor class for all visitors implementing dataflow analysis logic. 300 * This class define the shared logic for handling jumps (break/continue statements). 301 */ 302 static abstract class BaseAnalyzer<P extends BaseAnalyzer.PendingExit> extends TreeScanner { 303 304 enum JumpKind { 305 BREAK(JCTree.Tag.BREAK) { 306 @Override 307 JCTree getTarget(JCTree tree) { 308 return ((JCBreak)tree).target; 309 } 310 }, 311 CONTINUE(JCTree.Tag.CONTINUE) { 312 @Override 313 JCTree getTarget(JCTree tree) { 314 return ((JCContinue)tree).target; 315 } 316 }; 317 318 final JCTree.Tag treeTag; 319 320 private JumpKind(Tag treeTag) { 321 this.treeTag = treeTag; 322 } 323 324 abstract JCTree getTarget(JCTree tree); 325 } 326 327 /** The currently pending exits that go from current inner blocks 328 * to an enclosing block, in source order. 329 */ 330 ListBuffer<P> pendingExits; 331 332 /** A pending exit. These are the statements return, break, and 333 * continue. In addition, exception-throwing expressions or 334 * statements are put here when not known to be caught. This 335 * will typically result in an error unless it is within a 336 * try-finally whose finally block cannot complete normally. 337 */ 338 static class PendingExit { 339 JCTree tree; 340 341 PendingExit(JCTree tree) { 342 this.tree = tree; 343 } 344 345 void resolveJump() { 346 //do nothing 347 } 348 } 349 350 abstract void markDead(); 351 352 /** Record an outward transfer of control. */ 353 void recordExit(P pe) { 354 pendingExits.append(pe); 355 markDead(); 356 } 357 358 /** Resolve all jumps of this statement. */ 359 private boolean resolveJump(JCTree tree, 360 ListBuffer<P> oldPendingExits, 361 JumpKind jk) { 362 boolean resolved = false; 363 List<P> exits = pendingExits.toList(); 364 pendingExits = oldPendingExits; 365 for (; exits.nonEmpty(); exits = exits.tail) { 366 P exit = exits.head; 367 if (exit.tree.hasTag(jk.treeTag) && 368 jk.getTarget(exit.tree) == tree) { 369 exit.resolveJump(); 370 resolved = true; 371 } else { 372 pendingExits.append(exit); 373 } 374 } 375 return resolved; 376 } 377 378 /** Resolve all continues of this statement. */ 379 boolean resolveContinues(JCTree tree) { 380 return resolveJump(tree, new ListBuffer<P>(), JumpKind.CONTINUE); 381 } 382 383 /** Resolve all breaks of this statement. */ 384 boolean resolveBreaks(JCTree tree, ListBuffer<P> oldPendingExits) { 385 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); 386 } 387 388 @Override 389 public void scan(JCTree tree) { 390 if (tree != null && ( 391 tree.type == null || 392 tree.type != Type.stuckType)) { 393 super.scan(tree); 394 } 395 } 396 397 public void visitPackageDef(JCPackageDecl tree) { 398 // Do nothing for PackageDecl 399 } 400 } 401 402 /** 403 * This pass implements the first step of the dataflow analysis, namely 404 * the liveness analysis check. This checks that every statement is reachable. 405 * The output of this analysis pass are used by other analyzers. This analyzer 406 * sets the 'finallyCanCompleteNormally' field in the JCTry class. 407 */ 408 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 409 410 /** A flag that indicates whether the last statement could 411 * complete normally. 412 */ 413 private boolean alive; 414 415 @Override 416 void markDead() { 417 alive = false; 418 } 419 420 /************************************************************************* 421 * Visitor methods for statements and definitions 422 *************************************************************************/ 423 424 /** Analyze a definition. 425 */ 426 void scanDef(JCTree tree) { 427 scanStat(tree); 428 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { 429 log.error(tree.pos(), 430 "initializer.must.be.able.to.complete.normally"); 431 } 432 } 433 434 /** Analyze a statement. Check that statement is reachable. 435 */ 436 void scanStat(JCTree tree) { 437 if (!alive && tree != null) { 438 log.error(tree.pos(), "unreachable.stmt"); 439 if (!tree.hasTag(SKIP)) alive = true; 440 } 441 scan(tree); 442 } 443 444 /** Analyze list of statements. 445 */ 446 void scanStats(List<? extends JCStatement> trees) { 447 if (trees != null) 448 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) 449 scanStat(l.head); 450 } 451 452 /* ------------ Visitor methods for various sorts of trees -------------*/ 453 454 public void visitClassDef(JCClassDecl tree) { 455 if (tree.sym == null) return; 456 boolean alivePrev = alive; 457 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; 458 Lint lintPrev = lint; 459 460 pendingExits = new ListBuffer<>(); 461 lint = lint.augment(tree.sym); 462 463 try { 464 // process all the static initializers 465 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 466 if (!l.head.hasTag(METHODDEF) && 467 (TreeInfo.flags(l.head) & STATIC) != 0) { 468 scanDef(l.head); 469 } 470 } 471 472 // process all the instance initializers 473 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 474 if (!l.head.hasTag(METHODDEF) && 475 (TreeInfo.flags(l.head) & STATIC) == 0) { 476 scanDef(l.head); 477 } 478 } 479 480 // process all the methods 481 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 482 if (l.head.hasTag(METHODDEF)) { 483 scan(l.head); 484 } 485 } 486 } finally { 487 pendingExits = pendingExitsPrev; 488 alive = alivePrev; 489 lint = lintPrev; 490 } 491 } 492 493 public void visitMethodDef(JCMethodDecl tree) { 494 if (tree.body == null) return; 495 Lint lintPrev = lint; 496 497 lint = lint.augment(tree.sym); 498 499 Assert.check(pendingExits.isEmpty()); 500 501 try { 502 alive = true; 503 scanStat(tree.body); 504 505 if (alive && !tree.sym.type.getReturnType().hasTag(VOID)) 506 log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); 507 508 List<PendingExit> exits = pendingExits.toList(); 509 pendingExits = new ListBuffer<>(); 510 while (exits.nonEmpty()) { 511 PendingExit exit = exits.head; 512 exits = exits.tail; 513 Assert.check(exit.tree.hasTag(RETURN)); 514 } 515 } finally { 516 lint = lintPrev; 517 } 518 } 519 520 public void visitVarDef(JCVariableDecl tree) { 521 if (tree.init != null) { 522 Lint lintPrev = lint; 523 lint = lint.augment(tree.sym); 524 try{ 525 scan(tree.init); 526 } finally { 527 lint = lintPrev; 528 } 529 } 530 } 531 532 public void visitBlock(JCBlock tree) { 533 scanStats(tree.stats); 534 } 535 536 public void visitDoLoop(JCDoWhileLoop tree) { 537 ListBuffer<PendingExit> prevPendingExits = pendingExits; 538 pendingExits = new ListBuffer<>(); 539 scanStat(tree.body); 540 alive |= resolveContinues(tree); 541 scan(tree.cond); 542 alive = alive && !tree.cond.type.isTrue(); 543 alive |= resolveBreaks(tree, prevPendingExits); 544 } 545 546 public void visitWhileLoop(JCWhileLoop tree) { 547 ListBuffer<PendingExit> prevPendingExits = pendingExits; 548 pendingExits = new ListBuffer<>(); 549 scan(tree.cond); 550 alive = !tree.cond.type.isFalse(); 551 scanStat(tree.body); 552 alive |= resolveContinues(tree); 553 alive = resolveBreaks(tree, prevPendingExits) || 554 !tree.cond.type.isTrue(); 555 } 556 557 public void visitForLoop(JCForLoop tree) { 558 ListBuffer<PendingExit> prevPendingExits = pendingExits; 559 scanStats(tree.init); 560 pendingExits = new ListBuffer<>(); 561 if (tree.cond != null) { 562 scan(tree.cond); 563 alive = !tree.cond.type.isFalse(); 564 } else { 565 alive = true; 566 } 567 scanStat(tree.body); 568 alive |= resolveContinues(tree); 569 scan(tree.step); 570 alive = resolveBreaks(tree, prevPendingExits) || 571 tree.cond != null && !tree.cond.type.isTrue(); 572 } 573 574 public void visitForeachLoop(JCEnhancedForLoop tree) { 575 visitVarDef(tree.var); 576 ListBuffer<PendingExit> prevPendingExits = pendingExits; 577 scan(tree.expr); 578 pendingExits = new ListBuffer<>(); 579 scanStat(tree.body); 580 alive |= resolveContinues(tree); 581 resolveBreaks(tree, prevPendingExits); 582 alive = true; 583 } 584 585 public void visitLabelled(JCLabeledStatement tree) { 586 ListBuffer<PendingExit> prevPendingExits = pendingExits; 587 pendingExits = new ListBuffer<>(); 588 scanStat(tree.body); 589 alive |= resolveBreaks(tree, prevPendingExits); 590 } 591 592 public void visitSwitch(JCSwitch tree) { 593 ListBuffer<PendingExit> prevPendingExits = pendingExits; 594 pendingExits = new ListBuffer<>(); 595 scan(tree.selector); 596 boolean hasDefault = false; 597 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 598 alive = true; 599 JCCase c = l.head; 600 if (c.pat == null) 601 hasDefault = true; 602 else 603 scan(c.pat); 604 scanStats(c.stats); 605 // Warn about fall-through if lint switch fallthrough enabled. 606 if (alive && 607 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && 608 c.stats.nonEmpty() && l.tail.nonEmpty()) 609 log.warning(Lint.LintCategory.FALLTHROUGH, 610 l.tail.head.pos(), 611 "possible.fall-through.into.case"); 612 } 613 if (!hasDefault) { 614 alive = true; 615 } 616 alive |= resolveBreaks(tree, prevPendingExits); 617 } 618 619 public void visitTry(JCTry tree) { 620 ListBuffer<PendingExit> prevPendingExits = pendingExits; 621 pendingExits = new ListBuffer<>(); 622 for (JCTree resource : tree.resources) { 623 if (resource instanceof JCVariableDecl) { 624 JCVariableDecl vdecl = (JCVariableDecl) resource; 625 visitVarDef(vdecl); 626 } else if (resource instanceof JCExpression) { 627 scan((JCExpression) resource); 628 } else { 629 throw new AssertionError(tree); // parser error 630 } 631 } 632 633 scanStat(tree.body); 634 boolean aliveEnd = alive; 635 636 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 637 alive = true; 638 JCVariableDecl param = l.head.param; 639 scan(param); 640 scanStat(l.head.body); 641 aliveEnd |= alive; 642 } 643 if (tree.finalizer != null) { 644 ListBuffer<PendingExit> exits = pendingExits; 645 pendingExits = prevPendingExits; 646 alive = true; 647 scanStat(tree.finalizer); 648 tree.finallyCanCompleteNormally = alive; 649 if (!alive) { 650 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { 651 log.warning(Lint.LintCategory.FINALLY, 652 TreeInfo.diagEndPos(tree.finalizer), 653 "finally.cannot.complete"); 654 } 655 } else { 656 while (exits.nonEmpty()) { 657 pendingExits.append(exits.next()); 658 } 659 alive = aliveEnd; 660 } 661 } else { 662 alive = aliveEnd; 663 ListBuffer<PendingExit> exits = pendingExits; 664 pendingExits = prevPendingExits; 665 while (exits.nonEmpty()) pendingExits.append(exits.next()); 666 } 667 } 668 669 @Override 670 public void visitIf(JCIf tree) { 671 scan(tree.cond); 672 scanStat(tree.thenpart); 673 if (tree.elsepart != null) { 674 boolean aliveAfterThen = alive; 675 alive = true; 676 scanStat(tree.elsepart); 677 alive = alive | aliveAfterThen; 678 } else { 679 alive = true; 680 } 681 } 682 683 public void visitBreak(JCBreak tree) { 684 recordExit(new PendingExit(tree)); 685 } 686 687 public void visitContinue(JCContinue tree) { 688 recordExit(new PendingExit(tree)); 689 } 690 691 public void visitReturn(JCReturn tree) { 692 scan(tree.expr); 693 recordExit(new PendingExit(tree)); 694 } 695 696 public void visitThrow(JCThrow tree) { 697 scan(tree.expr); 698 markDead(); 699 } 700 701 public void visitApply(JCMethodInvocation tree) { 702 scan(tree.meth); 703 scan(tree.args); 704 } 705 706 public void visitNewClass(JCNewClass tree) { 707 scan(tree.encl); 708 scan(tree.args); 709 if (tree.def != null) { 710 scan(tree.def); 711 } 712 } 713 714 @Override 715 public void visitLambda(JCLambda tree) { 716 if (tree.type != null && 717 tree.type.isErroneous()) { 718 return; 719 } 720 721 ListBuffer<PendingExit> prevPending = pendingExits; 722 boolean prevAlive = alive; 723 try { 724 pendingExits = new ListBuffer<>(); 725 alive = true; 726 scanStat(tree.body); 727 tree.canCompleteNormally = alive; 728 } 729 finally { 730 pendingExits = prevPending; 731 alive = prevAlive; 732 } 733 } 734 735 public void visitModuleDef(JCModuleDecl tree) { 736 // Do nothing for modules 737 } 738 739 /************************************************************************** 740 * main method 741 *************************************************************************/ 742 743 /** Perform definite assignment/unassignment analysis on a tree. 744 */ 745 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 746 analyzeTree(env, env.tree, make); 747 } 748 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 749 try { 750 attrEnv = env; 751 Flow.this.make = make; 752 pendingExits = new ListBuffer<>(); 753 alive = true; 754 scan(tree); 755 } finally { 756 pendingExits = null; 757 Flow.this.make = null; 758 } 759 } 760 } 761 762 /** 763 * This pass implements the second step of the dataflow analysis, namely 764 * the exception analysis. This is to ensure that every checked exception that is 765 * thrown is declared or caught. The analyzer uses some info that has been set by 766 * the liveliness analyzer. 767 */ 768 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { 769 770 /** A flag that indicates whether the last statement could 771 * complete normally. 772 */ 773 HashMap<Symbol, List<Type>> preciseRethrowTypes; 774 775 /** The current class being defined. 776 */ 777 JCClassDecl classDef; 778 779 /** The list of possibly thrown declarable exceptions. 780 */ 781 List<Type> thrown; 782 783 /** The list of exceptions that are either caught or declared to be 784 * thrown. 785 */ 786 List<Type> caught; 787 788 class FlowPendingExit extends BaseAnalyzer.PendingExit { 789 790 Type thrown; 791 792 FlowPendingExit(JCTree tree, Type thrown) { 793 super(tree); 794 this.thrown = thrown; 795 } 796 } 797 798 @Override 799 void markDead() { 800 //do nothing 801 } 802 803 /*-------------------- Exceptions ----------------------*/ 804 805 /** Complain that pending exceptions are not caught. 806 */ 807 void errorUncaught() { 808 for (FlowPendingExit exit = pendingExits.next(); 809 exit != null; 810 exit = pendingExits.next()) { 811 if (classDef != null && 812 classDef.pos == exit.tree.pos) { 813 log.error(exit.tree.pos(), 814 "unreported.exception.default.constructor", 815 exit.thrown); 816 } else if (exit.tree.hasTag(VARDEF) && 817 ((JCVariableDecl)exit.tree).sym.isResourceVariable()) { 818 log.error(exit.tree.pos(), 819 "unreported.exception.implicit.close", 820 exit.thrown, 821 ((JCVariableDecl)exit.tree).sym.name); 822 } else { 823 log.error(exit.tree.pos(), 824 "unreported.exception.need.to.catch.or.throw", 825 exit.thrown); 826 } 827 } 828 } 829 830 /** Record that exception is potentially thrown and check that it 831 * is caught. 832 */ 833 void markThrown(JCTree tree, Type exc) { 834 if (!chk.isUnchecked(tree.pos(), exc)) { 835 if (!chk.isHandled(exc, caught)) { 836 pendingExits.append(new FlowPendingExit(tree, exc)); 837 } 838 thrown = chk.incl(exc, thrown); 839 } 840 } 841 842 /************************************************************************* 843 * Visitor methods for statements and definitions 844 *************************************************************************/ 845 846 /* ------------ Visitor methods for various sorts of trees -------------*/ 847 848 public void visitClassDef(JCClassDecl tree) { 849 if (tree.sym == null) return; 850 851 JCClassDecl classDefPrev = classDef; 852 List<Type> thrownPrev = thrown; 853 List<Type> caughtPrev = caught; 854 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; 855 Lint lintPrev = lint; 856 857 pendingExits = new ListBuffer<>(); 858 if (tree.name != names.empty) { 859 caught = List.nil(); 860 } 861 classDef = tree; 862 thrown = List.nil(); 863 lint = lint.augment(tree.sym); 864 865 try { 866 // process all the static initializers 867 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 868 if (!l.head.hasTag(METHODDEF) && 869 (TreeInfo.flags(l.head) & STATIC) != 0) { 870 scan(l.head); 871 errorUncaught(); 872 } 873 } 874 875 // add intersection of all thrown clauses of initial constructors 876 // to set of caught exceptions, unless class is anonymous. 877 if (tree.name != names.empty) { 878 boolean firstConstructor = true; 879 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 880 if (TreeInfo.isInitialConstructor(l.head)) { 881 List<Type> mthrown = 882 ((JCMethodDecl) l.head).sym.type.getThrownTypes(); 883 if (firstConstructor) { 884 caught = mthrown; 885 firstConstructor = false; 886 } else { 887 caught = chk.intersect(mthrown, caught); 888 } 889 } 890 } 891 } 892 893 // process all the instance initializers 894 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 895 if (!l.head.hasTag(METHODDEF) && 896 (TreeInfo.flags(l.head) & STATIC) == 0) { 897 scan(l.head); 898 errorUncaught(); 899 } 900 } 901 902 // in an anonymous class, add the set of thrown exceptions to 903 // the throws clause of the synthetic constructor and propagate 904 // outwards. 905 // Changing the throws clause on the fly is okay here because 906 // the anonymous constructor can't be invoked anywhere else, 907 // and its type hasn't been cached. 908 if (tree.name == names.empty) { 909 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 910 if (TreeInfo.isInitialConstructor(l.head)) { 911 JCMethodDecl mdef = (JCMethodDecl)l.head; 912 mdef.thrown = make.Types(thrown); 913 mdef.sym.type = types.createMethodTypeWithThrown(mdef.sym.type, thrown); 914 } 915 } 916 thrownPrev = chk.union(thrown, thrownPrev); 917 } 918 919 // process all the methods 920 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 921 if (l.head.hasTag(METHODDEF)) { 922 scan(l.head); 923 errorUncaught(); 924 } 925 } 926 927 thrown = thrownPrev; 928 } finally { 929 pendingExits = pendingExitsPrev; 930 caught = caughtPrev; 931 classDef = classDefPrev; 932 lint = lintPrev; 933 } 934 } 935 936 public void visitMethodDef(JCMethodDecl tree) { 937 if (tree.body == null) return; 938 939 List<Type> caughtPrev = caught; 940 List<Type> mthrown = tree.sym.type.getThrownTypes(); 941 Lint lintPrev = lint; 942 943 lint = lint.augment(tree.sym); 944 945 Assert.check(pendingExits.isEmpty()); 946 947 try { 948 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 949 JCVariableDecl def = l.head; 950 scan(def); 951 } 952 if (TreeInfo.isInitialConstructor(tree)) 953 caught = chk.union(caught, mthrown); 954 else if ((tree.sym.flags() & (BLOCK | STATIC)) != BLOCK) 955 caught = mthrown; 956 // else we are in an instance initializer block; 957 // leave caught unchanged. 958 959 scan(tree.body); 960 961 List<FlowPendingExit> exits = pendingExits.toList(); 962 pendingExits = new ListBuffer<>(); 963 while (exits.nonEmpty()) { 964 FlowPendingExit exit = exits.head; 965 exits = exits.tail; 966 if (exit.thrown == null) { 967 Assert.check(exit.tree.hasTag(RETURN)); 968 } else { 969 // uncaught throws will be reported later 970 pendingExits.append(exit); 971 } 972 } 973 } finally { 974 caught = caughtPrev; 975 lint = lintPrev; 976 } 977 } 978 979 public void visitVarDef(JCVariableDecl tree) { 980 if (tree.init != null) { 981 Lint lintPrev = lint; 982 lint = lint.augment(tree.sym); 983 try{ 984 scan(tree.init); 985 } finally { 986 lint = lintPrev; 987 } 988 } 989 } 990 991 public void visitBlock(JCBlock tree) { 992 scan(tree.stats); 993 } 994 995 public void visitDoLoop(JCDoWhileLoop tree) { 996 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 997 pendingExits = new ListBuffer<>(); 998 scan(tree.body); 999 resolveContinues(tree); 1000 scan(tree.cond); 1001 resolveBreaks(tree, prevPendingExits); 1002 } 1003 1004 public void visitWhileLoop(JCWhileLoop tree) { 1005 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1006 pendingExits = new ListBuffer<>(); 1007 scan(tree.cond); 1008 scan(tree.body); 1009 resolveContinues(tree); 1010 resolveBreaks(tree, prevPendingExits); 1011 } 1012 1013 public void visitForLoop(JCForLoop tree) { 1014 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1015 scan(tree.init); 1016 pendingExits = new ListBuffer<>(); 1017 if (tree.cond != null) { 1018 scan(tree.cond); 1019 } 1020 scan(tree.body); 1021 resolveContinues(tree); 1022 scan(tree.step); 1023 resolveBreaks(tree, prevPendingExits); 1024 } 1025 1026 public void visitForeachLoop(JCEnhancedForLoop tree) { 1027 visitVarDef(tree.var); 1028 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1029 scan(tree.expr); 1030 pendingExits = new ListBuffer<>(); 1031 scan(tree.body); 1032 resolveContinues(tree); 1033 resolveBreaks(tree, prevPendingExits); 1034 } 1035 1036 public void visitLabelled(JCLabeledStatement tree) { 1037 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1038 pendingExits = new ListBuffer<>(); 1039 scan(tree.body); 1040 resolveBreaks(tree, prevPendingExits); 1041 } 1042 1043 public void visitSwitch(JCSwitch tree) { 1044 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1045 pendingExits = new ListBuffer<>(); 1046 scan(tree.selector); 1047 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 1048 JCCase c = l.head; 1049 if (c.pat != null) { 1050 scan(c.pat); 1051 } 1052 scan(c.stats); 1053 } 1054 resolveBreaks(tree, prevPendingExits); 1055 } 1056 1057 public void visitTry(JCTry tree) { 1058 List<Type> caughtPrev = caught; 1059 List<Type> thrownPrev = thrown; 1060 thrown = List.nil(); 1061 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1062 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1063 ((JCTypeUnion)l.head.param.vartype).alternatives : 1064 List.of(l.head.param.vartype); 1065 for (JCExpression ct : subClauses) { 1066 caught = chk.incl(ct.type, caught); 1067 } 1068 } 1069 1070 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; 1071 pendingExits = new ListBuffer<>(); 1072 for (JCTree resource : tree.resources) { 1073 if (resource instanceof JCVariableDecl) { 1074 JCVariableDecl vdecl = (JCVariableDecl) resource; 1075 visitVarDef(vdecl); 1076 } else if (resource instanceof JCExpression) { 1077 scan((JCExpression) resource); 1078 } else { 1079 throw new AssertionError(tree); // parser error 1080 } 1081 } 1082 for (JCTree resource : tree.resources) { 1083 List<Type> closeableSupertypes = resource.type.isCompound() ? 1084 types.interfaces(resource.type).prepend(types.supertype(resource.type)) : 1085 List.of(resource.type); 1086 for (Type sup : closeableSupertypes) { 1087 if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { 1088 Symbol closeMethod = rs.resolveQualifiedMethod(tree, 1089 attrEnv, 1090 types.skipTypeVars(sup, false), 1091 names.close, 1092 List.<Type>nil(), 1093 List.<Type>nil()); 1094 Type mt = types.memberType(resource.type, closeMethod); 1095 if (closeMethod.kind == MTH) { 1096 for (Type t : mt.getThrownTypes()) { 1097 markThrown(resource, t); 1098 } 1099 } 1100 } 1101 } 1102 } 1103 scan(tree.body); 1104 List<Type> thrownInTry = allowImprovedCatchAnalysis ? 1105 chk.union(thrown, List.of(syms.runtimeExceptionType, syms.errorType)) : 1106 thrown; 1107 thrown = thrownPrev; 1108 caught = caughtPrev; 1109 1110 List<Type> caughtInTry = List.nil(); 1111 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 1112 JCVariableDecl param = l.head.param; 1113 List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? 1114 ((JCTypeUnion)l.head.param.vartype).alternatives : 1115 List.of(l.head.param.vartype); 1116 List<Type> ctypes = List.nil(); 1117 List<Type> rethrownTypes = chk.diff(thrownInTry, caughtInTry); 1118 for (JCExpression ct : subClauses) { 1119 Type exc = ct.type; 1120 if (exc != syms.unknownType) { 1121 ctypes = ctypes.append(exc); 1122 if (types.isSameType(exc, syms.objectType)) 1123 continue; 1124 checkCaughtType(l.head.pos(), exc, thrownInTry, caughtInTry); 1125 caughtInTry = chk.incl(exc, caughtInTry); 1126 } 1127 } 1128 scan(param); 1129 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); 1130 scan(l.head.body); 1131 preciseRethrowTypes.remove(param.sym); 1132 } 1133 if (tree.finalizer != null) { 1134 List<Type> savedThrown = thrown; 1135 thrown = List.nil(); 1136 ListBuffer<FlowPendingExit> exits = pendingExits; 1137 pendingExits = prevPendingExits; 1138 scan(tree.finalizer); 1139 if (!tree.finallyCanCompleteNormally) { 1140 // discard exits and exceptions from try and finally 1141 thrown = chk.union(thrown, thrownPrev); 1142 } else { 1143 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1144 thrown = chk.union(thrown, savedThrown); 1145 // FIX: this doesn't preserve source order of exits in catch 1146 // versus finally! 1147 while (exits.nonEmpty()) { 1148 pendingExits.append(exits.next()); 1149 } 1150 } 1151 } else { 1152 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); 1153 ListBuffer<FlowPendingExit> exits = pendingExits; 1154 pendingExits = prevPendingExits; 1155 while (exits.nonEmpty()) pendingExits.append(exits.next()); 1156 } 1157 } 1158 1159 @Override 1160 public void visitIf(JCIf tree) { 1161 scan(tree.cond); 1162 scan(tree.thenpart); 1163 if (tree.elsepart != null) { 1164 scan(tree.elsepart); 1165 } 1166 } 1167 1168 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { 1169 if (chk.subset(exc, caughtInTry)) { 1170 log.error(pos, "except.already.caught", exc); 1171 } else if (!chk.isUnchecked(pos, exc) && 1172 !isExceptionOrThrowable(exc) && 1173 !chk.intersects(exc, thrownInTry)) { 1174 log.error(pos, "except.never.thrown.in.try", exc); 1175 } else if (allowImprovedCatchAnalysis) { 1176 List<Type> catchableThrownTypes = chk.intersect(List.of(exc), thrownInTry); 1177 // 'catchableThrownTypes' cannnot possibly be empty - if 'exc' was an 1178 // unchecked exception, the result list would not be empty, as the augmented 1179 // thrown set includes { RuntimeException, Error }; if 'exc' was a checked 1180 // exception, that would have been covered in the branch above 1181 if (chk.diff(catchableThrownTypes, caughtInTry).isEmpty() && 1182 !isExceptionOrThrowable(exc)) { 1183 String key = catchableThrownTypes.length() == 1 ? 1184 "unreachable.catch" : 1185 "unreachable.catch.1"; 1186 log.warning(pos, key, catchableThrownTypes); 1187 } 1188 } 1189 } 1190 //where 1191 private boolean isExceptionOrThrowable(Type exc) { 1192 return exc.tsym == syms.throwableType.tsym || 1193 exc.tsym == syms.exceptionType.tsym; 1194 } 1195 1196 public void visitBreak(JCBreak tree) { 1197 recordExit(new FlowPendingExit(tree, null)); 1198 } 1199 1200 public void visitContinue(JCContinue tree) { 1201 recordExit(new FlowPendingExit(tree, null)); 1202 } 1203 1204 public void visitReturn(JCReturn tree) { 1205 scan(tree.expr); 1206 recordExit(new FlowPendingExit(tree, null)); 1207 } 1208 1209 public void visitThrow(JCThrow tree) { 1210 scan(tree.expr); 1211 Symbol sym = TreeInfo.symbol(tree.expr); 1212 if (sym != null && 1213 sym.kind == VAR && 1214 (sym.flags() & (FINAL | EFFECTIVELY_FINAL)) != 0 && 1215 preciseRethrowTypes.get(sym) != null && 1216 allowImprovedRethrowAnalysis) { 1217 for (Type t : preciseRethrowTypes.get(sym)) { 1218 markThrown(tree, t); 1219 } 1220 } 1221 else { 1222 markThrown(tree, tree.expr.type); 1223 } 1224 markDead(); 1225 } 1226 1227 public void visitApply(JCMethodInvocation tree) { 1228 scan(tree.meth); 1229 scan(tree.args); 1230 for (List<Type> l = tree.meth.type.getThrownTypes(); l.nonEmpty(); l = l.tail) 1231 markThrown(tree, l.head); 1232 } 1233 1234 public void visitNewClass(JCNewClass tree) { 1235 scan(tree.encl); 1236 scan(tree.args); 1237 // scan(tree.def); 1238 for (List<Type> l = tree.constructorType.getThrownTypes(); 1239 l.nonEmpty(); 1240 l = l.tail) { 1241 markThrown(tree, l.head); 1242 } 1243 List<Type> caughtPrev = caught; 1244 try { 1245 // If the new class expression defines an anonymous class, 1246 // analysis of the anonymous constructor may encounter thrown 1247 // types which are unsubstituted type variables. 1248 // However, since the constructor's actual thrown types have 1249 // already been marked as thrown, it is safe to simply include 1250 // each of the constructor's formal thrown types in the set of 1251 // 'caught/declared to be thrown' types, for the duration of 1252 // the class def analysis. 1253 if (tree.def != null) 1254 for (List<Type> l = tree.constructor.type.getThrownTypes(); 1255 l.nonEmpty(); 1256 l = l.tail) { 1257 caught = chk.incl(l.head, caught); 1258 } 1259 scan(tree.def); 1260 } 1261 finally { 1262 caught = caughtPrev; 1263 } 1264 } 1265 1266 @Override 1267 public void visitLambda(JCLambda tree) { 1268 if (tree.type != null && 1269 tree.type.isErroneous()) { 1270 return; 1271 } 1272 List<Type> prevCaught = caught; 1273 List<Type> prevThrown = thrown; 1274 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1275 try { 1276 pendingExits = new ListBuffer<>(); 1277 caught = tree.getDescriptorType(types).getThrownTypes(); 1278 thrown = List.nil(); 1279 scan(tree.body); 1280 List<FlowPendingExit> exits = pendingExits.toList(); 1281 pendingExits = new ListBuffer<>(); 1282 while (exits.nonEmpty()) { 1283 FlowPendingExit exit = exits.head; 1284 exits = exits.tail; 1285 if (exit.thrown == null) { 1286 Assert.check(exit.tree.hasTag(RETURN)); 1287 } else { 1288 // uncaught throws will be reported later 1289 pendingExits.append(exit); 1290 } 1291 } 1292 1293 errorUncaught(); 1294 } finally { 1295 pendingExits = prevPending; 1296 caught = prevCaught; 1297 thrown = prevThrown; 1298 } 1299 } 1300 1301 public void visitModuleDef(JCModuleDecl tree) { 1302 // Do nothing for modules 1303 } 1304 1305 /************************************************************************** 1306 * main method 1307 *************************************************************************/ 1308 1309 /** Perform definite assignment/unassignment analysis on a tree. 1310 */ 1311 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 1312 analyzeTree(env, env.tree, make); 1313 } 1314 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 1315 try { 1316 attrEnv = env; 1317 Flow.this.make = make; 1318 pendingExits = new ListBuffer<>(); 1319 preciseRethrowTypes = new HashMap<>(); 1320 this.thrown = this.caught = null; 1321 this.classDef = null; 1322 scan(tree); 1323 } finally { 1324 pendingExits = null; 1325 Flow.this.make = null; 1326 this.thrown = this.caught = null; 1327 this.classDef = null; 1328 } 1329 } 1330 } 1331 1332 /** 1333 * Specialized pass that performs reachability analysis on a lambda 1334 */ 1335 class LambdaAliveAnalyzer extends AliveAnalyzer { 1336 1337 boolean inLambda; 1338 1339 @Override 1340 public void visitReturn(JCReturn tree) { 1341 //ignore lambda return expression (which might not even be attributed) 1342 recordExit(new PendingExit(tree)); 1343 } 1344 1345 @Override 1346 public void visitLambda(JCLambda tree) { 1347 if (inLambda || tree.getBodyKind() == BodyKind.EXPRESSION) { 1348 return; 1349 } 1350 inLambda = true; 1351 try { 1352 super.visitLambda(tree); 1353 } finally { 1354 inLambda = false; 1355 } 1356 } 1357 1358 @Override 1359 public void visitClassDef(JCClassDecl tree) { 1360 //skip 1361 } 1362 } 1363 1364 /** 1365 * Specialized pass that performs DA/DU on a lambda 1366 */ 1367 class LambdaAssignAnalyzer extends AssignAnalyzer { 1368 WriteableScope enclosedSymbols; 1369 boolean inLambda; 1370 1371 LambdaAssignAnalyzer(Env<AttrContext> env) { 1372 enclosedSymbols = WriteableScope.create(env.enclClass.sym); 1373 } 1374 1375 @Override 1376 public void visitLambda(JCLambda tree) { 1377 if (inLambda) { 1378 return; 1379 } 1380 inLambda = true; 1381 try { 1382 super.visitLambda(tree); 1383 } finally { 1384 inLambda = false; 1385 } 1386 } 1387 1388 @Override 1389 public void visitVarDef(JCVariableDecl tree) { 1390 enclosedSymbols.enter(tree.sym); 1391 super.visitVarDef(tree); 1392 } 1393 @Override 1394 protected boolean trackable(VarSymbol sym) { 1395 return enclosedSymbols.includes(sym) && 1396 sym.owner.kind == MTH; 1397 } 1398 1399 @Override 1400 public void visitClassDef(JCClassDecl tree) { 1401 //skip 1402 } 1403 } 1404 1405 /** 1406 * Specialized pass that performs inference of thrown types for lambdas. 1407 */ 1408 class LambdaFlowAnalyzer extends FlowAnalyzer { 1409 List<Type> inferredThrownTypes; 1410 boolean inLambda; 1411 @Override 1412 public void visitLambda(JCLambda tree) { 1413 if ((tree.type != null && 1414 tree.type.isErroneous()) || inLambda) { 1415 return; 1416 } 1417 List<Type> prevCaught = caught; 1418 List<Type> prevThrown = thrown; 1419 ListBuffer<FlowPendingExit> prevPending = pendingExits; 1420 inLambda = true; 1421 try { 1422 pendingExits = new ListBuffer<>(); 1423 caught = List.of(syms.throwableType); 1424 thrown = List.nil(); 1425 scan(tree.body); 1426 inferredThrownTypes = thrown; 1427 } finally { 1428 pendingExits = prevPending; 1429 caught = prevCaught; 1430 thrown = prevThrown; 1431 inLambda = false; 1432 } 1433 } 1434 @Override 1435 public void visitClassDef(JCClassDecl tree) { 1436 //skip 1437 } 1438 } 1439 1440 /** 1441 * This pass implements (i) definite assignment analysis, which ensures that 1442 * each variable is assigned when used and (ii) definite unassignment analysis, 1443 * which ensures that no final variable is assigned more than once. This visitor 1444 * depends on the results of the liveliness analyzer. This pass is also used to mark 1445 * effectively-final local variables/parameters. 1446 */ 1447 1448 public class AssignAnalyzer extends BaseAnalyzer<AssignAnalyzer.AssignPendingExit> { 1449 1450 /** The set of definitely assigned variables. 1451 */ 1452 final Bits inits; 1453 1454 /** The set of definitely unassigned variables. 1455 */ 1456 final Bits uninits; 1457 1458 /** The set of variables that are definitely unassigned everywhere 1459 * in current try block. This variable is maintained lazily; it is 1460 * updated only when something gets removed from uninits, 1461 * typically by being assigned in reachable code. To obtain the 1462 * correct set of variables which are definitely unassigned 1463 * anywhere in current try block, intersect uninitsTry and 1464 * uninits. 1465 */ 1466 final Bits uninitsTry; 1467 1468 /** When analyzing a condition, inits and uninits are null. 1469 * Instead we have: 1470 */ 1471 final Bits initsWhenTrue; 1472 final Bits initsWhenFalse; 1473 final Bits uninitsWhenTrue; 1474 final Bits uninitsWhenFalse; 1475 1476 /** A mapping from addresses to variable symbols. 1477 */ 1478 protected JCVariableDecl[] vardecls; 1479 1480 /** The current class being defined. 1481 */ 1482 JCClassDecl classDef; 1483 1484 /** The first variable sequence number in this class definition. 1485 */ 1486 int firstadr; 1487 1488 /** The next available variable sequence number. 1489 */ 1490 protected int nextadr; 1491 1492 /** The first variable sequence number in a block that can return. 1493 */ 1494 protected int returnadr; 1495 1496 /** The list of unreferenced automatic resources. 1497 */ 1498 WriteableScope unrefdResources; 1499 1500 /** Modified when processing a loop body the second time for DU analysis. */ 1501 FlowKind flowKind = FlowKind.NORMAL; 1502 1503 /** The starting position of the analyzed tree */ 1504 int startPos; 1505 1506 public class AssignPendingExit extends BaseAnalyzer.PendingExit { 1507 1508 final Bits inits; 1509 final Bits uninits; 1510 final Bits exit_inits = new Bits(true); 1511 final Bits exit_uninits = new Bits(true); 1512 1513 public AssignPendingExit(JCTree tree, final Bits inits, final Bits uninits) { 1514 super(tree); 1515 this.inits = inits; 1516 this.uninits = uninits; 1517 this.exit_inits.assign(inits); 1518 this.exit_uninits.assign(uninits); 1519 } 1520 1521 @Override 1522 public void resolveJump() { 1523 inits.andSet(exit_inits); 1524 uninits.andSet(exit_uninits); 1525 } 1526 } 1527 1528 public AssignAnalyzer() { 1529 this.inits = new Bits(); 1530 uninits = new Bits(); 1531 uninitsTry = new Bits(); 1532 initsWhenTrue = new Bits(true); 1533 initsWhenFalse = new Bits(true); 1534 uninitsWhenTrue = new Bits(true); 1535 uninitsWhenFalse = new Bits(true); 1536 } 1537 1538 private boolean isInitialConstructor = false; 1539 1540 @Override 1541 protected void markDead() { 1542 if (!isInitialConstructor) { 1543 inits.inclRange(returnadr, nextadr); 1544 } else { 1545 for (int address = returnadr; address < nextadr; address++) { 1546 if (!(isFinalUninitializedStaticField(vardecls[address].sym))) { 1547 inits.incl(address); 1548 } 1549 } 1550 } 1551 uninits.inclRange(returnadr, nextadr); 1552 } 1553 1554 /*-------------- Processing variables ----------------------*/ 1555 1556 /** Do we need to track init/uninit state of this symbol? 1557 * I.e. is symbol either a local or a blank final variable? 1558 */ 1559 protected boolean trackable(VarSymbol sym) { 1560 return 1561 sym.pos >= startPos && 1562 ((sym.owner.kind == MTH || 1563 isFinalUninitializedField(sym))); 1564 } 1565 1566 boolean isFinalUninitializedField(VarSymbol sym) { 1567 return sym.owner.kind == TYP && 1568 ((sym.flags() & (FINAL | HASINIT | PARAMETER)) == FINAL && 1569 classDef.sym.isEnclosedBy((ClassSymbol)sym.owner)); 1570 } 1571 1572 boolean isFinalUninitializedStaticField(VarSymbol sym) { 1573 return isFinalUninitializedField(sym) && sym.isStatic(); 1574 } 1575 1576 /** Initialize new trackable variable by setting its address field 1577 * to the next available sequence number and entering it under that 1578 * index into the vars array. 1579 */ 1580 void newVar(JCVariableDecl varDecl) { 1581 VarSymbol sym = varDecl.sym; 1582 vardecls = ArrayUtils.ensureCapacity(vardecls, nextadr); 1583 if ((sym.flags() & FINAL) == 0) { 1584 sym.flags_field |= EFFECTIVELY_FINAL; 1585 } 1586 sym.adr = nextadr; 1587 vardecls[nextadr] = varDecl; 1588 inits.excl(nextadr); 1589 uninits.incl(nextadr); 1590 nextadr++; 1591 } 1592 1593 /** Record an initialization of a trackable variable. 1594 */ 1595 void letInit(DiagnosticPosition pos, VarSymbol sym) { 1596 if (sym.adr >= firstadr && trackable(sym)) { 1597 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { 1598 if (!uninits.isMember(sym.adr)) { 1599 //assignment targeting an effectively final variable 1600 //makes the variable lose its status of effectively final 1601 //if the variable is _not_ definitively unassigned 1602 sym.flags_field &= ~EFFECTIVELY_FINAL; 1603 } else { 1604 uninit(sym); 1605 } 1606 } 1607 else if ((sym.flags() & FINAL) != 0) { 1608 if ((sym.flags() & PARAMETER) != 0) { 1609 if ((sym.flags() & UNION) != 0) { //multi-catch parameter 1610 log.error(pos, "multicatch.parameter.may.not.be.assigned", sym); 1611 } 1612 else { 1613 log.error(pos, "final.parameter.may.not.be.assigned", 1614 sym); 1615 } 1616 } else if (!uninits.isMember(sym.adr)) { 1617 log.error(pos, flowKind.errKey, sym); 1618 } else { 1619 uninit(sym); 1620 } 1621 } 1622 inits.incl(sym.adr); 1623 } else if ((sym.flags() & FINAL) != 0) { 1624 log.error(pos, "var.might.already.be.assigned", sym); 1625 } 1626 } 1627 //where 1628 void uninit(VarSymbol sym) { 1629 if (!inits.isMember(sym.adr)) { 1630 // reachable assignment 1631 uninits.excl(sym.adr); 1632 uninitsTry.excl(sym.adr); 1633 } else { 1634 //log.rawWarning(pos, "unreachable assignment");//DEBUG 1635 uninits.excl(sym.adr); 1636 } 1637 } 1638 1639 /** If tree is either a simple name or of the form this.name or 1640 * C.this.name, and tree represents a trackable variable, 1641 * record an initialization of the variable. 1642 */ 1643 void letInit(JCTree tree) { 1644 tree = TreeInfo.skipParens(tree); 1645 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 1646 Symbol sym = TreeInfo.symbol(tree); 1647 if (sym.kind == VAR) { 1648 letInit(tree.pos(), (VarSymbol)sym); 1649 } 1650 } 1651 } 1652 1653 /** Check that trackable variable is initialized. 1654 */ 1655 void checkInit(DiagnosticPosition pos, VarSymbol sym) { 1656 checkInit(pos, sym, "var.might.not.have.been.initialized"); 1657 } 1658 1659 void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) { 1660 if ((sym.adr >= firstadr || sym.owner.kind != TYP) && 1661 trackable(sym) && 1662 !inits.isMember(sym.adr)) { 1663 log.error(pos, errkey, sym); 1664 inits.incl(sym.adr); 1665 } 1666 } 1667 1668 /** Utility method to reset several Bits instances. 1669 */ 1670 private void resetBits(Bits... bits) { 1671 for (Bits b : bits) { 1672 b.reset(); 1673 } 1674 } 1675 1676 /** Split (duplicate) inits/uninits into WhenTrue/WhenFalse sets 1677 */ 1678 void split(boolean setToNull) { 1679 initsWhenFalse.assign(inits); 1680 uninitsWhenFalse.assign(uninits); 1681 initsWhenTrue.assign(inits); 1682 uninitsWhenTrue.assign(uninits); 1683 if (setToNull) { 1684 resetBits(inits, uninits); 1685 } 1686 } 1687 1688 /** Merge (intersect) inits/uninits from WhenTrue/WhenFalse sets. 1689 */ 1690 protected void merge() { 1691 inits.assign(initsWhenFalse.andSet(initsWhenTrue)); 1692 uninits.assign(uninitsWhenFalse.andSet(uninitsWhenTrue)); 1693 } 1694 1695 /* ************************************************************************ 1696 * Visitor methods for statements and definitions 1697 *************************************************************************/ 1698 1699 /** Analyze an expression. Make sure to set (un)inits rather than 1700 * (un)initsWhenTrue(WhenFalse) on exit. 1701 */ 1702 void scanExpr(JCTree tree) { 1703 if (tree != null) { 1704 scan(tree); 1705 if (inits.isReset()) { 1706 merge(); 1707 } 1708 } 1709 } 1710 1711 /** Analyze a list of expressions. 1712 */ 1713 void scanExprs(List<? extends JCExpression> trees) { 1714 if (trees != null) 1715 for (List<? extends JCExpression> l = trees; l.nonEmpty(); l = l.tail) 1716 scanExpr(l.head); 1717 } 1718 1719 /** Analyze a condition. Make sure to set (un)initsWhenTrue(WhenFalse) 1720 * rather than (un)inits on exit. 1721 */ 1722 void scanCond(JCTree tree) { 1723 if (tree.type.isFalse()) { 1724 if (inits.isReset()) merge(); 1725 initsWhenTrue.assign(inits); 1726 initsWhenTrue.inclRange(firstadr, nextadr); 1727 uninitsWhenTrue.assign(uninits); 1728 uninitsWhenTrue.inclRange(firstadr, nextadr); 1729 initsWhenFalse.assign(inits); 1730 uninitsWhenFalse.assign(uninits); 1731 } else if (tree.type.isTrue()) { 1732 if (inits.isReset()) merge(); 1733 initsWhenFalse.assign(inits); 1734 initsWhenFalse.inclRange(firstadr, nextadr); 1735 uninitsWhenFalse.assign(uninits); 1736 uninitsWhenFalse.inclRange(firstadr, nextadr); 1737 initsWhenTrue.assign(inits); 1738 uninitsWhenTrue.assign(uninits); 1739 } else { 1740 scan(tree); 1741 if (!inits.isReset()) 1742 split(tree.type != syms.unknownType); 1743 } 1744 if (tree.type != syms.unknownType) { 1745 resetBits(inits, uninits); 1746 } 1747 } 1748 1749 /* ------------ Visitor methods for various sorts of trees -------------*/ 1750 1751 public void visitClassDef(JCClassDecl tree) { 1752 if (tree.sym == null) { 1753 return; 1754 } 1755 1756 Lint lintPrev = lint; 1757 lint = lint.augment(tree.sym); 1758 try { 1759 if (tree.sym == null) { 1760 return; 1761 } 1762 1763 JCClassDecl classDefPrev = classDef; 1764 int firstadrPrev = firstadr; 1765 int nextadrPrev = nextadr; 1766 ListBuffer<AssignPendingExit> pendingExitsPrev = pendingExits; 1767 1768 pendingExits = new ListBuffer<>(); 1769 if (tree.name != names.empty) { 1770 firstadr = nextadr; 1771 } 1772 classDef = tree; 1773 try { 1774 // define all the static fields 1775 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1776 if (l.head.hasTag(VARDEF)) { 1777 JCVariableDecl def = (JCVariableDecl)l.head; 1778 if ((def.mods.flags & STATIC) != 0) { 1779 VarSymbol sym = def.sym; 1780 if (trackable(sym)) { 1781 newVar(def); 1782 } 1783 } 1784 } 1785 } 1786 1787 // process all the static initializers 1788 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1789 if (!l.head.hasTag(METHODDEF) && 1790 (TreeInfo.flags(l.head) & STATIC) != 0) { 1791 scan(l.head); 1792 } 1793 } 1794 1795 // define all the instance fields 1796 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1797 if (l.head.hasTag(VARDEF)) { 1798 JCVariableDecl def = (JCVariableDecl)l.head; 1799 if ((def.mods.flags & STATIC) == 0) { 1800 VarSymbol sym = def.sym; 1801 if (trackable(sym)) { 1802 newVar(def); 1803 } 1804 } 1805 } 1806 } 1807 1808 // process all the instance initializers 1809 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1810 if (!l.head.hasTag(METHODDEF) && 1811 (TreeInfo.flags(l.head) & STATIC) == 0) { 1812 scan(l.head); 1813 } 1814 } 1815 1816 // process all the methods 1817 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { 1818 if (l.head.hasTag(METHODDEF)) { 1819 scan(l.head); 1820 } 1821 } 1822 } finally { 1823 pendingExits = pendingExitsPrev; 1824 nextadr = nextadrPrev; 1825 firstadr = firstadrPrev; 1826 classDef = classDefPrev; 1827 } 1828 } finally { 1829 lint = lintPrev; 1830 } 1831 } 1832 1833 public void visitMethodDef(JCMethodDecl tree) { 1834 if (tree.body == null) { 1835 return; 1836 } 1837 1838 /* MemberEnter can generate synthetic methods ignore them 1839 */ 1840 if ((tree.sym.flags() & SYNTHETIC) != 0) { 1841 return; 1842 } 1843 1844 Lint lintPrev = lint; 1845 lint = lint.augment(tree.sym); 1846 try { 1847 if (tree.body == null) { 1848 return; 1849 } 1850 /* Ignore synthetic methods, except for translated lambda methods. 1851 */ 1852 if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) { 1853 return; 1854 } 1855 1856 final Bits initsPrev = new Bits(inits); 1857 final Bits uninitsPrev = new Bits(uninits); 1858 int nextadrPrev = nextadr; 1859 int firstadrPrev = firstadr; 1860 int returnadrPrev = returnadr; 1861 1862 Assert.check(pendingExits.isEmpty()); 1863 boolean lastInitialConstructor = isInitialConstructor; 1864 try { 1865 isInitialConstructor = TreeInfo.isInitialConstructor(tree); 1866 1867 if (!isInitialConstructor) { 1868 firstadr = nextadr; 1869 } 1870 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 1871 JCVariableDecl def = l.head; 1872 scan(def); 1873 Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter without PARAMETER flag"); 1874 /* If we are executing the code from Gen, then there can be 1875 * synthetic or mandated variables, ignore them. 1876 */ 1877 initParam(def); 1878 } 1879 // else we are in an instance initializer block; 1880 // leave caught unchanged. 1881 scan(tree.body); 1882 1883 if (isInitialConstructor) { 1884 boolean isSynthesized = (tree.sym.flags() & 1885 GENERATEDCONSTR) != 0; 1886 for (int i = firstadr; i < nextadr; i++) { 1887 JCVariableDecl vardecl = vardecls[i]; 1888 VarSymbol var = vardecl.sym; 1889 if (var.owner == classDef.sym) { 1890 // choose the diagnostic position based on whether 1891 // the ctor is default(synthesized) or not 1892 if (isSynthesized) { 1893 checkInit(TreeInfo.diagnosticPositionFor(var, vardecl), 1894 var, "var.not.initialized.in.default.constructor"); 1895 } else { 1896 checkInit(TreeInfo.diagEndPos(tree.body), var); 1897 } 1898 } 1899 } 1900 } 1901 List<AssignPendingExit> exits = pendingExits.toList(); 1902 pendingExits = new ListBuffer<>(); 1903 while (exits.nonEmpty()) { 1904 AssignPendingExit exit = exits.head; 1905 exits = exits.tail; 1906 Assert.check(exit.tree.hasTag(RETURN), exit.tree); 1907 if (isInitialConstructor) { 1908 inits.assign(exit.exit_inits); 1909 for (int i = firstadr; i < nextadr; i++) { 1910 checkInit(exit.tree.pos(), vardecls[i].sym); 1911 } 1912 } 1913 } 1914 } finally { 1915 inits.assign(initsPrev); 1916 uninits.assign(uninitsPrev); 1917 nextadr = nextadrPrev; 1918 firstadr = firstadrPrev; 1919 returnadr = returnadrPrev; 1920 isInitialConstructor = lastInitialConstructor; 1921 } 1922 } finally { 1923 lint = lintPrev; 1924 } 1925 } 1926 1927 protected void initParam(JCVariableDecl def) { 1928 inits.incl(def.sym.adr); 1929 uninits.excl(def.sym.adr); 1930 } 1931 1932 public void visitVarDef(JCVariableDecl tree) { 1933 Lint lintPrev = lint; 1934 lint = lint.augment(tree.sym); 1935 try{ 1936 boolean track = trackable(tree.sym); 1937 if (track && tree.sym.owner.kind == MTH) { 1938 newVar(tree); 1939 } 1940 if (tree.init != null) { 1941 scanExpr(tree.init); 1942 if (track) { 1943 letInit(tree.pos(), tree.sym); 1944 } 1945 } 1946 } finally { 1947 lint = lintPrev; 1948 } 1949 } 1950 1951 public void visitBlock(JCBlock tree) { 1952 int nextadrPrev = nextadr; 1953 scan(tree.stats); 1954 nextadr = nextadrPrev; 1955 } 1956 1957 public void visitDoLoop(JCDoWhileLoop tree) { 1958 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1959 FlowKind prevFlowKind = flowKind; 1960 flowKind = FlowKind.NORMAL; 1961 final Bits initsSkip = new Bits(true); 1962 final Bits uninitsSkip = new Bits(true); 1963 pendingExits = new ListBuffer<>(); 1964 int prevErrors = log.nerrors; 1965 do { 1966 final Bits uninitsEntry = new Bits(uninits); 1967 uninitsEntry.excludeFrom(nextadr); 1968 scan(tree.body); 1969 resolveContinues(tree); 1970 scanCond(tree.cond); 1971 if (!flowKind.isFinal()) { 1972 initsSkip.assign(initsWhenFalse); 1973 uninitsSkip.assign(uninitsWhenFalse); 1974 } 1975 if (log.nerrors != prevErrors || 1976 flowKind.isFinal() || 1977 new Bits(uninitsEntry).diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) 1978 break; 1979 inits.assign(initsWhenTrue); 1980 uninits.assign(uninitsEntry.andSet(uninitsWhenTrue)); 1981 flowKind = FlowKind.SPECULATIVE_LOOP; 1982 } while (true); 1983 flowKind = prevFlowKind; 1984 inits.assign(initsSkip); 1985 uninits.assign(uninitsSkip); 1986 resolveBreaks(tree, prevPendingExits); 1987 } 1988 1989 public void visitWhileLoop(JCWhileLoop tree) { 1990 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 1991 FlowKind prevFlowKind = flowKind; 1992 flowKind = FlowKind.NORMAL; 1993 final Bits initsSkip = new Bits(true); 1994 final Bits uninitsSkip = new Bits(true); 1995 pendingExits = new ListBuffer<>(); 1996 int prevErrors = log.nerrors; 1997 final Bits uninitsEntry = new Bits(uninits); 1998 uninitsEntry.excludeFrom(nextadr); 1999 do { 2000 scanCond(tree.cond); 2001 if (!flowKind.isFinal()) { 2002 initsSkip.assign(initsWhenFalse) ; 2003 uninitsSkip.assign(uninitsWhenFalse); 2004 } 2005 inits.assign(initsWhenTrue); 2006 uninits.assign(uninitsWhenTrue); 2007 scan(tree.body); 2008 resolveContinues(tree); 2009 if (log.nerrors != prevErrors || 2010 flowKind.isFinal() || 2011 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) { 2012 break; 2013 } 2014 uninits.assign(uninitsEntry.andSet(uninits)); 2015 flowKind = FlowKind.SPECULATIVE_LOOP; 2016 } while (true); 2017 flowKind = prevFlowKind; 2018 //a variable is DA/DU after the while statement, if it's DA/DU assuming the 2019 //branch is not taken AND if it's DA/DU before any break statement 2020 inits.assign(initsSkip); 2021 uninits.assign(uninitsSkip); 2022 resolveBreaks(tree, prevPendingExits); 2023 } 2024 2025 public void visitForLoop(JCForLoop tree) { 2026 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2027 FlowKind prevFlowKind = flowKind; 2028 flowKind = FlowKind.NORMAL; 2029 int nextadrPrev = nextadr; 2030 scan(tree.init); 2031 final Bits initsSkip = new Bits(true); 2032 final Bits uninitsSkip = new Bits(true); 2033 pendingExits = new ListBuffer<>(); 2034 int prevErrors = log.nerrors; 2035 do { 2036 final Bits uninitsEntry = new Bits(uninits); 2037 uninitsEntry.excludeFrom(nextadr); 2038 if (tree.cond != null) { 2039 scanCond(tree.cond); 2040 if (!flowKind.isFinal()) { 2041 initsSkip.assign(initsWhenFalse); 2042 uninitsSkip.assign(uninitsWhenFalse); 2043 } 2044 inits.assign(initsWhenTrue); 2045 uninits.assign(uninitsWhenTrue); 2046 } else if (!flowKind.isFinal()) { 2047 initsSkip.assign(inits); 2048 initsSkip.inclRange(firstadr, nextadr); 2049 uninitsSkip.assign(uninits); 2050 uninitsSkip.inclRange(firstadr, nextadr); 2051 } 2052 scan(tree.body); 2053 resolveContinues(tree); 2054 scan(tree.step); 2055 if (log.nerrors != prevErrors || 2056 flowKind.isFinal() || 2057 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2058 break; 2059 uninits.assign(uninitsEntry.andSet(uninits)); 2060 flowKind = FlowKind.SPECULATIVE_LOOP; 2061 } while (true); 2062 flowKind = prevFlowKind; 2063 //a variable is DA/DU after a for loop, if it's DA/DU assuming the 2064 //branch is not taken AND if it's DA/DU before any break statement 2065 inits.assign(initsSkip); 2066 uninits.assign(uninitsSkip); 2067 resolveBreaks(tree, prevPendingExits); 2068 nextadr = nextadrPrev; 2069 } 2070 2071 public void visitForeachLoop(JCEnhancedForLoop tree) { 2072 visitVarDef(tree.var); 2073 2074 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2075 FlowKind prevFlowKind = flowKind; 2076 flowKind = FlowKind.NORMAL; 2077 int nextadrPrev = nextadr; 2078 scan(tree.expr); 2079 final Bits initsStart = new Bits(inits); 2080 final Bits uninitsStart = new Bits(uninits); 2081 2082 letInit(tree.pos(), tree.var.sym); 2083 pendingExits = new ListBuffer<>(); 2084 int prevErrors = log.nerrors; 2085 do { 2086 final Bits uninitsEntry = new Bits(uninits); 2087 uninitsEntry.excludeFrom(nextadr); 2088 scan(tree.body); 2089 resolveContinues(tree); 2090 if (log.nerrors != prevErrors || 2091 flowKind.isFinal() || 2092 new Bits(uninitsEntry).diffSet(uninits).nextBit(firstadr) == -1) 2093 break; 2094 uninits.assign(uninitsEntry.andSet(uninits)); 2095 flowKind = FlowKind.SPECULATIVE_LOOP; 2096 } while (true); 2097 flowKind = prevFlowKind; 2098 inits.assign(initsStart); 2099 uninits.assign(uninitsStart.andSet(uninits)); 2100 resolveBreaks(tree, prevPendingExits); 2101 nextadr = nextadrPrev; 2102 } 2103 2104 public void visitLabelled(JCLabeledStatement tree) { 2105 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2106 pendingExits = new ListBuffer<>(); 2107 scan(tree.body); 2108 resolveBreaks(tree, prevPendingExits); 2109 } 2110 2111 public void visitSwitch(JCSwitch tree) { 2112 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2113 pendingExits = new ListBuffer<>(); 2114 int nextadrPrev = nextadr; 2115 scanExpr(tree.selector); 2116 final Bits initsSwitch = new Bits(inits); 2117 final Bits uninitsSwitch = new Bits(uninits); 2118 boolean hasDefault = false; 2119 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { 2120 inits.assign(initsSwitch); 2121 uninits.assign(uninits.andSet(uninitsSwitch)); 2122 JCCase c = l.head; 2123 if (c.pat == null) { 2124 hasDefault = true; 2125 } else { 2126 scanExpr(c.pat); 2127 } 2128 if (hasDefault) { 2129 inits.assign(initsSwitch); 2130 uninits.assign(uninits.andSet(uninitsSwitch)); 2131 } 2132 scan(c.stats); 2133 addVars(c.stats, initsSwitch, uninitsSwitch); 2134 if (!hasDefault) { 2135 inits.assign(initsSwitch); 2136 uninits.assign(uninits.andSet(uninitsSwitch)); 2137 } 2138 // Warn about fall-through if lint switch fallthrough enabled. 2139 } 2140 if (!hasDefault) { 2141 inits.andSet(initsSwitch); 2142 } 2143 resolveBreaks(tree, prevPendingExits); 2144 nextadr = nextadrPrev; 2145 } 2146 // where 2147 /** Add any variables defined in stats to inits and uninits. */ 2148 private void addVars(List<JCStatement> stats, final Bits inits, 2149 final Bits uninits) { 2150 for (;stats.nonEmpty(); stats = stats.tail) { 2151 JCTree stat = stats.head; 2152 if (stat.hasTag(VARDEF)) { 2153 int adr = ((JCVariableDecl) stat).sym.adr; 2154 inits.excl(adr); 2155 uninits.incl(adr); 2156 } 2157 } 2158 } 2159 2160 public void visitTry(JCTry tree) { 2161 ListBuffer<JCVariableDecl> resourceVarDecls = new ListBuffer<>(); 2162 final Bits uninitsTryPrev = new Bits(uninitsTry); 2163 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; 2164 pendingExits = new ListBuffer<>(); 2165 final Bits initsTry = new Bits(inits); 2166 uninitsTry.assign(uninits); 2167 for (JCTree resource : tree.resources) { 2168 if (resource instanceof JCVariableDecl) { 2169 JCVariableDecl vdecl = (JCVariableDecl) resource; 2170 visitVarDef(vdecl); 2171 unrefdResources.enter(vdecl.sym); 2172 resourceVarDecls.append(vdecl); 2173 } else if (resource instanceof JCExpression) { 2174 scanExpr((JCExpression) resource); 2175 } else { 2176 throw new AssertionError(tree); // parser error 2177 } 2178 } 2179 scan(tree.body); 2180 uninitsTry.andSet(uninits); 2181 final Bits initsEnd = new Bits(inits); 2182 final Bits uninitsEnd = new Bits(uninits); 2183 int nextadrCatch = nextadr; 2184 2185 if (!resourceVarDecls.isEmpty() && 2186 lint.isEnabled(Lint.LintCategory.TRY)) { 2187 for (JCVariableDecl resVar : resourceVarDecls) { 2188 if (unrefdResources.includes(resVar.sym)) { 2189 log.warning(Lint.LintCategory.TRY, resVar.pos(), 2190 "try.resource.not.referenced", resVar.sym); 2191 unrefdResources.remove(resVar.sym); 2192 } 2193 } 2194 } 2195 2196 /* The analysis of each catch should be independent. 2197 * Each one should have the same initial values of inits and 2198 * uninits. 2199 */ 2200 final Bits initsCatchPrev = new Bits(initsTry); 2201 final Bits uninitsCatchPrev = new Bits(uninitsTry); 2202 2203 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { 2204 JCVariableDecl param = l.head.param; 2205 inits.assign(initsCatchPrev); 2206 uninits.assign(uninitsCatchPrev); 2207 scan(param); 2208 /* If this is a TWR and we are executing the code from Gen, 2209 * then there can be synthetic variables, ignore them. 2210 */ 2211 initParam(param); 2212 scan(l.head.body); 2213 initsEnd.andSet(inits); 2214 uninitsEnd.andSet(uninits); 2215 nextadr = nextadrCatch; 2216 } 2217 if (tree.finalizer != null) { 2218 inits.assign(initsTry); 2219 uninits.assign(uninitsTry); 2220 ListBuffer<AssignPendingExit> exits = pendingExits; 2221 pendingExits = prevPendingExits; 2222 scan(tree.finalizer); 2223 if (!tree.finallyCanCompleteNormally) { 2224 // discard exits and exceptions from try and finally 2225 } else { 2226 uninits.andSet(uninitsEnd); 2227 // FIX: this doesn't preserve source order of exits in catch 2228 // versus finally! 2229 while (exits.nonEmpty()) { 2230 AssignPendingExit exit = exits.next(); 2231 if (exit.exit_inits != null) { 2232 exit.exit_inits.orSet(inits); 2233 exit.exit_uninits.andSet(uninits); 2234 } 2235 pendingExits.append(exit); 2236 } 2237 inits.orSet(initsEnd); 2238 } 2239 } else { 2240 inits.assign(initsEnd); 2241 uninits.assign(uninitsEnd); 2242 ListBuffer<AssignPendingExit> exits = pendingExits; 2243 pendingExits = prevPendingExits; 2244 while (exits.nonEmpty()) pendingExits.append(exits.next()); 2245 } 2246 uninitsTry.andSet(uninitsTryPrev).andSet(uninits); 2247 } 2248 2249 public void visitConditional(JCConditional tree) { 2250 scanCond(tree.cond); 2251 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2252 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2253 inits.assign(initsWhenTrue); 2254 uninits.assign(uninitsWhenTrue); 2255 if (tree.truepart.type.hasTag(BOOLEAN) && 2256 tree.falsepart.type.hasTag(BOOLEAN)) { 2257 // if b and c are boolean valued, then 2258 // v is (un)assigned after a?b:c when true iff 2259 // v is (un)assigned after b when true and 2260 // v is (un)assigned after c when true 2261 scanCond(tree.truepart); 2262 final Bits initsAfterThenWhenTrue = new Bits(initsWhenTrue); 2263 final Bits initsAfterThenWhenFalse = new Bits(initsWhenFalse); 2264 final Bits uninitsAfterThenWhenTrue = new Bits(uninitsWhenTrue); 2265 final Bits uninitsAfterThenWhenFalse = new Bits(uninitsWhenFalse); 2266 inits.assign(initsBeforeElse); 2267 uninits.assign(uninitsBeforeElse); 2268 scanCond(tree.falsepart); 2269 initsWhenTrue.andSet(initsAfterThenWhenTrue); 2270 initsWhenFalse.andSet(initsAfterThenWhenFalse); 2271 uninitsWhenTrue.andSet(uninitsAfterThenWhenTrue); 2272 uninitsWhenFalse.andSet(uninitsAfterThenWhenFalse); 2273 } else { 2274 scanExpr(tree.truepart); 2275 final Bits initsAfterThen = new Bits(inits); 2276 final Bits uninitsAfterThen = new Bits(uninits); 2277 inits.assign(initsBeforeElse); 2278 uninits.assign(uninitsBeforeElse); 2279 scanExpr(tree.falsepart); 2280 inits.andSet(initsAfterThen); 2281 uninits.andSet(uninitsAfterThen); 2282 } 2283 } 2284 2285 public void visitIf(JCIf tree) { 2286 scanCond(tree.cond); 2287 final Bits initsBeforeElse = new Bits(initsWhenFalse); 2288 final Bits uninitsBeforeElse = new Bits(uninitsWhenFalse); 2289 inits.assign(initsWhenTrue); 2290 uninits.assign(uninitsWhenTrue); 2291 scan(tree.thenpart); 2292 if (tree.elsepart != null) { 2293 final Bits initsAfterThen = new Bits(inits); 2294 final Bits uninitsAfterThen = new Bits(uninits); 2295 inits.assign(initsBeforeElse); 2296 uninits.assign(uninitsBeforeElse); 2297 scan(tree.elsepart); 2298 inits.andSet(initsAfterThen); 2299 uninits.andSet(uninitsAfterThen); 2300 } else { 2301 inits.andSet(initsBeforeElse); 2302 uninits.andSet(uninitsBeforeElse); 2303 } 2304 } 2305 2306 @Override 2307 public void visitBreak(JCBreak tree) { 2308 recordExit(new AssignPendingExit(tree, inits, uninits)); 2309 } 2310 2311 @Override 2312 public void visitContinue(JCContinue tree) { 2313 recordExit(new AssignPendingExit(tree, inits, uninits)); 2314 } 2315 2316 @Override 2317 public void visitReturn(JCReturn tree) { 2318 scanExpr(tree.expr); 2319 recordExit(new AssignPendingExit(tree, inits, uninits)); 2320 } 2321 2322 public void visitThrow(JCThrow tree) { 2323 scanExpr(tree.expr); 2324 markDead(); 2325 } 2326 2327 public void visitApply(JCMethodInvocation tree) { 2328 scanExpr(tree.meth); 2329 scanExprs(tree.args); 2330 } 2331 2332 public void visitNewClass(JCNewClass tree) { 2333 scanExpr(tree.encl); 2334 scanExprs(tree.args); 2335 scan(tree.def); 2336 } 2337 2338 @Override 2339 public void visitLambda(JCLambda tree) { 2340 final Bits prevUninits = new Bits(uninits); 2341 final Bits prevInits = new Bits(inits); 2342 int returnadrPrev = returnadr; 2343 int nextadrPrev = nextadr; 2344 ListBuffer<AssignPendingExit> prevPending = pendingExits; 2345 try { 2346 returnadr = nextadr; 2347 pendingExits = new ListBuffer<>(); 2348 for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) { 2349 JCVariableDecl def = l.head; 2350 scan(def); 2351 inits.incl(def.sym.adr); 2352 uninits.excl(def.sym.adr); 2353 } 2354 if (tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 2355 scanExpr(tree.body); 2356 } else { 2357 scan(tree.body); 2358 } 2359 } 2360 finally { 2361 returnadr = returnadrPrev; 2362 uninits.assign(prevUninits); 2363 inits.assign(prevInits); 2364 pendingExits = prevPending; 2365 nextadr = nextadrPrev; 2366 } 2367 } 2368 2369 public void visitNewArray(JCNewArray tree) { 2370 scanExprs(tree.dims); 2371 scanExprs(tree.elems); 2372 } 2373 2374 public void visitAssert(JCAssert tree) { 2375 final Bits initsExit = new Bits(inits); 2376 final Bits uninitsExit = new Bits(uninits); 2377 scanCond(tree.cond); 2378 uninitsExit.andSet(uninitsWhenTrue); 2379 if (tree.detail != null) { 2380 inits.assign(initsWhenFalse); 2381 uninits.assign(uninitsWhenFalse); 2382 scanExpr(tree.detail); 2383 } 2384 inits.assign(initsExit); 2385 uninits.assign(uninitsExit); 2386 } 2387 2388 public void visitAssign(JCAssign tree) { 2389 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2390 if (!isIdentOrThisDotIdent(lhs)) 2391 scanExpr(lhs); 2392 scanExpr(tree.rhs); 2393 letInit(lhs); 2394 } 2395 private boolean isIdentOrThisDotIdent(JCTree lhs) { 2396 if (lhs.hasTag(IDENT)) 2397 return true; 2398 if (!lhs.hasTag(SELECT)) 2399 return false; 2400 2401 JCFieldAccess fa = (JCFieldAccess)lhs; 2402 return fa.selected.hasTag(IDENT) && 2403 ((JCIdent)fa.selected).name == names._this; 2404 } 2405 2406 // check fields accessed through this.<field> are definitely 2407 // assigned before reading their value 2408 public void visitSelect(JCFieldAccess tree) { 2409 super.visitSelect(tree); 2410 JCTree sel = TreeInfo.skipParens(tree.selected); 2411 if (enforceThisDotInit && 2412 sel.hasTag(IDENT) && 2413 ((JCIdent)sel).name == names._this && 2414 tree.sym.kind == VAR) { 2415 checkInit(tree.pos(), (VarSymbol)tree.sym); 2416 } 2417 } 2418 2419 public void visitAssignop(JCAssignOp tree) { 2420 scanExpr(tree.lhs); 2421 scanExpr(tree.rhs); 2422 letInit(tree.lhs); 2423 } 2424 2425 public void visitUnary(JCUnary tree) { 2426 switch (tree.getTag()) { 2427 case NOT: 2428 scanCond(tree.arg); 2429 final Bits t = new Bits(initsWhenFalse); 2430 initsWhenFalse.assign(initsWhenTrue); 2431 initsWhenTrue.assign(t); 2432 t.assign(uninitsWhenFalse); 2433 uninitsWhenFalse.assign(uninitsWhenTrue); 2434 uninitsWhenTrue.assign(t); 2435 break; 2436 case PREINC: case POSTINC: 2437 case PREDEC: case POSTDEC: 2438 scanExpr(tree.arg); 2439 letInit(tree.arg); 2440 break; 2441 default: 2442 scanExpr(tree.arg); 2443 } 2444 } 2445 2446 public void visitBinary(JCBinary tree) { 2447 switch (tree.getTag()) { 2448 case AND: 2449 scanCond(tree.lhs); 2450 final Bits initsWhenFalseLeft = new Bits(initsWhenFalse); 2451 final Bits uninitsWhenFalseLeft = new Bits(uninitsWhenFalse); 2452 inits.assign(initsWhenTrue); 2453 uninits.assign(uninitsWhenTrue); 2454 scanCond(tree.rhs); 2455 initsWhenFalse.andSet(initsWhenFalseLeft); 2456 uninitsWhenFalse.andSet(uninitsWhenFalseLeft); 2457 break; 2458 case OR: 2459 scanCond(tree.lhs); 2460 final Bits initsWhenTrueLeft = new Bits(initsWhenTrue); 2461 final Bits uninitsWhenTrueLeft = new Bits(uninitsWhenTrue); 2462 inits.assign(initsWhenFalse); 2463 uninits.assign(uninitsWhenFalse); 2464 scanCond(tree.rhs); 2465 initsWhenTrue.andSet(initsWhenTrueLeft); 2466 uninitsWhenTrue.andSet(uninitsWhenTrueLeft); 2467 break; 2468 default: 2469 scanExpr(tree.lhs); 2470 scanExpr(tree.rhs); 2471 } 2472 } 2473 2474 public void visitIdent(JCIdent tree) { 2475 if (tree.sym.kind == VAR) { 2476 checkInit(tree.pos(), (VarSymbol)tree.sym); 2477 referenced(tree.sym); 2478 } 2479 } 2480 2481 void referenced(Symbol sym) { 2482 unrefdResources.remove(sym); 2483 } 2484 2485 public void visitAnnotatedType(JCAnnotatedType tree) { 2486 // annotations don't get scanned 2487 tree.underlyingType.accept(this); 2488 } 2489 2490 public void visitModuleDef(JCModuleDecl tree) { 2491 // Do nothing for modules 2492 } 2493 2494 /************************************************************************** 2495 * main method 2496 *************************************************************************/ 2497 2498 /** Perform definite assignment/unassignment analysis on a tree. 2499 */ 2500 public void analyzeTree(Env<?> env) { 2501 analyzeTree(env, env.tree); 2502 } 2503 2504 public void analyzeTree(Env<?> env, JCTree tree) { 2505 try { 2506 startPos = tree.pos().getStartPosition(); 2507 2508 if (vardecls == null) 2509 vardecls = new JCVariableDecl[32]; 2510 else 2511 for (int i=0; i<vardecls.length; i++) 2512 vardecls[i] = null; 2513 firstadr = 0; 2514 nextadr = 0; 2515 pendingExits = new ListBuffer<>(); 2516 this.classDef = null; 2517 unrefdResources = WriteableScope.create(env.enclClass.sym); 2518 scan(tree); 2519 } finally { 2520 // note that recursive invocations of this method fail hard 2521 startPos = -1; 2522 resetBits(inits, uninits, uninitsTry, initsWhenTrue, 2523 initsWhenFalse, uninitsWhenTrue, uninitsWhenFalse); 2524 if (vardecls != null) { 2525 for (int i=0; i<vardecls.length; i++) 2526 vardecls[i] = null; 2527 } 2528 firstadr = 0; 2529 nextadr = 0; 2530 pendingExits = null; 2531 this.classDef = null; 2532 unrefdResources = null; 2533 } 2534 } 2535 } 2536 2537 /** 2538 * This pass implements the last step of the dataflow analysis, namely 2539 * the effectively-final analysis check. This checks that every local variable 2540 * reference from a lambda body/local inner class is either final or effectively final. 2541 * Additional this also checks that every variable that is used as an operand to 2542 * try-with-resources is final or effectively final. 2543 * As effectively final variables are marked as such during DA/DU, this pass must run after 2544 * AssignAnalyzer. 2545 */ 2546 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { 2547 2548 JCTree currentTree; //local class or lambda 2549 2550 @Override 2551 void markDead() { 2552 //do nothing 2553 } 2554 2555 @SuppressWarnings("fallthrough") 2556 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { 2557 if (currentTree != null && 2558 sym.owner.kind == MTH && 2559 sym.pos < currentTree.getStartPosition()) { 2560 switch (currentTree.getTag()) { 2561 case CLASSDEF: 2562 if (!allowEffectivelyFinalInInnerClasses) { 2563 if ((sym.flags() & FINAL) == 0) { 2564 reportInnerClsNeedsFinalError(pos, sym); 2565 } 2566 break; 2567 } 2568 case LAMBDA: 2569 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { 2570 reportEffectivelyFinalError(pos, sym); 2571 } 2572 } 2573 } 2574 } 2575 2576 @SuppressWarnings("fallthrough") 2577 void letInit(JCTree tree) { 2578 tree = TreeInfo.skipParens(tree); 2579 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { 2580 Symbol sym = TreeInfo.symbol(tree); 2581 if (currentTree != null && 2582 sym.kind == VAR && 2583 sym.owner.kind == MTH && 2584 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { 2585 switch (currentTree.getTag()) { 2586 case CLASSDEF: 2587 if (!allowEffectivelyFinalInInnerClasses) { 2588 reportInnerClsNeedsFinalError(tree, sym); 2589 break; 2590 } 2591 case LAMBDA: 2592 reportEffectivelyFinalError(tree, sym); 2593 } 2594 } 2595 } 2596 } 2597 2598 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { 2599 String subKey = currentTree.hasTag(LAMBDA) ? 2600 "lambda" : "inner.cls"; 2601 log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey)); 2602 } 2603 2604 void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) { 2605 log.error(pos, 2606 "local.var.accessed.from.icls.needs.final", 2607 sym); 2608 } 2609 2610 /************************************************************************* 2611 * Visitor methods for statements and definitions 2612 *************************************************************************/ 2613 2614 /* ------------ Visitor methods for various sorts of trees -------------*/ 2615 2616 public void visitClassDef(JCClassDecl tree) { 2617 JCTree prevTree = currentTree; 2618 try { 2619 currentTree = tree.sym.isLocal() ? tree : null; 2620 super.visitClassDef(tree); 2621 } finally { 2622 currentTree = prevTree; 2623 } 2624 } 2625 2626 @Override 2627 public void visitLambda(JCLambda tree) { 2628 JCTree prevTree = currentTree; 2629 try { 2630 currentTree = tree; 2631 super.visitLambda(tree); 2632 } finally { 2633 currentTree = prevTree; 2634 } 2635 } 2636 2637 @Override 2638 public void visitIdent(JCIdent tree) { 2639 if (tree.sym.kind == VAR) { 2640 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); 2641 } 2642 } 2643 2644 public void visitAssign(JCAssign tree) { 2645 JCTree lhs = TreeInfo.skipParens(tree.lhs); 2646 if (!(lhs instanceof JCIdent)) { 2647 scan(lhs); 2648 } 2649 scan(tree.rhs); 2650 letInit(lhs); 2651 } 2652 2653 public void visitAssignop(JCAssignOp tree) { 2654 scan(tree.lhs); 2655 scan(tree.rhs); 2656 letInit(tree.lhs); 2657 } 2658 2659 public void visitUnary(JCUnary tree) { 2660 switch (tree.getTag()) { 2661 case PREINC: case POSTINC: 2662 case PREDEC: case POSTDEC: 2663 scan(tree.arg); 2664 letInit(tree.arg); 2665 break; 2666 default: 2667 scan(tree.arg); 2668 } 2669 } 2670 2671 public void visitTry(JCTry tree) { 2672 for (JCTree resource : tree.resources) { 2673 if (!resource.hasTag(VARDEF)) { 2674 Symbol var = TreeInfo.symbol(resource); 2675 if (var != null && (var.flags() & (FINAL | EFFECTIVELY_FINAL)) == 0) { 2676 log.error(resource.pos(), "try.with.resources.expr.effectively.final.var", var); 2677 } 2678 } 2679 } 2680 super.visitTry(tree); 2681 } 2682 2683 public void visitModuleDef(JCModuleDecl tree) { 2684 // Do nothing for modules 2685 } 2686 2687 /************************************************************************** 2688 * main method 2689 *************************************************************************/ 2690 2691 /** Perform definite assignment/unassignment analysis on a tree. 2692 */ 2693 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { 2694 analyzeTree(env, env.tree, make); 2695 } 2696 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { 2697 try { 2698 attrEnv = env; 2699 Flow.this.make = make; 2700 pendingExits = new ListBuffer<>(); 2701 scan(tree); 2702 } finally { 2703 pendingExits = null; 2704 Flow.this.make = null; 2705 } 2706 } 2707 } 2708} 2709