1 /* 2 * Copyright (c) 2014, 2017, 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 */ 25 26 package com.sun.tools.javac.comp; 27 28 import java.util.ArrayList; 29 30 import com.sun.source.tree.LambdaExpressionTree; 31 import com.sun.tools.javac.code.Source; 32 import com.sun.tools.javac.code.Source.Feature; 33 import com.sun.tools.javac.code.Type; 34 import com.sun.tools.javac.code.Types; 35 import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; 36 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 37 import com.sun.tools.javac.tree.JCTree; 38 import com.sun.tools.javac.tree.JCTree.JCBlock; 39 import com.sun.tools.javac.tree.JCTree.JCClassDecl; 40 import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; 41 import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; 42 import com.sun.tools.javac.tree.JCTree.JCForLoop; 43 import com.sun.tools.javac.tree.JCTree.JCIf; 44 import com.sun.tools.javac.tree.JCTree.JCLambda; 45 import com.sun.tools.javac.tree.JCTree.JCLambda.ParameterKind; 46 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 47 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 48 import com.sun.tools.javac.tree.JCTree.JCNewClass; 49 import com.sun.tools.javac.tree.JCTree.JCStatement; 50 import com.sun.tools.javac.tree.JCTree.JCSwitch; 51 import com.sun.tools.javac.tree.JCTree.JCTypeApply; 52 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 53 import com.sun.tools.javac.tree.JCTree.JCWhileLoop; 54 import com.sun.tools.javac.tree.JCTree.Tag; 55 import com.sun.tools.javac.tree.TreeCopier; 56 import com.sun.tools.javac.tree.TreeInfo; 57 import com.sun.tools.javac.tree.TreeMaker; 58 import com.sun.tools.javac.tree.TreeScanner; 59 import com.sun.tools.javac.util.Context; 60 import com.sun.tools.javac.util.DefinedBy; 61 import com.sun.tools.javac.util.DefinedBy.Api; 62 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; 63 import com.sun.tools.javac.util.List; 64 import com.sun.tools.javac.util.ListBuffer; 65 import com.sun.tools.javac.util.Log; 66 import com.sun.tools.javac.util.Options; 67 import com.sun.tools.javac.util.Position; 68 69 import java.util.EnumSet; 70 import java.util.HashMap; 71 import java.util.Map; 72 import java.util.function.Predicate; 73 74 import com.sun.source.tree.NewClassTree; 75 import com.sun.tools.javac.code.Flags; 76 import com.sun.tools.javac.code.Kinds.Kind; 77 import com.sun.tools.javac.code.Symbol.ClassSymbol; 78 import com.sun.tools.javac.tree.JCTree.JCTry; 79 import com.sun.tools.javac.tree.JCTree.JCUnary; 80 import com.sun.tools.javac.util.Assert; 81 import com.sun.tools.javac.util.DiagnosticSource; 82 83 import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR; 84 import static com.sun.tools.javac.code.TypeTag.CLASS; 85 import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; 86 import static com.sun.tools.javac.tree.JCTree.Tag.FOREACHLOOP; 87 import static com.sun.tools.javac.tree.JCTree.Tag.LABELLED; 88 import static com.sun.tools.javac.tree.JCTree.Tag.METHODDEF; 89 import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS; 90 import static com.sun.tools.javac.tree.JCTree.Tag.NULLCHK; 91 import static com.sun.tools.javac.tree.JCTree.Tag.TYPEAPPLY; 92 import static com.sun.tools.javac.tree.JCTree.Tag.VARDEF; 93 94 /** 95 * Helper class for defining custom code analysis, such as finding instance creation expression 96 * that can benefit from diamond syntax. 97 */ 98 public class Analyzer { 99 protected static final Context.Key<Analyzer> analyzerKey = new Context.Key<>(); 100 101 final Types types; 102 final Log log; 103 final Attr attr; 104 final DeferredAttr deferredAttr; 105 final ArgumentAttr argumentAttr; 106 final TreeMaker make; 107 final AnalyzerCopier copier; 108 private final boolean allowDiamondWithAnonymousClassCreation; 109 110 final EnumSet<AnalyzerMode> analyzerModes; 111 112 public static Analyzer instance(Context context) { 113 Analyzer instance = context.get(analyzerKey); 114 if (instance == null) 115 instance = new Analyzer(context); 116 return instance; 117 } 118 119 protected Analyzer(Context context) { 120 context.put(analyzerKey, this); 121 types = Types.instance(context); 122 log = Log.instance(context); 123 attr = Attr.instance(context); 124 deferredAttr = DeferredAttr.instance(context); 125 argumentAttr = ArgumentAttr.instance(context); 126 make = TreeMaker.instance(context); 127 copier = new AnalyzerCopier(); 128 Options options = Options.instance(context); 129 String findOpt = options.get("find"); 130 //parse modes 131 Source source = Source.instance(context); 132 allowDiamondWithAnonymousClassCreation = Feature.DIAMOND_WITH_ANONYMOUS_CLASS_CREATION.allowedInSource(source); 133 analyzerModes = AnalyzerMode.getAnalyzerModes(findOpt, source); 134 } 135 136 /** 137 * This enum defines supported analyzer modes, as well as defining the logic for decoding 138 * the {@code -XDfind} option. 139 */ 140 enum AnalyzerMode { 141 DIAMOND("diamond", Feature.DIAMOND), 142 LAMBDA("lambda", Feature.LAMBDA), 143 METHOD("method", Feature.GRAPH_INFERENCE), 144 LOCAL("local", Feature.LOCAL_VARIABLE_TYPE_INFERENCE); 145 146 final String opt; 147 final Feature feature; 148 149 AnalyzerMode(String opt, Feature feature) { 150 this.opt = opt; 151 this.feature = feature; 152 } 153 154 /** 155 * This method is used to parse the {@code find} option. 156 * Possible modes are separated by colon; a mode can be excluded by 157 * prepending '-' to its name. Finally, the special mode 'all' can be used to 158 * add all modes to the resulting enum. 159 */ 160 static EnumSet<AnalyzerMode> getAnalyzerModes(String opt, Source source) { 161 if (opt == null) { 162 return EnumSet.noneOf(AnalyzerMode.class); 163 } 164 List<String> modes = List.from(opt.split(",")); 165 EnumSet<AnalyzerMode> res = EnumSet.noneOf(AnalyzerMode.class); 166 if (modes.contains("all")) { 167 res = EnumSet.allOf(AnalyzerMode.class); 168 } 169 for (AnalyzerMode mode : values()) { 170 if (modes.contains(mode.opt)) { 171 res.add(mode); 172 } else if (modes.contains("-" + mode.opt) || !mode.feature.allowedInSource(source)) { 173 res.remove(mode); 174 } 175 } 176 return res; 177 } 178 } 179 180 /** 181 * A statement analyzer is a work-unit that matches certain AST nodes (of given type {@code S}), 182 * rewrites them to different AST nodes (of type {@code T}) and then generates some meaningful 183 * messages in case the analysis has been successful. 184 */ 185 abstract class StatementAnalyzer<S extends JCTree, T extends JCTree> { 186 187 AnalyzerMode mode; 188 JCTree.Tag tag; 189 190 StatementAnalyzer(AnalyzerMode mode, Tag tag) { 191 this.mode = mode; 192 this.tag = tag; 193 } 194 195 /** 196 * Is this analyzer allowed to run? 197 */ 198 boolean isEnabled() { 199 return analyzerModes.contains(mode); 200 } 201 202 /** 203 * Should this analyzer be rewriting the given tree? 204 */ 205 abstract boolean match(S tree); 206 207 /** 208 * Rewrite a given AST node into a new one(s) 209 */ 210 abstract List<T> rewrite(S oldTree); 211 212 /** 213 * Entry-point for comparing results and generating diagnostics. 214 */ 215 abstract void process(S oldTree, T newTree, boolean hasErrors); 216 } 217 218 /** 219 * This analyzer checks if generic instance creation expression can use diamond syntax. 220 */ 221 class DiamondInitializer extends StatementAnalyzer<JCNewClass, JCNewClass> { 222 223 DiamondInitializer() { 224 super(AnalyzerMode.DIAMOND, NEWCLASS); 225 } 226 227 @Override 228 boolean match(JCNewClass tree) { 229 return tree.clazz.hasTag(TYPEAPPLY) && 230 !TreeInfo.isDiamond(tree) && 231 (tree.def == null || allowDiamondWithAnonymousClassCreation); 232 } 233 234 @Override 235 List<JCNewClass> rewrite(JCNewClass oldTree) { 236 if (oldTree.clazz.hasTag(TYPEAPPLY)) { 237 JCNewClass nc = copier.copy(oldTree); 238 ((JCTypeApply)nc.clazz).arguments = List.nil(); 239 return List.of(nc); 240 } else { 241 return List.of(oldTree); 242 } 243 } 244 245 @Override 246 void process(JCNewClass oldTree, JCNewClass newTree, boolean hasErrors) { 247 if (!hasErrors) { 248 List<Type> inferredArgs, explicitArgs; 249 if (oldTree.def != null) { 250 inferredArgs = newTree.def.implementing.nonEmpty() 251 ? newTree.def.implementing.get(0).type.getTypeArguments() 252 : newTree.def.extending.type.getTypeArguments(); 253 explicitArgs = oldTree.def.implementing.nonEmpty() 254 ? oldTree.def.implementing.get(0).type.getTypeArguments() 255 : oldTree.def.extending.type.getTypeArguments(); 256 } else { 257 inferredArgs = newTree.type.getTypeArguments(); 258 explicitArgs = oldTree.type.getTypeArguments(); 259 } 260 for (Type t : inferredArgs) { 261 if (!types.isSameType(t, explicitArgs.head)) { 262 return; 263 } 264 explicitArgs = explicitArgs.tail; 265 } 266 //exact match 267 log.warning(oldTree.clazz, Warnings.DiamondRedundantArgs); 268 } 269 } 270 } 271 272 /** 273 * This analyzer checks if anonymous instance creation expression can replaced by lambda. 274 */ 275 class LambdaAnalyzer extends StatementAnalyzer<JCNewClass, JCLambda> { 276 277 LambdaAnalyzer() { 278 super(AnalyzerMode.LAMBDA, NEWCLASS); 279 } 280 281 @Override 282 boolean match (JCNewClass tree){ 283 Type clazztype = tree.clazz.type; 284 return tree.def != null && 285 clazztype.hasTag(CLASS) && 286 types.isFunctionalInterface(clazztype.tsym) && 287 decls(tree.def).length() == 1; 288 } 289 //where 290 private List<JCTree> decls(JCClassDecl decl) { 291 ListBuffer<JCTree> decls = new ListBuffer<>(); 292 for (JCTree t : decl.defs) { 293 if (t.hasTag(METHODDEF)) { 294 JCMethodDecl md = (JCMethodDecl)t; 295 if ((md.getModifiers().flags & GENERATEDCONSTR) == 0) { 296 decls.add(md); 297 } 298 } else { 299 decls.add(t); 300 } 301 } 302 return decls.toList(); 303 } 304 305 @Override 306 List<JCLambda> rewrite(JCNewClass oldTree){ 307 JCMethodDecl md = (JCMethodDecl)decls(oldTree.def).head; 308 List<JCVariableDecl> params = md.params; 309 JCBlock body = md.body; 310 JCLambda newTree = make.at(oldTree).Lambda(params, body); 311 return List.of(newTree); 312 } 313 314 @Override 315 void process (JCNewClass oldTree, JCLambda newTree, boolean hasErrors){ 316 if (!hasErrors) { 317 log.warning(oldTree.def, Warnings.PotentialLambdaFound); 318 } 319 } 320 } 321 322 /** 323 * This analyzer checks if generic method call has redundant type arguments. 324 */ 325 class RedundantTypeArgAnalyzer extends StatementAnalyzer<JCMethodInvocation, JCMethodInvocation> { 326 327 RedundantTypeArgAnalyzer() { 328 super(AnalyzerMode.METHOD, APPLY); 329 } 330 331 @Override 332 boolean match (JCMethodInvocation tree){ 333 return tree.typeargs != null && 334 tree.typeargs.nonEmpty(); 335 } 336 @Override 337 List<JCMethodInvocation> rewrite(JCMethodInvocation oldTree){ 338 JCMethodInvocation app = copier.copy(oldTree); 339 app.typeargs = List.nil(); 340 return List.of(app); 341 } 342 343 @Override 344 void process (JCMethodInvocation oldTree, JCMethodInvocation newTree, boolean hasErrors){ 345 if (!hasErrors) { 346 //exact match 347 log.warning(oldTree, Warnings.MethodRedundantTypeargs); 348 } 349 } 350 } 351 352 /** 353 * Base class for local variable inference analyzers. 354 */ 355 abstract class RedundantLocalVarTypeAnalyzerBase<X extends JCStatement> extends StatementAnalyzer<X, X> { 356 357 RedundantLocalVarTypeAnalyzerBase(JCTree.Tag tag) { 358 super(AnalyzerMode.LOCAL, tag); 359 } 360 361 /** 362 * Map a variable tree into a new declaration using implicit type. 363 */ 364 JCVariableDecl rewriteVarType(JCVariableDecl oldTree) { 365 JCVariableDecl newTree = copier.copy(oldTree); 366 newTree.vartype = null; 367 return newTree; 368 } 369 370 /** 371 * Analyze results of local variable inference. 372 */ 373 void processVar(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors) { 374 if (!hasErrors) { 375 if (types.isSameType(oldTree.type, newTree.type)) { 376 log.warning(oldTree, Warnings.LocalRedundantType); 377 } 378 } 379 } 380 } 381 382 /** 383 * This analyzer checks if a local variable declaration has redundant type. 384 */ 385 class RedundantLocalVarTypeAnalyzer extends RedundantLocalVarTypeAnalyzerBase<JCVariableDecl> { 386 387 RedundantLocalVarTypeAnalyzer() { 388 super(VARDEF); 389 } 390 391 boolean match(JCVariableDecl tree){ 392 return tree.sym.owner.kind == Kind.MTH && 393 tree.init != null && !tree.isImplicitlyTyped() && 394 attr.canInferLocalVarType(tree) == null; 395 } 396 @Override 397 List<JCVariableDecl> rewrite(JCVariableDecl oldTree) { 398 return List.of(rewriteVarType(oldTree)); 399 } 400 @Override 401 void process(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors){ 402 processVar(oldTree, newTree, hasErrors); 403 } 404 } 405 406 /** 407 * This analyzer checks if a for each variable declaration has redundant type. 408 */ 409 class RedundantLocalVarTypeAnalyzerForEach extends RedundantLocalVarTypeAnalyzerBase<JCEnhancedForLoop> { 410 411 RedundantLocalVarTypeAnalyzerForEach() { 412 super(FOREACHLOOP); 413 } 414 415 @Override 416 boolean match(JCEnhancedForLoop tree){ 417 return !tree.var.isImplicitlyTyped(); 418 } 419 @Override 420 List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) { 421 JCEnhancedForLoop newTree = copier.copy(oldTree); 422 newTree.var = rewriteVarType(oldTree.var); 423 newTree.body = make.at(oldTree.body).Block(0, List.nil()); 424 return List.of(newTree); 425 } 426 @Override 427 void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){ 428 processVar(oldTree.var, newTree.var, hasErrors); 429 } 430 } 431 432 @SuppressWarnings({"unchecked", "rawtypes"}) 433 StatementAnalyzer<JCTree, JCTree>[] analyzers = new StatementAnalyzer[] { 434 new DiamondInitializer(), 435 new LambdaAnalyzer(), 436 new RedundantTypeArgAnalyzer(), 437 new RedundantLocalVarTypeAnalyzer(), 438 new RedundantLocalVarTypeAnalyzerForEach() 439 }; 440 441 /** 442 * Create a copy of Env if needed. 443 */ 444 Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) { 445 if (!analyzerModes.isEmpty() && 446 !env.info.isSpeculative && 447 TreeInfo.isStatement(tree) && 448 !tree.hasTag(LABELLED)) { 449 Env<AttrContext> analyzeEnv = 450 env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); 451 analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ? 452 attr.new ResultInfo(analyzeEnv.info.returnResult.pkind, 453 analyzeEnv.info.returnResult.pt) : null; 454 return analyzeEnv; 455 } else { 456 return null; 457 } 458 } 459 460 /** 461 * Analyze an AST node if needed. 462 */ 463 void analyzeIfNeeded(JCTree tree, Env<AttrContext> env) { 464 if (env != null) { 465 JCStatement stmt = (JCStatement)tree; 466 analyze(stmt, env); 467 } 468 } 469 470 /** 471 * Analyze an AST node; this involves collecting a list of all the nodes that needs rewriting, 472 * and speculatively type-check the rewritten code to compare results against previously attributed code. 473 */ 474 void analyze(JCStatement statement, Env<AttrContext> env) { 475 StatementScanner statementScanner = new StatementScanner(statement, env); 476 statementScanner.scan(); 477 478 if (!statementScanner.rewritings.isEmpty()) { 479 for (RewritingContext rewriting : statementScanner.rewritings) { 480 deferredAnalysisHelper.queue(rewriting); 481 } 482 } 483 } 484 485 /** 486 * Helper interface to handle deferral of analysis tasks. 487 */ 488 interface DeferredAnalysisHelper { 489 /** 490 * Add a new analysis task to the queue. 491 */ 492 void queue(RewritingContext rewriting); 493 /** 494 * Flush queue with given attribution env. 495 */ 496 void flush(Env<AttrContext> flushEnv); 497 } 498 499 /** 500 * Dummy deferral handler. 501 */ 502 DeferredAnalysisHelper flushDeferredHelper = new DeferredAnalysisHelper() { 503 @Override 504 public void queue(RewritingContext rewriting) { 505 //do nothing 506 } 507 508 @Override 509 public void flush(Env<AttrContext> flushEnv) { 510 //do nothing 511 } 512 }; 513 514 /** 515 * Simple deferral handler. All tasks belonging to the same outermost class are added to 516 * the same queue. The queue is flushed after flow analysis (only if no error occurred). 517 */ 518 DeferredAnalysisHelper queueDeferredHelper = new DeferredAnalysisHelper() { 519 520 Map<ClassSymbol, ArrayList<RewritingContext>> Q = new HashMap<>(); 521 522 @Override 523 public void queue(RewritingContext rewriting) { 524 ArrayList<RewritingContext> s = Q.computeIfAbsent(rewriting.env.enclClass.sym.outermostClass(), k -> new ArrayList<>()); 525 s.add(rewriting); 526 } 527 528 @Override 529 public void flush(Env<AttrContext> flushEnv) { 530 if (!Q.isEmpty()) { 531 DeferredAnalysisHelper prevHelper = deferredAnalysisHelper; 532 try { 533 deferredAnalysisHelper = flushDeferredHelper; 534 ArrayList<RewritingContext> rewritings = Q.get(flushEnv.enclClass.sym.outermostClass()); 535 while (rewritings != null && !rewritings.isEmpty()) { 536 doAnalysis(rewritings.remove(0)); 537 } 538 } finally { 539 deferredAnalysisHelper = prevHelper; 540 } 541 } 542 } 543 }; 544 545 DeferredAnalysisHelper deferredAnalysisHelper = queueDeferredHelper; 546 547 void doAnalysis(RewritingContext rewriting) { 548 DiagnosticSource prevSource = log.currentSource(); 549 LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); 550 try { 551 log.useSource(rewriting.env.toplevel.getSourceFile()); 552 553 JCStatement treeToAnalyze = (JCStatement)rewriting.originalTree; 554 if (rewriting.env.info.scope.owner.kind == Kind.TYP) { 555 //add a block to hoist potential dangling variable declarations 556 treeToAnalyze = make.at(Position.NOPOS) 557 .Block(Flags.SYNTHETIC, List.of((JCStatement)rewriting.originalTree)); 558 } 559 560 //TODO: to further refine the analysis, try all rewriting combinations 561 deferredAttr.attribSpeculative(treeToAnalyze, rewriting.env, attr.statInfo, new TreeRewriter(rewriting), 562 t -> rewriting.diagHandler(), argumentAttr.withLocalCacheContext()); 563 rewriting.analyzer.process(rewriting.oldTree, rewriting.replacement, rewriting.erroneous); 564 } catch (Throwable ex) { 565 Assert.error("Analyzer error when processing: " + rewriting.originalTree); 566 } finally { 567 log.useSource(prevSource.getFile()); 568 localCacheContext.leave(); 569 } 570 } 571 572 public void flush(Env<AttrContext> flushEnv) { 573 deferredAnalysisHelper.flush(flushEnv); 574 } 575 576 /** 577 * Subclass of {@link com.sun.tools.javac.tree.TreeScanner} which visit AST-nodes w/o crossing 578 * statement boundaries. 579 */ 580 class StatementScanner extends TreeScanner { 581 /** Tree rewritings (generated by analyzers). */ 582 ListBuffer<RewritingContext> rewritings = new ListBuffer<>(); 583 JCTree originalTree; 584 Env<AttrContext> env; 585 586 StatementScanner(JCTree originalTree, Env<AttrContext> env) { 587 this.originalTree = originalTree; 588 this.env = attr.copyEnv(env); 589 } 590 591 public void scan() { 592 scan(originalTree); 593 } 594 595 @Override 596 @SuppressWarnings("unchecked") 597 public void scan(JCTree tree) { 598 if (tree != null) { 599 for (StatementAnalyzer<JCTree, JCTree> analyzer : analyzers) { 600 if (analyzer.isEnabled() && 601 tree.hasTag(analyzer.tag) && 602 analyzer.match(tree)) { 603 for (JCTree t : analyzer.rewrite(tree)) { 604 rewritings.add(new RewritingContext(originalTree, tree, t, analyzer, env)); 605 } 606 break; //TODO: cover cases where multiple matching analyzers are found 607 } 608 } 609 } 610 super.scan(tree); 611 } 612 613 @Override 614 public void visitClassDef(JCClassDecl tree) { 615 //do nothing (prevents seeing same stuff twice) 616 } 617 618 @Override 619 public void visitMethodDef(JCMethodDecl tree) { 620 //do nothing (prevents seeing same stuff twice) 621 } 622 623 @Override 624 public void visitBlock(JCBlock tree) { 625 //do nothing (prevents seeing same stuff twice) 626 } 627 628 @Override 629 public void visitSwitch(JCSwitch tree) { 630 scan(tree.getExpression()); 631 } 632 633 @Override 634 public void visitForLoop(JCForLoop tree) { 635 //skip body and var decl (to prevents same statements to be analyzed twice) 636 scan(tree.getCondition()); 637 scan(tree.getUpdate()); 638 } 639 640 @Override 641 public void visitTry(JCTry tree) { 642 //skip resources (to prevents same statements to be analyzed twice) 643 scan(tree.getBlock()); 644 scan(tree.getCatches()); 645 scan(tree.getFinallyBlock()); 646 } 647 648 @Override 649 public void visitForeachLoop(JCEnhancedForLoop tree) { 650 //skip body (to prevents same statements to be analyzed twice) 651 scan(tree.getExpression()); 652 } 653 654 @Override 655 public void visitWhileLoop(JCWhileLoop tree) { 656 //skip body (to prevents same statements to be analyzed twice) 657 scan(tree.getCondition()); 658 } 659 660 @Override 661 public void visitDoLoop(JCDoWhileLoop tree) { 662 //skip body (to prevents same statements to be analyzed twice) 663 scan(tree.getCondition()); 664 } 665 666 @Override 667 public void visitIf(JCIf tree) { 668 //skip body (to prevents same statements to be analyzed twice) 669 scan(tree.getCondition()); 670 } 671 } 672 673 class RewritingContext { 674 // the whole tree being analyzed 675 JCTree originalTree; 676 // a subtree, old tree, that will be rewritten 677 JCTree oldTree; 678 // the replacement for the old tree 679 JCTree replacement; 680 // did the compiler find any error 681 boolean erroneous; 682 // the env 683 Env<AttrContext> env; 684 // the corresponding analyzer 685 StatementAnalyzer<JCTree, JCTree> analyzer; 686 687 RewritingContext( 688 JCTree originalTree, 689 JCTree oldTree, 690 JCTree replacement, 691 StatementAnalyzer<JCTree, JCTree> analyzer, 692 Env<AttrContext> env) { 693 this.originalTree = originalTree; 694 this.oldTree = oldTree; 695 this.replacement = replacement; 696 this.analyzer = analyzer; 697 this.env = attr.copyEnv(env); 698 /* this is a temporary workaround that should be removed once we have a truly independent 699 * clone operation 700 */ 701 if (originalTree.hasTag(VARDEF)) { 702 // avoid redefinition clashes 703 this.env.info.scope.remove(((JCVariableDecl)originalTree).sym); 704 } 705 } 706 707 /** 708 * Simple deferred diagnostic handler which filters out all messages and keep track of errors. 709 */ 710 Log.DeferredDiagnosticHandler diagHandler() { 711 return new Log.DeferredDiagnosticHandler(log, d -> { 712 if (d.getType() == DiagnosticType.ERROR) { 713 erroneous = true; 714 } 715 return true; 716 }); 717 } 718 } 719 720 /** 721 * Subclass of TreeCopier that maps nodes matched by analyzers onto new AST nodes. 722 */ 723 class AnalyzerCopier extends TreeCopier<Void> { 724 725 public AnalyzerCopier() { 726 super(make); 727 } 728 729 @Override @DefinedBy(Api.COMPILER_TREE) 730 public JCTree visitLambdaExpression(LambdaExpressionTree node, Void _unused) { 731 JCLambda oldLambda = (JCLambda)node; 732 JCLambda newLambda = (JCLambda)super.visitLambdaExpression(node, _unused); 733 if (oldLambda.paramKind == ParameterKind.IMPLICIT) { 734 //reset implicit lambda parameters (whose type might have been set during attr) 735 newLambda.paramKind = ParameterKind.IMPLICIT; 736 newLambda.params.forEach(p -> p.vartype = null); 737 } 738 return newLambda; 739 } 740 741 @Override @DefinedBy(Api.COMPILER_TREE) 742 public JCTree visitNewClass(NewClassTree node, Void aVoid) { 743 JCNewClass oldNewClazz = (JCNewClass)node; 744 JCNewClass newNewClazz = (JCNewClass)super.visitNewClass(node, aVoid); 745 if (!oldNewClazz.args.isEmpty() && oldNewClazz.args.head.hasTag(NULLCHK)) { 746 //workaround to Attr generating trees 747 newNewClazz.encl = ((JCUnary)newNewClazz.args.head).arg; 748 newNewClazz.args = newNewClazz.args.tail; 749 } 750 return newNewClazz; 751 } 752 } 753 754 class TreeRewriter extends AnalyzerCopier { 755 756 RewritingContext rewriting; 757 758 TreeRewriter(RewritingContext rewriting) { 759 this.rewriting = rewriting; 760 } 761 762 @Override 763 @SuppressWarnings("unchecked") 764 public <Z extends JCTree> Z copy(Z tree, Void _unused) { 765 Z newTree = super.copy(tree, null); 766 if (tree != null && tree == rewriting.oldTree) { 767 Assert.checkNonNull(rewriting.replacement); 768 newTree = (Z)rewriting.replacement; 769 } 770 return newTree; 771 } 772 } 773 }