186 private final Symtab syms; |
188 private final Symtab syms; |
187 private final Types types; |
189 private final Types types; |
188 private final Check chk; |
190 private final Check chk; |
189 private TreeMaker make; |
191 private TreeMaker make; |
190 private final Resolve rs; |
192 private final Resolve rs; |
|
193 private final JCDiagnostic.Factory diags; |
191 private Env<AttrContext> attrEnv; |
194 private Env<AttrContext> attrEnv; |
192 private Lint lint; |
195 private Lint lint; |
193 private final boolean allowImprovedRethrowAnalysis; |
196 private final boolean allowImprovedRethrowAnalysis; |
194 private final boolean allowImprovedCatchAnalysis; |
197 private final boolean allowImprovedCatchAnalysis; |
|
198 private final boolean allowEffectivelyFinalInInnerClasses; |
195 |
199 |
196 public static Flow instance(Context context) { |
200 public static Flow instance(Context context) { |
197 Flow instance = context.get(flowKey); |
201 Flow instance = context.get(flowKey); |
198 if (instance == null) |
202 if (instance == null) |
199 instance = new Flow(context); |
203 instance = new Flow(context); |
200 return instance; |
204 return instance; |
201 } |
205 } |
202 |
206 |
203 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
207 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
|
208 new AliveAnalyzer().analyzeTree(env, make); |
|
209 new AssignAnalyzer().analyzeTree(env, make); |
204 new FlowAnalyzer().analyzeTree(env, make); |
210 new FlowAnalyzer().analyzeTree(env, make); |
205 new AssignAnalyzer().analyzeTree(env, make); |
211 new CaptureAnalyzer().analyzeTree(env, make); |
|
212 } |
|
213 |
|
214 /** |
|
215 * Definite assignment scan mode |
|
216 */ |
|
217 enum FlowKind { |
|
218 /** |
|
219 * This is the normal DA/DU analysis mode |
|
220 */ |
|
221 NORMAL("var.might.already.be.assigned", false), |
|
222 /** |
|
223 * This is the speculative DA/DU analysis mode used to speculatively |
|
224 * derive assertions within loop bodies |
|
225 */ |
|
226 SPECULATIVE_LOOP("var.might.be.assigned.in.loop", true); |
|
227 |
|
228 String errKey; |
|
229 boolean isFinal; |
|
230 |
|
231 FlowKind(String errKey, boolean isFinal) { |
|
232 this.errKey = errKey; |
|
233 this.isFinal = isFinal; |
|
234 } |
|
235 |
|
236 boolean isFinal() { |
|
237 return isFinal; |
|
238 } |
206 } |
239 } |
207 |
240 |
208 protected Flow(Context context) { |
241 protected Flow(Context context) { |
209 context.put(flowKey, this); |
242 context.put(flowKey, this); |
210 names = Names.instance(context); |
243 names = Names.instance(context); |
307 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); |
346 return resolveJump(tree, oldPendingExits, JumpKind.BREAK); |
308 } |
347 } |
309 } |
348 } |
310 |
349 |
311 /** |
350 /** |
312 * This pass implements the first two steps of the dataflow analysis: |
351 * This pass implements the first step of the dataflow analysis, namely |
313 * (i) liveness analysis checks that every statement is reachable and (ii) |
352 * the liveness analysis check. This checks that every statement is reachable. |
314 * exception analysis to ensure that every checked exception that is |
353 * The output of this analysis pass are used by other analyzers. This analyzer |
315 * thrown is declared or caught. |
354 * sets the 'finallyCanCompleteNormally' field in the JCTry class. |
|
355 */ |
|
356 class AliveAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { |
|
357 |
|
358 /** A flag that indicates whether the last statement could |
|
359 * complete normally. |
|
360 */ |
|
361 private boolean alive; |
|
362 |
|
363 @Override |
|
364 void markDead() { |
|
365 alive = false; |
|
366 } |
|
367 |
|
368 /************************************************************************* |
|
369 * Visitor methods for statements and definitions |
|
370 *************************************************************************/ |
|
371 |
|
372 /** Analyze a definition. |
|
373 */ |
|
374 void scanDef(JCTree tree) { |
|
375 scanStat(tree); |
|
376 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { |
|
377 log.error(tree.pos(), |
|
378 "initializer.must.be.able.to.complete.normally"); |
|
379 } |
|
380 } |
|
381 |
|
382 /** Analyze a statement. Check that statement is reachable. |
|
383 */ |
|
384 void scanStat(JCTree tree) { |
|
385 if (!alive && tree != null) { |
|
386 log.error(tree.pos(), "unreachable.stmt"); |
|
387 if (!tree.hasTag(SKIP)) alive = true; |
|
388 } |
|
389 scan(tree); |
|
390 } |
|
391 |
|
392 /** Analyze list of statements. |
|
393 */ |
|
394 void scanStats(List<? extends JCStatement> trees) { |
|
395 if (trees != null) |
|
396 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) |
|
397 scanStat(l.head); |
|
398 } |
|
399 |
|
400 /* ------------ Visitor methods for various sorts of trees -------------*/ |
|
401 |
|
402 public void visitClassDef(JCClassDecl tree) { |
|
403 if (tree.sym == null) return; |
|
404 boolean alivePrev = alive; |
|
405 ListBuffer<PendingExit> pendingExitsPrev = pendingExits; |
|
406 Lint lintPrev = lint; |
|
407 |
|
408 pendingExits = new ListBuffer<PendingExit>(); |
|
409 lint = lint.augment(tree.sym.attributes_field); |
|
410 |
|
411 try { |
|
412 // process all the static initializers |
|
413 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
414 if (!l.head.hasTag(METHODDEF) && |
|
415 (TreeInfo.flags(l.head) & STATIC) != 0) { |
|
416 scanDef(l.head); |
|
417 } |
|
418 } |
|
419 |
|
420 // process all the instance initializers |
|
421 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
422 if (!l.head.hasTag(METHODDEF) && |
|
423 (TreeInfo.flags(l.head) & STATIC) == 0) { |
|
424 scanDef(l.head); |
|
425 } |
|
426 } |
|
427 |
|
428 // process all the methods |
|
429 for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) { |
|
430 if (l.head.hasTag(METHODDEF)) { |
|
431 scan(l.head); |
|
432 } |
|
433 } |
|
434 } finally { |
|
435 pendingExits = pendingExitsPrev; |
|
436 alive = alivePrev; |
|
437 lint = lintPrev; |
|
438 } |
|
439 } |
|
440 |
|
441 public void visitMethodDef(JCMethodDecl tree) { |
|
442 if (tree.body == null) return; |
|
443 Lint lintPrev = lint; |
|
444 |
|
445 lint = lint.augment(tree.sym.attributes_field); |
|
446 |
|
447 Assert.check(pendingExits.isEmpty()); |
|
448 |
|
449 try { |
|
450 alive = true; |
|
451 scanStat(tree.body); |
|
452 |
|
453 if (alive && tree.sym.type.getReturnType().tag != VOID) |
|
454 log.error(TreeInfo.diagEndPos(tree.body), "missing.ret.stmt"); |
|
455 |
|
456 List<PendingExit> exits = pendingExits.toList(); |
|
457 pendingExits = new ListBuffer<PendingExit>(); |
|
458 while (exits.nonEmpty()) { |
|
459 PendingExit exit = exits.head; |
|
460 exits = exits.tail; |
|
461 Assert.check(exit.tree.hasTag(RETURN)); |
|
462 } |
|
463 } finally { |
|
464 lint = lintPrev; |
|
465 } |
|
466 } |
|
467 |
|
468 public void visitVarDef(JCVariableDecl tree) { |
|
469 if (tree.init != null) { |
|
470 Lint lintPrev = lint; |
|
471 lint = lint.augment(tree.sym.attributes_field); |
|
472 try{ |
|
473 scan(tree.init); |
|
474 } finally { |
|
475 lint = lintPrev; |
|
476 } |
|
477 } |
|
478 } |
|
479 |
|
480 public void visitBlock(JCBlock tree) { |
|
481 scanStats(tree.stats); |
|
482 } |
|
483 |
|
484 public void visitDoLoop(JCDoWhileLoop tree) { |
|
485 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
486 pendingExits = new ListBuffer<PendingExit>(); |
|
487 scanStat(tree.body); |
|
488 alive |= resolveContinues(tree); |
|
489 scan(tree.cond); |
|
490 alive = alive && !tree.cond.type.isTrue(); |
|
491 alive |= resolveBreaks(tree, prevPendingExits); |
|
492 } |
|
493 |
|
494 public void visitWhileLoop(JCWhileLoop tree) { |
|
495 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
496 pendingExits = new ListBuffer<PendingExit>(); |
|
497 scan(tree.cond); |
|
498 alive = !tree.cond.type.isFalse(); |
|
499 scanStat(tree.body); |
|
500 alive |= resolveContinues(tree); |
|
501 alive = resolveBreaks(tree, prevPendingExits) || |
|
502 !tree.cond.type.isTrue(); |
|
503 } |
|
504 |
|
505 public void visitForLoop(JCForLoop tree) { |
|
506 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
507 scanStats(tree.init); |
|
508 pendingExits = new ListBuffer<PendingExit>(); |
|
509 if (tree.cond != null) { |
|
510 scan(tree.cond); |
|
511 alive = !tree.cond.type.isFalse(); |
|
512 } else { |
|
513 alive = true; |
|
514 } |
|
515 scanStat(tree.body); |
|
516 alive |= resolveContinues(tree); |
|
517 scan(tree.step); |
|
518 alive = resolveBreaks(tree, prevPendingExits) || |
|
519 tree.cond != null && !tree.cond.type.isTrue(); |
|
520 } |
|
521 |
|
522 public void visitForeachLoop(JCEnhancedForLoop tree) { |
|
523 visitVarDef(tree.var); |
|
524 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
525 scan(tree.expr); |
|
526 pendingExits = new ListBuffer<PendingExit>(); |
|
527 scanStat(tree.body); |
|
528 alive |= resolveContinues(tree); |
|
529 resolveBreaks(tree, prevPendingExits); |
|
530 alive = true; |
|
531 } |
|
532 |
|
533 public void visitLabelled(JCLabeledStatement tree) { |
|
534 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
535 pendingExits = new ListBuffer<PendingExit>(); |
|
536 scanStat(tree.body); |
|
537 alive |= resolveBreaks(tree, prevPendingExits); |
|
538 } |
|
539 |
|
540 public void visitSwitch(JCSwitch tree) { |
|
541 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
542 pendingExits = new ListBuffer<PendingExit>(); |
|
543 scan(tree.selector); |
|
544 boolean hasDefault = false; |
|
545 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
|
546 alive = true; |
|
547 JCCase c = l.head; |
|
548 if (c.pat == null) |
|
549 hasDefault = true; |
|
550 else |
|
551 scan(c.pat); |
|
552 scanStats(c.stats); |
|
553 // Warn about fall-through if lint switch fallthrough enabled. |
|
554 if (alive && |
|
555 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && |
|
556 c.stats.nonEmpty() && l.tail.nonEmpty()) |
|
557 log.warning(Lint.LintCategory.FALLTHROUGH, |
|
558 l.tail.head.pos(), |
|
559 "possible.fall-through.into.case"); |
|
560 } |
|
561 if (!hasDefault) { |
|
562 alive = true; |
|
563 } |
|
564 alive |= resolveBreaks(tree, prevPendingExits); |
|
565 } |
|
566 |
|
567 public void visitTry(JCTry tree) { |
|
568 ListBuffer<PendingExit> prevPendingExits = pendingExits; |
|
569 pendingExits = new ListBuffer<PendingExit>(); |
|
570 for (JCTree resource : tree.resources) { |
|
571 if (resource instanceof JCVariableDecl) { |
|
572 JCVariableDecl vdecl = (JCVariableDecl) resource; |
|
573 visitVarDef(vdecl); |
|
574 } else if (resource instanceof JCExpression) { |
|
575 scan((JCExpression) resource); |
|
576 } else { |
|
577 throw new AssertionError(tree); // parser error |
|
578 } |
|
579 } |
|
580 |
|
581 scanStat(tree.body); |
|
582 boolean aliveEnd = alive; |
|
583 |
|
584 for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { |
|
585 alive = true; |
|
586 JCVariableDecl param = l.head.param; |
|
587 scan(param); |
|
588 scanStat(l.head.body); |
|
589 aliveEnd |= alive; |
|
590 } |
|
591 if (tree.finalizer != null) { |
|
592 ListBuffer<PendingExit> exits = pendingExits; |
|
593 pendingExits = prevPendingExits; |
|
594 alive = true; |
|
595 scanStat(tree.finalizer); |
|
596 tree.finallyCanCompleteNormally = alive; |
|
597 if (!alive) { |
|
598 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { |
|
599 log.warning(Lint.LintCategory.FINALLY, |
|
600 TreeInfo.diagEndPos(tree.finalizer), |
|
601 "finally.cannot.complete"); |
|
602 } |
|
603 } else { |
|
604 while (exits.nonEmpty()) { |
|
605 pendingExits.append(exits.next()); |
|
606 } |
|
607 alive = aliveEnd; |
|
608 } |
|
609 } else { |
|
610 alive = aliveEnd; |
|
611 ListBuffer<PendingExit> exits = pendingExits; |
|
612 pendingExits = prevPendingExits; |
|
613 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
|
614 } |
|
615 } |
|
616 |
|
617 @Override |
|
618 public void visitIf(JCIf tree) { |
|
619 scan(tree.cond); |
|
620 scanStat(tree.thenpart); |
|
621 if (tree.elsepart != null) { |
|
622 boolean aliveAfterThen = alive; |
|
623 alive = true; |
|
624 scanStat(tree.elsepart); |
|
625 alive = alive | aliveAfterThen; |
|
626 } else { |
|
627 alive = true; |
|
628 } |
|
629 } |
|
630 |
|
631 public void visitBreak(JCBreak tree) { |
|
632 recordExit(tree, new PendingExit(tree)); |
|
633 } |
|
634 |
|
635 public void visitContinue(JCContinue tree) { |
|
636 recordExit(tree, new PendingExit(tree)); |
|
637 } |
|
638 |
|
639 public void visitReturn(JCReturn tree) { |
|
640 scan(tree.expr); |
|
641 recordExit(tree, new PendingExit(tree)); |
|
642 } |
|
643 |
|
644 public void visitThrow(JCThrow tree) { |
|
645 scan(tree.expr); |
|
646 markDead(); |
|
647 } |
|
648 |
|
649 public void visitApply(JCMethodInvocation tree) { |
|
650 scan(tree.meth); |
|
651 scan(tree.args); |
|
652 } |
|
653 |
|
654 public void visitNewClass(JCNewClass tree) { |
|
655 scan(tree.encl); |
|
656 scan(tree.args); |
|
657 if (tree.def != null) { |
|
658 scan(tree.def); |
|
659 } |
|
660 } |
|
661 |
|
662 public void visitTopLevel(JCCompilationUnit tree) { |
|
663 // Do nothing for TopLevel since each class is visited individually |
|
664 } |
|
665 |
|
666 /************************************************************************** |
|
667 * main method |
|
668 *************************************************************************/ |
|
669 |
|
670 /** Perform definite assignment/unassignment analysis on a tree. |
|
671 */ |
|
672 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
|
673 try { |
|
674 attrEnv = env; |
|
675 Flow.this.make = make; |
|
676 pendingExits = new ListBuffer<PendingExit>(); |
|
677 alive = true; |
|
678 scan(env.tree); |
|
679 } finally { |
|
680 pendingExits = null; |
|
681 Flow.this.make = null; |
|
682 } |
|
683 } |
|
684 } |
|
685 |
|
686 /** |
|
687 * This pass implements the second step of the dataflow analysis, namely |
|
688 * the exception analysis. This is to ensure that every checked exception that is |
|
689 * thrown is declared or caught. The analyzer uses some info that has been set by |
|
690 * the liveliness analyzer. |
316 */ |
691 */ |
317 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { |
692 class FlowAnalyzer extends BaseAnalyzer<FlowAnalyzer.FlowPendingExit> { |
318 |
693 |
319 /** A flag that indicates whether the last statement could |
694 /** A flag that indicates whether the last statement could |
320 * complete normally. |
695 * complete normally. |
321 */ |
696 */ |
322 private boolean alive; |
|
323 |
|
324 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
697 HashMap<Symbol, List<Type>> preciseRethrowTypes; |
325 |
698 |
326 /** The current class being defined. |
699 /** The current class being defined. |
327 */ |
700 */ |
328 JCClassDecl classDef; |
701 JCClassDecl classDef; |
393 |
764 |
394 /************************************************************************* |
765 /************************************************************************* |
395 * Visitor methods for statements and definitions |
766 * Visitor methods for statements and definitions |
396 *************************************************************************/ |
767 *************************************************************************/ |
397 |
768 |
398 /** Analyze a definition. |
|
399 */ |
|
400 void scanDef(JCTree tree) { |
|
401 scanStat(tree); |
|
402 if (tree != null && tree.hasTag(JCTree.Tag.BLOCK) && !alive) { |
|
403 log.error(tree.pos(), |
|
404 "initializer.must.be.able.to.complete.normally"); |
|
405 } |
|
406 } |
|
407 |
|
408 /** Analyze a statement. Check that statement is reachable. |
|
409 */ |
|
410 void scanStat(JCTree tree) { |
|
411 if (!alive && tree != null) { |
|
412 log.error(tree.pos(), "unreachable.stmt"); |
|
413 if (!tree.hasTag(SKIP)) alive = true; |
|
414 } |
|
415 scan(tree); |
|
416 } |
|
417 |
|
418 /** Analyze list of statements. |
|
419 */ |
|
420 void scanStats(List<? extends JCStatement> trees) { |
|
421 if (trees != null) |
|
422 for (List<? extends JCStatement> l = trees; l.nonEmpty(); l = l.tail) |
|
423 scanStat(l.head); |
|
424 } |
|
425 |
|
426 /* ------------ Visitor methods for various sorts of trees -------------*/ |
769 /* ------------ Visitor methods for various sorts of trees -------------*/ |
427 |
770 |
428 public void visitClassDef(JCClassDecl tree) { |
771 public void visitClassDef(JCClassDecl tree) { |
429 if (tree.sym == null) return; |
772 if (tree.sym == null) return; |
430 |
773 |
431 JCClassDecl classDefPrev = classDef; |
774 JCClassDecl classDefPrev = classDef; |
432 List<Type> thrownPrev = thrown; |
775 List<Type> thrownPrev = thrown; |
433 List<Type> caughtPrev = caught; |
776 List<Type> caughtPrev = caught; |
434 boolean alivePrev = alive; |
|
435 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; |
777 ListBuffer<FlowPendingExit> pendingExitsPrev = pendingExits; |
436 Lint lintPrev = lint; |
778 Lint lintPrev = lint; |
437 |
779 |
438 pendingExits = new ListBuffer<FlowPendingExit>(); |
780 pendingExits = new ListBuffer<FlowPendingExit>(); |
439 if (tree.name != names.empty) { |
781 if (tree.name != names.empty) { |
573 } |
910 } |
574 } |
911 } |
575 } |
912 } |
576 |
913 |
577 public void visitBlock(JCBlock tree) { |
914 public void visitBlock(JCBlock tree) { |
578 scanStats(tree.stats); |
915 scan(tree.stats); |
579 } |
916 } |
580 |
917 |
581 public void visitDoLoop(JCDoWhileLoop tree) { |
918 public void visitDoLoop(JCDoWhileLoop tree) { |
582 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
919 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
583 pendingExits = new ListBuffer<FlowPendingExit>(); |
920 pendingExits = new ListBuffer<FlowPendingExit>(); |
584 scanStat(tree.body); |
921 scan(tree.body); |
585 alive |= resolveContinues(tree); |
922 resolveContinues(tree); |
586 scan(tree.cond); |
923 scan(tree.cond); |
587 alive = alive && !tree.cond.type.isTrue(); |
924 resolveBreaks(tree, prevPendingExits); |
588 alive |= resolveBreaks(tree, prevPendingExits); |
|
589 } |
925 } |
590 |
926 |
591 public void visitWhileLoop(JCWhileLoop tree) { |
927 public void visitWhileLoop(JCWhileLoop tree) { |
592 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
928 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
593 pendingExits = new ListBuffer<FlowPendingExit>(); |
929 pendingExits = new ListBuffer<FlowPendingExit>(); |
594 scan(tree.cond); |
930 scan(tree.cond); |
595 alive = !tree.cond.type.isFalse(); |
931 scan(tree.body); |
596 scanStat(tree.body); |
932 resolveContinues(tree); |
597 alive |= resolveContinues(tree); |
933 resolveBreaks(tree, prevPendingExits); |
598 alive = resolveBreaks(tree, prevPendingExits) || |
|
599 !tree.cond.type.isTrue(); |
|
600 } |
934 } |
601 |
935 |
602 public void visitForLoop(JCForLoop tree) { |
936 public void visitForLoop(JCForLoop tree) { |
603 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
937 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
604 scanStats(tree.init); |
938 scan(tree.init); |
605 pendingExits = new ListBuffer<FlowPendingExit>(); |
939 pendingExits = new ListBuffer<FlowPendingExit>(); |
606 if (tree.cond != null) { |
940 if (tree.cond != null) { |
607 scan(tree.cond); |
941 scan(tree.cond); |
608 alive = !tree.cond.type.isFalse(); |
942 } |
609 } else { |
943 scan(tree.body); |
610 alive = true; |
944 resolveContinues(tree); |
611 } |
|
612 scanStat(tree.body); |
|
613 alive |= resolveContinues(tree); |
|
614 scan(tree.step); |
945 scan(tree.step); |
615 alive = resolveBreaks(tree, prevPendingExits) || |
946 resolveBreaks(tree, prevPendingExits); |
616 tree.cond != null && !tree.cond.type.isTrue(); |
|
617 } |
947 } |
618 |
948 |
619 public void visitForeachLoop(JCEnhancedForLoop tree) { |
949 public void visitForeachLoop(JCEnhancedForLoop tree) { |
620 visitVarDef(tree.var); |
950 visitVarDef(tree.var); |
621 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
951 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
622 scan(tree.expr); |
952 scan(tree.expr); |
623 pendingExits = new ListBuffer<FlowPendingExit>(); |
953 pendingExits = new ListBuffer<FlowPendingExit>(); |
624 scanStat(tree.body); |
954 scan(tree.body); |
625 alive |= resolveContinues(tree); |
955 resolveContinues(tree); |
626 resolveBreaks(tree, prevPendingExits); |
956 resolveBreaks(tree, prevPendingExits); |
627 alive = true; |
|
628 } |
957 } |
629 |
958 |
630 public void visitLabelled(JCLabeledStatement tree) { |
959 public void visitLabelled(JCLabeledStatement tree) { |
631 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
960 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
632 pendingExits = new ListBuffer<FlowPendingExit>(); |
961 pendingExits = new ListBuffer<FlowPendingExit>(); |
633 scanStat(tree.body); |
962 scan(tree.body); |
634 alive |= resolveBreaks(tree, prevPendingExits); |
963 resolveBreaks(tree, prevPendingExits); |
635 } |
964 } |
636 |
965 |
637 public void visitSwitch(JCSwitch tree) { |
966 public void visitSwitch(JCSwitch tree) { |
638 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
967 ListBuffer<FlowPendingExit> prevPendingExits = pendingExits; |
639 pendingExits = new ListBuffer<FlowPendingExit>(); |
968 pendingExits = new ListBuffer<FlowPendingExit>(); |
640 scan(tree.selector); |
969 scan(tree.selector); |
641 boolean hasDefault = false; |
|
642 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
970 for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) { |
643 alive = true; |
|
644 JCCase c = l.head; |
971 JCCase c = l.head; |
645 if (c.pat == null) |
972 if (c.pat != null) { |
646 hasDefault = true; |
|
647 else |
|
648 scan(c.pat); |
973 scan(c.pat); |
649 scanStats(c.stats); |
974 } |
650 // Warn about fall-through if lint switch fallthrough enabled. |
975 scan(c.stats); |
651 if (alive && |
976 } |
652 lint.isEnabled(Lint.LintCategory.FALLTHROUGH) && |
977 resolveBreaks(tree, prevPendingExits); |
653 c.stats.nonEmpty() && l.tail.nonEmpty()) |
|
654 log.warning(Lint.LintCategory.FALLTHROUGH, |
|
655 l.tail.head.pos(), |
|
656 "possible.fall-through.into.case"); |
|
657 } |
|
658 if (!hasDefault) { |
|
659 alive = true; |
|
660 } |
|
661 alive |= resolveBreaks(tree, prevPendingExits); |
|
662 } |
978 } |
663 |
979 |
664 public void visitTry(JCTry tree) { |
980 public void visitTry(JCTry tree) { |
665 List<Type> caughtPrev = caught; |
981 List<Type> caughtPrev = caught; |
666 List<Type> thrownPrev = thrown; |
982 List<Type> thrownPrev = thrown; |
733 caughtInTry = chk.incl(exc, caughtInTry); |
1047 caughtInTry = chk.incl(exc, caughtInTry); |
734 } |
1048 } |
735 } |
1049 } |
736 scan(param); |
1050 scan(param); |
737 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); |
1051 preciseRethrowTypes.put(param.sym, chk.intersect(ctypes, rethrownTypes)); |
738 scanStat(l.head.body); |
1052 scan(l.head.body); |
739 preciseRethrowTypes.remove(param.sym); |
1053 preciseRethrowTypes.remove(param.sym); |
740 aliveEnd |= alive; |
|
741 } |
1054 } |
742 if (tree.finalizer != null) { |
1055 if (tree.finalizer != null) { |
743 List<Type> savedThrown = thrown; |
1056 List<Type> savedThrown = thrown; |
744 thrown = List.nil(); |
1057 thrown = List.nil(); |
745 ListBuffer<FlowPendingExit> exits = pendingExits; |
1058 ListBuffer<FlowPendingExit> exits = pendingExits; |
746 pendingExits = prevPendingExits; |
1059 pendingExits = prevPendingExits; |
747 alive = true; |
1060 scan(tree.finalizer); |
748 scanStat(tree.finalizer); |
1061 if (!tree.finallyCanCompleteNormally) { |
749 tree.finallyCanCompleteNormally = alive; |
|
750 if (!alive) { |
|
751 // discard exits and exceptions from try and finally |
1062 // discard exits and exceptions from try and finally |
752 thrown = chk.union(thrown, thrownPrev); |
1063 thrown = chk.union(thrown, thrownPrev); |
753 if (lint.isEnabled(Lint.LintCategory.FINALLY)) { |
|
754 log.warning(Lint.LintCategory.FINALLY, |
|
755 TreeInfo.diagEndPos(tree.finalizer), |
|
756 "finally.cannot.complete"); |
|
757 } |
|
758 } else { |
1064 } else { |
759 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
1065 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
760 thrown = chk.union(thrown, savedThrown); |
1066 thrown = chk.union(thrown, savedThrown); |
761 // FIX: this doesn't preserve source order of exits in catch |
1067 // FIX: this doesn't preserve source order of exits in catch |
762 // versus finally! |
1068 // versus finally! |
763 while (exits.nonEmpty()) { |
1069 while (exits.nonEmpty()) { |
764 pendingExits.append(exits.next()); |
1070 pendingExits.append(exits.next()); |
765 } |
1071 } |
766 alive = aliveEnd; |
|
767 } |
1072 } |
768 } else { |
1073 } else { |
769 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
1074 thrown = chk.union(thrown, chk.diff(thrownInTry, caughtInTry)); |
770 alive = aliveEnd; |
|
771 ListBuffer<FlowPendingExit> exits = pendingExits; |
1075 ListBuffer<FlowPendingExit> exits = pendingExits; |
772 pendingExits = prevPendingExits; |
1076 pendingExits = prevPendingExits; |
773 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
1077 while (exits.nonEmpty()) pendingExits.append(exits.next()); |
774 } |
1078 } |
775 } |
1079 } |
776 |
1080 |
777 @Override |
1081 @Override |
778 public void visitIf(JCIf tree) { |
1082 public void visitIf(JCIf tree) { |
779 scan(tree.cond); |
1083 scan(tree.cond); |
780 scanStat(tree.thenpart); |
1084 scan(tree.thenpart); |
781 if (tree.elsepart != null) { |
1085 if (tree.elsepart != null) { |
782 boolean aliveAfterThen = alive; |
1086 scan(tree.elsepart); |
783 alive = true; |
|
784 scanStat(tree.elsepart); |
|
785 alive = alive | aliveAfterThen; |
|
786 } else { |
|
787 alive = true; |
|
788 } |
1087 } |
789 } |
1088 } |
790 |
1089 |
791 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { |
1090 void checkCaughtType(DiagnosticPosition pos, Type exc, List<Type> thrownInTry, List<Type> caughtInTry) { |
792 if (chk.subset(exc, caughtInTry)) { |
1091 if (chk.subset(exc, caughtInTry)) { |
1028 |
1335 |
1029 /** Record an initialization of a trackable variable. |
1336 /** Record an initialization of a trackable variable. |
1030 */ |
1337 */ |
1031 void letInit(DiagnosticPosition pos, VarSymbol sym) { |
1338 void letInit(DiagnosticPosition pos, VarSymbol sym) { |
1032 if (sym.adr >= firstadr && trackable(sym)) { |
1339 if (sym.adr >= firstadr && trackable(sym)) { |
1033 if ((sym.flags() & FINAL) != 0) { |
1340 if ((sym.flags() & EFFECTIVELY_FINAL) != 0) { |
|
1341 if (!uninits.isMember(sym.adr)) { |
|
1342 //assignment targeting an effectively final variable |
|
1343 //makes the variable lose its status of effectively final |
|
1344 //if the variable is _not_ definitively unassigned |
|
1345 sym.flags_field &= ~EFFECTIVELY_FINAL; |
|
1346 } else { |
|
1347 uninit(sym); |
|
1348 } |
|
1349 } |
|
1350 else if ((sym.flags() & FINAL) != 0) { |
1034 if ((sym.flags() & PARAMETER) != 0) { |
1351 if ((sym.flags() & PARAMETER) != 0) { |
1035 if ((sym.flags() & UNION) != 0) { //multi-catch parameter |
1352 if ((sym.flags() & UNION) != 0) { //multi-catch parameter |
1036 log.error(pos, "multicatch.parameter.may.not.be.assigned", |
1353 log.error(pos, "multicatch.parameter.may.not.be.assigned", |
1037 sym); |
1354 sym); |
1038 } |
1355 } |
1039 else { |
1356 else { |
1040 log.error(pos, "final.parameter.may.not.be.assigned", |
1357 log.error(pos, "final.parameter.may.not.be.assigned", |
1041 sym); |
1358 sym); |
1042 } |
1359 } |
1043 } else if (!uninits.isMember(sym.adr)) { |
1360 } else if (!uninits.isMember(sym.adr)) { |
1044 log.error(pos, |
1361 log.error(pos, flowKind.errKey, sym); |
1045 loopPassTwo |
|
1046 ? "var.might.be.assigned.in.loop" |
|
1047 : "var.might.already.be.assigned", |
|
1048 sym); |
|
1049 } else if (!inits.isMember(sym.adr)) { |
|
1050 // reachable assignment |
|
1051 uninits.excl(sym.adr); |
|
1052 uninitsTry.excl(sym.adr); |
|
1053 } else { |
1362 } else { |
1054 //log.rawWarning(pos, "unreachable assignment");//DEBUG |
1363 uninit(sym); |
1055 uninits.excl(sym.adr); |
|
1056 } |
1364 } |
1057 } |
1365 } |
1058 inits.incl(sym.adr); |
1366 inits.incl(sym.adr); |
1059 } else if ((sym.flags() & FINAL) != 0) { |
1367 } else if ((sym.flags() & FINAL) != 0) { |
1060 log.error(pos, "var.might.already.be.assigned", sym); |
1368 log.error(pos, "var.might.already.be.assigned", sym); |
1061 } |
1369 } |
1062 } |
1370 } |
|
1371 //where |
|
1372 void uninit(VarSymbol sym) { |
|
1373 if (!inits.isMember(sym.adr)) { |
|
1374 // reachable assignment |
|
1375 uninits.excl(sym.adr); |
|
1376 uninitsTry.excl(sym.adr); |
|
1377 } else { |
|
1378 //log.rawWarning(pos, "unreachable assignment");//DEBUG |
|
1379 uninits.excl(sym.adr); |
|
1380 } |
|
1381 } |
1063 |
1382 |
1064 /** If tree is either a simple name or of the form this.name or |
1383 /** If tree is either a simple name or of the form this.name or |
1065 * C.this.name, and tree represents a trackable variable, |
1384 * C.this.name, and tree represents a trackable variable, |
1066 * record an initialization of the variable. |
1385 * record an initialization of the variable. |
1067 */ |
1386 */ |
1306 nextadr = nextadrPrev; |
1625 nextadr = nextadrPrev; |
1307 } |
1626 } |
1308 |
1627 |
1309 public void visitDoLoop(JCDoWhileLoop tree) { |
1628 public void visitDoLoop(JCDoWhileLoop tree) { |
1310 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1629 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1311 boolean prevLoopPassTwo = loopPassTwo; |
1630 FlowKind prevFlowKind = flowKind; |
|
1631 flowKind = FlowKind.NORMAL; |
|
1632 Bits initsSkip = null; |
|
1633 Bits uninitsSkip = null; |
1312 pendingExits = new ListBuffer<AssignPendingExit>(); |
1634 pendingExits = new ListBuffer<AssignPendingExit>(); |
1313 int prevErrors = log.nerrors; |
1635 int prevErrors = log.nerrors; |
1314 do { |
1636 do { |
1315 Bits uninitsEntry = uninits.dup(); |
1637 Bits uninitsEntry = uninits.dup(); |
1316 uninitsEntry.excludeFrom(nextadr); |
1638 uninitsEntry.excludeFrom(nextadr); |
1317 scan(tree.body); |
1639 scan(tree.body); |
1318 resolveContinues(tree); |
1640 resolveContinues(tree); |
1319 scanCond(tree.cond); |
1641 scanCond(tree.cond); |
|
1642 if (!flowKind.isFinal()) { |
|
1643 initsSkip = initsWhenFalse; |
|
1644 uninitsSkip = uninitsWhenFalse; |
|
1645 } |
1320 if (log.nerrors != prevErrors || |
1646 if (log.nerrors != prevErrors || |
1321 loopPassTwo || |
1647 flowKind.isFinal() || |
1322 uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) |
1648 uninitsEntry.dup().diffSet(uninitsWhenTrue).nextBit(firstadr)==-1) |
1323 break; |
1649 break; |
1324 inits = initsWhenTrue; |
1650 inits = initsWhenTrue; |
1325 uninits = uninitsEntry.andSet(uninitsWhenTrue); |
1651 uninits = uninitsEntry.andSet(uninitsWhenTrue); |
1326 loopPassTwo = true; |
1652 flowKind = FlowKind.SPECULATIVE_LOOP; |
1327 } while (true); |
1653 } while (true); |
1328 loopPassTwo = prevLoopPassTwo; |
1654 flowKind = prevFlowKind; |
1329 inits = initsWhenFalse; |
1655 inits = initsSkip; |
1330 uninits = uninitsWhenFalse; |
1656 uninits = uninitsSkip; |
1331 resolveBreaks(tree, prevPendingExits); |
1657 resolveBreaks(tree, prevPendingExits); |
1332 } |
1658 } |
1333 |
1659 |
1334 public void visitWhileLoop(JCWhileLoop tree) { |
1660 public void visitWhileLoop(JCWhileLoop tree) { |
1335 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1661 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1336 boolean prevLoopPassTwo = loopPassTwo; |
1662 FlowKind prevFlowKind = flowKind; |
1337 Bits initsCond; |
1663 flowKind = FlowKind.NORMAL; |
1338 Bits uninitsCond; |
1664 Bits initsSkip = null; |
|
1665 Bits uninitsSkip = null; |
1339 pendingExits = new ListBuffer<AssignPendingExit>(); |
1666 pendingExits = new ListBuffer<AssignPendingExit>(); |
1340 int prevErrors = log.nerrors; |
1667 int prevErrors = log.nerrors; |
|
1668 Bits uninitsEntry = uninits.dup(); |
|
1669 uninitsEntry.excludeFrom(nextadr); |
1341 do { |
1670 do { |
1342 Bits uninitsEntry = uninits.dup(); |
|
1343 uninitsEntry.excludeFrom(nextadr); |
|
1344 scanCond(tree.cond); |
1671 scanCond(tree.cond); |
1345 initsCond = initsWhenFalse; |
1672 if (!flowKind.isFinal()) { |
1346 uninitsCond = uninitsWhenFalse; |
1673 initsSkip = initsWhenFalse; |
|
1674 uninitsSkip = uninitsWhenFalse; |
|
1675 } |
1347 inits = initsWhenTrue; |
1676 inits = initsWhenTrue; |
1348 uninits = uninitsWhenTrue; |
1677 uninits = uninitsWhenTrue; |
1349 scan(tree.body); |
1678 scan(tree.body); |
1350 resolveContinues(tree); |
1679 resolveContinues(tree); |
1351 if (log.nerrors != prevErrors || |
1680 if (log.nerrors != prevErrors || |
1352 loopPassTwo || |
1681 flowKind.isFinal() || |
1353 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
1682 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
1354 break; |
1683 break; |
1355 uninits = uninitsEntry.andSet(uninits); |
1684 uninits = uninitsEntry.andSet(uninits); |
1356 loopPassTwo = true; |
1685 flowKind = FlowKind.SPECULATIVE_LOOP; |
1357 } while (true); |
1686 } while (true); |
1358 loopPassTwo = prevLoopPassTwo; |
1687 flowKind = prevFlowKind; |
1359 inits = initsCond; |
1688 //a variable is DA/DU after the while statement, if it's DA/DU assuming the |
1360 uninits = uninitsCond; |
1689 //branch is not taken AND if it's DA/DU before any break statement |
|
1690 inits = initsSkip; |
|
1691 uninits = uninitsSkip; |
1361 resolveBreaks(tree, prevPendingExits); |
1692 resolveBreaks(tree, prevPendingExits); |
1362 } |
1693 } |
1363 |
1694 |
1364 public void visitForLoop(JCForLoop tree) { |
1695 public void visitForLoop(JCForLoop tree) { |
1365 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1696 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1366 boolean prevLoopPassTwo = loopPassTwo; |
1697 FlowKind prevFlowKind = flowKind; |
|
1698 flowKind = FlowKind.NORMAL; |
1367 int nextadrPrev = nextadr; |
1699 int nextadrPrev = nextadr; |
1368 scan(tree.init); |
1700 scan(tree.init); |
1369 Bits initsCond; |
1701 Bits initsSkip = null; |
1370 Bits uninitsCond; |
1702 Bits uninitsSkip = null; |
1371 pendingExits = new ListBuffer<AssignPendingExit>(); |
1703 pendingExits = new ListBuffer<AssignPendingExit>(); |
1372 int prevErrors = log.nerrors; |
1704 int prevErrors = log.nerrors; |
1373 do { |
1705 do { |
1374 Bits uninitsEntry = uninits.dup(); |
1706 Bits uninitsEntry = uninits.dup(); |
1375 uninitsEntry.excludeFrom(nextadr); |
1707 uninitsEntry.excludeFrom(nextadr); |
1376 if (tree.cond != null) { |
1708 if (tree.cond != null) { |
1377 scanCond(tree.cond); |
1709 scanCond(tree.cond); |
1378 initsCond = initsWhenFalse; |
1710 if (!flowKind.isFinal()) { |
1379 uninitsCond = uninitsWhenFalse; |
1711 initsSkip = initsWhenFalse; |
|
1712 uninitsSkip = uninitsWhenFalse; |
|
1713 } |
1380 inits = initsWhenTrue; |
1714 inits = initsWhenTrue; |
1381 uninits = uninitsWhenTrue; |
1715 uninits = uninitsWhenTrue; |
1382 } else { |
1716 } else if (!flowKind.isFinal()) { |
1383 initsCond = inits.dup(); |
1717 initsSkip = inits.dup(); |
1384 initsCond.inclRange(firstadr, nextadr); |
1718 initsSkip.inclRange(firstadr, nextadr); |
1385 uninitsCond = uninits.dup(); |
1719 uninitsSkip = uninits.dup(); |
1386 uninitsCond.inclRange(firstadr, nextadr); |
1720 uninitsSkip.inclRange(firstadr, nextadr); |
1387 } |
1721 } |
1388 scan(tree.body); |
1722 scan(tree.body); |
1389 resolveContinues(tree); |
1723 resolveContinues(tree); |
1390 scan(tree.step); |
1724 scan(tree.step); |
1391 if (log.nerrors != prevErrors || |
1725 if (log.nerrors != prevErrors || |
1392 loopPassTwo || |
1726 flowKind.isFinal() || |
1393 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
1727 uninitsEntry.dup().diffSet(uninits).nextBit(firstadr) == -1) |
1394 break; |
1728 break; |
1395 uninits = uninitsEntry.andSet(uninits); |
1729 uninits = uninitsEntry.andSet(uninits); |
1396 loopPassTwo = true; |
1730 flowKind = FlowKind.SPECULATIVE_LOOP; |
1397 } while (true); |
1731 } while (true); |
1398 loopPassTwo = prevLoopPassTwo; |
1732 flowKind = prevFlowKind; |
1399 inits = initsCond; |
1733 //a variable is DA/DU after a for loop, if it's DA/DU assuming the |
1400 uninits = uninitsCond; |
1734 //branch is not taken AND if it's DA/DU before any break statement |
|
1735 inits = initsSkip; |
|
1736 uninits = uninitsSkip; |
1401 resolveBreaks(tree, prevPendingExits); |
1737 resolveBreaks(tree, prevPendingExits); |
1402 nextadr = nextadrPrev; |
1738 nextadr = nextadrPrev; |
1403 } |
1739 } |
1404 |
1740 |
1405 public void visitForeachLoop(JCEnhancedForLoop tree) { |
1741 public void visitForeachLoop(JCEnhancedForLoop tree) { |
1406 visitVarDef(tree.var); |
1742 visitVarDef(tree.var); |
1407 |
1743 |
1408 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1744 ListBuffer<AssignPendingExit> prevPendingExits = pendingExits; |
1409 boolean prevLoopPassTwo = loopPassTwo; |
1745 FlowKind prevFlowKind = flowKind; |
|
1746 flowKind = FlowKind.NORMAL; |
1410 int nextadrPrev = nextadr; |
1747 int nextadrPrev = nextadr; |
1411 scan(tree.expr); |
1748 scan(tree.expr); |
1412 Bits initsStart = inits.dup(); |
1749 Bits initsStart = inits.dup(); |
1413 Bits uninitsStart = uninits.dup(); |
1750 Bits uninitsStart = uninits.dup(); |
1414 |
1751 |
1785 this.classDef = null; |
2128 this.classDef = null; |
1786 unrefdResources = null; |
2129 unrefdResources = null; |
1787 } |
2130 } |
1788 } |
2131 } |
1789 } |
2132 } |
|
2133 |
|
2134 /** |
|
2135 * This pass implements the last step of the dataflow analysis, namely |
|
2136 * the effectively-final analysis check. This checks that every local variable |
|
2137 * reference from a lambda body/local inner class is either final or effectively final. |
|
2138 * As effectively final variables are marked as such during DA/DU, this pass must run after |
|
2139 * AssignAnalyzer. |
|
2140 */ |
|
2141 class CaptureAnalyzer extends BaseAnalyzer<BaseAnalyzer.PendingExit> { |
|
2142 |
|
2143 JCTree currentTree; //local class or lambda |
|
2144 |
|
2145 @Override |
|
2146 void markDead() { |
|
2147 //do nothing |
|
2148 } |
|
2149 |
|
2150 @SuppressWarnings("fallthrough") |
|
2151 void checkEffectivelyFinal(DiagnosticPosition pos, VarSymbol sym) { |
|
2152 if (currentTree != null && |
|
2153 sym.owner.kind == MTH && |
|
2154 sym.pos < currentTree.getStartPosition()) { |
|
2155 switch (currentTree.getTag()) { |
|
2156 case CLASSDEF: |
|
2157 if (!allowEffectivelyFinalInInnerClasses) { |
|
2158 if ((sym.flags() & FINAL) == 0) { |
|
2159 reportInnerClsNeedsFinalError(pos, sym); |
|
2160 } |
|
2161 break; |
|
2162 } |
|
2163 case LAMBDA: |
|
2164 if ((sym.flags() & (EFFECTIVELY_FINAL | FINAL)) == 0) { |
|
2165 reportEffectivelyFinalError(pos, sym); |
|
2166 } |
|
2167 } |
|
2168 } |
|
2169 } |
|
2170 |
|
2171 @SuppressWarnings("fallthrough") |
|
2172 void letInit(JCTree tree) { |
|
2173 tree = TreeInfo.skipParens(tree); |
|
2174 if (tree.hasTag(IDENT) || tree.hasTag(SELECT)) { |
|
2175 Symbol sym = TreeInfo.symbol(tree); |
|
2176 if (currentTree != null && |
|
2177 sym.kind == VAR && |
|
2178 sym.owner.kind == MTH && |
|
2179 ((VarSymbol)sym).pos < currentTree.getStartPosition()) { |
|
2180 switch (currentTree.getTag()) { |
|
2181 case CLASSDEF: |
|
2182 if (!allowEffectivelyFinalInInnerClasses) { |
|
2183 reportInnerClsNeedsFinalError(tree, sym); |
|
2184 break; |
|
2185 } |
|
2186 case LAMBDA: |
|
2187 reportEffectivelyFinalError(tree, sym); |
|
2188 } |
|
2189 } |
|
2190 } |
|
2191 } |
|
2192 |
|
2193 void reportEffectivelyFinalError(DiagnosticPosition pos, Symbol sym) { |
|
2194 String subKey = currentTree.hasTag(LAMBDA) ? |
|
2195 "lambda" : "inner.cls"; |
|
2196 log.error(pos, "cant.ref.non.effectively.final.var", sym, diags.fragment(subKey)); |
|
2197 } |
|
2198 |
|
2199 void reportInnerClsNeedsFinalError(DiagnosticPosition pos, Symbol sym) { |
|
2200 log.error(pos, |
|
2201 "local.var.accessed.from.icls.needs.final", |
|
2202 sym); |
|
2203 } |
|
2204 |
|
2205 /************************************************************************* |
|
2206 * Visitor methods for statements and definitions |
|
2207 *************************************************************************/ |
|
2208 |
|
2209 /* ------------ Visitor methods for various sorts of trees -------------*/ |
|
2210 |
|
2211 public void visitClassDef(JCClassDecl tree) { |
|
2212 JCTree prevTree = currentTree; |
|
2213 try { |
|
2214 currentTree = tree.sym.isLocal() ? tree : null; |
|
2215 super.visitClassDef(tree); |
|
2216 } finally { |
|
2217 currentTree = prevTree; |
|
2218 } |
|
2219 } |
|
2220 |
|
2221 @Override |
|
2222 public void visitLambda(JCLambda tree) { |
|
2223 JCTree prevTree = currentTree; |
|
2224 try { |
|
2225 currentTree = tree; |
|
2226 super.visitLambda(tree); |
|
2227 } finally { |
|
2228 currentTree = prevTree; |
|
2229 } |
|
2230 } |
|
2231 |
|
2232 @Override |
|
2233 public void visitIdent(JCIdent tree) { |
|
2234 if (tree.sym.kind == VAR) { |
|
2235 checkEffectivelyFinal(tree, (VarSymbol)tree.sym); |
|
2236 } |
|
2237 } |
|
2238 |
|
2239 public void visitAssign(JCAssign tree) { |
|
2240 JCTree lhs = TreeInfo.skipParens(tree.lhs); |
|
2241 if (!(lhs instanceof JCIdent)) { |
|
2242 scan(lhs); |
|
2243 } |
|
2244 scan(tree.rhs); |
|
2245 letInit(lhs); |
|
2246 } |
|
2247 |
|
2248 public void visitAssignop(JCAssignOp tree) { |
|
2249 scan(tree.lhs); |
|
2250 scan(tree.rhs); |
|
2251 letInit(tree.lhs); |
|
2252 } |
|
2253 |
|
2254 public void visitUnary(JCUnary tree) { |
|
2255 switch (tree.getTag()) { |
|
2256 case PREINC: case POSTINC: |
|
2257 case PREDEC: case POSTDEC: |
|
2258 scan(tree.arg); |
|
2259 letInit(tree.arg); |
|
2260 break; |
|
2261 default: |
|
2262 scan(tree.arg); |
|
2263 } |
|
2264 } |
|
2265 |
|
2266 public void visitTopLevel(JCCompilationUnit tree) { |
|
2267 // Do nothing for TopLevel since each class is visited individually |
|
2268 } |
|
2269 |
|
2270 /************************************************************************** |
|
2271 * main method |
|
2272 *************************************************************************/ |
|
2273 |
|
2274 /** Perform definite assignment/unassignment analysis on a tree. |
|
2275 */ |
|
2276 public void analyzeTree(Env<AttrContext> env, TreeMaker make) { |
|
2277 analyzeTree(env, env.tree, make); |
|
2278 } |
|
2279 public void analyzeTree(Env<AttrContext> env, JCTree tree, TreeMaker make) { |
|
2280 try { |
|
2281 attrEnv = env; |
|
2282 Flow.this.make = make; |
|
2283 pendingExits = new ListBuffer<PendingExit>(); |
|
2284 scan(tree); |
|
2285 } finally { |
|
2286 pendingExits = null; |
|
2287 Flow.this.make = null; |
|
2288 } |
|
2289 } |
|
2290 } |
1790 } |
2291 } |