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