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