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)copier.copy(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 boolean isImplicitlyTyped(JCVariableDecl decl) { 362 return decl.vartype.pos == Position.NOPOS; 363 } 364 365 /** 366 * Map a variable tree into a new declaration using implicit type. 367 */ 368 JCVariableDecl rewriteVarType(JCVariableDecl oldTree) { 369 JCVariableDecl newTree = copier.copy(oldTree); 370 newTree.vartype = null; 371 return newTree; 372 } 373 374 /** 375 * Analyze results of local variable inference. 376 */ 377 void processVar(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors) { 378 if (!hasErrors) { 379 if (types.isSameType(oldTree.type, newTree.type)) { 380 log.warning(oldTree, Warnings.LocalRedundantType); 381 } 382 } 383 } 384 } 385 386 /** 387 * This analyzer checks if a local variable declaration has redundant type. 388 */ 389 class RedundantLocalVarTypeAnalyzer extends RedundantLocalVarTypeAnalyzerBase<JCVariableDecl> { 390 391 RedundantLocalVarTypeAnalyzer() { 392 super(VARDEF); 393 } 394 395 boolean match(JCVariableDecl tree){ 396 return tree.sym.owner.kind == Kind.MTH && 397 tree.init != null && !isImplicitlyTyped(tree) && 398 attr.canInferLocalVarType(tree) == null; 399 } 400 @Override 401 List<JCVariableDecl> rewrite(JCVariableDecl oldTree) { 402 return List.of(rewriteVarType(oldTree)); 403 } 404 @Override 405 void process(JCVariableDecl oldTree, JCVariableDecl newTree, boolean hasErrors){ 406 processVar(oldTree, newTree, hasErrors); 407 } 408 } 409 410 /** 411 * This analyzer checks if a for each variable declaration has redundant type. 412 */ 413 class RedundantLocalVarTypeAnalyzerForEach extends RedundantLocalVarTypeAnalyzerBase<JCEnhancedForLoop> { 414 415 RedundantLocalVarTypeAnalyzerForEach() { 416 super(FOREACHLOOP); 417 } 418 419 @Override 420 boolean match(JCEnhancedForLoop tree){ 421 return !isImplicitlyTyped(tree.var); 422 } 423 @Override 424 List<JCEnhancedForLoop> rewrite(JCEnhancedForLoop oldTree) { 425 JCEnhancedForLoop newTree = copier.copy(oldTree); 426 newTree.var = rewriteVarType(oldTree.var); 427 newTree.body = make.at(oldTree.body).Block(0, List.nil()); 428 return List.of(newTree); 429 } 430 @Override 431 void process(JCEnhancedForLoop oldTree, JCEnhancedForLoop newTree, boolean hasErrors){ 432 processVar(oldTree.var, newTree.var, hasErrors); 433 } 434 } 435 436 @SuppressWarnings({"unchecked", "rawtypes"}) 437 StatementAnalyzer<JCTree, JCTree>[] analyzers = new StatementAnalyzer[] { 438 new DiamondInitializer(), 439 new LambdaAnalyzer(), 440 new RedundantTypeArgAnalyzer(), 441 new RedundantLocalVarTypeAnalyzer(), 442 new RedundantLocalVarTypeAnalyzerForEach() 443 }; 444 445 /** 446 * Create a copy of Env if needed. 447 */ 448 Env<AttrContext> copyEnvIfNeeded(JCTree tree, Env<AttrContext> env) { 449 if (!analyzerModes.isEmpty() && 450 !env.info.isSpeculative && 451 TreeInfo.isStatement(tree) && 452 !tree.hasTag(LABELLED)) { 453 Env<AttrContext> analyzeEnv = 454 env.dup(env.tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); 455 analyzeEnv.info.returnResult = analyzeEnv.info.returnResult != null ? 456 attr.new ResultInfo(analyzeEnv.info.returnResult.pkind, 457 analyzeEnv.info.returnResult.pt) : null; 458 return analyzeEnv; 459 } else { 460 return null; 461 } 462 } 463 464 /** 465 * Analyze an AST node if needed. 466 */ 467 void analyzeIfNeeded(JCTree tree, Env<AttrContext> env) { 468 if (env != null) { 469 JCStatement stmt = (JCStatement)tree; 470 analyze(stmt, env); 471 } 472 } 473 474 /** 475 * Analyze an AST node; this involves collecting a list of all the nodes that needs rewriting, 476 * and speculatively type-check the rewritten code to compare results against previously attributed code. 477 */ 478 void analyze(JCStatement statement, Env<AttrContext> env) { 479 StatementScanner statementScanner = new StatementScanner(statement, env); 480 statementScanner.scan(); 481 482 if (!statementScanner.rewritings.isEmpty()) { 483 for (RewritingContext rewriting : statementScanner.rewritings) { 484 deferredAnalysisHelper.queue(rewriting); 485 } 486 } 487 } 488 489 /** 490 * Helper interface to handle deferral of analysis tasks. 491 */ 492 interface DeferredAnalysisHelper { 493 /** 494 * Add a new analysis task to the queue. 495 */ 496 void queue(RewritingContext rewriting); 497 /** 498 * Flush queue with given attribution env. 499 */ 500 void flush(Env<AttrContext> flushEnv); 501 } 502 503 /** 504 * Dummy deferral handler. 505 */ 506 DeferredAnalysisHelper flushDeferredHelper = new DeferredAnalysisHelper() { 507 @Override 508 public void queue(RewritingContext rewriting) { 509 //do nothing 510 } 511 512 @Override 513 public void flush(Env<AttrContext> flushEnv) { 514 //do nothing 515 } 516 }; 517 518 /** 519 * Simple deferral handler. All tasks belonging to the same outermost class are added to 520 * the same queue. The queue is flushed after flow analysis (only if no error occurred). 521 */ 522 DeferredAnalysisHelper queueDeferredHelper = new DeferredAnalysisHelper() { 523 524 Map<ClassSymbol, ArrayList<RewritingContext>> Q = new HashMap<>(); 525 526 @Override 527 public void queue(RewritingContext rewriting) { 528 ArrayList<RewritingContext> s = Q.computeIfAbsent(rewriting.env.enclClass.sym.outermostClass(), k -> new ArrayList<>()); 529 s.add(rewriting); 530 } 531 532 @Override 533 public void flush(Env<AttrContext> flushEnv) { 534 if (!Q.isEmpty()) { 535 DeferredAnalysisHelper prevHelper = deferredAnalysisHelper; 536 try { 537 deferredAnalysisHelper = flushDeferredHelper; 538 ArrayList<RewritingContext> rewritings = Q.get(flushEnv.enclClass.sym.outermostClass()); 539 while (rewritings != null && !rewritings.isEmpty()) { 540 doAnalysis(rewritings.remove(0)); 541 } 542 } finally { 543 deferredAnalysisHelper = prevHelper; 544 } 545 } 546 } 547 }; 548 549 DeferredAnalysisHelper deferredAnalysisHelper = queueDeferredHelper; 550 551 void doAnalysis(RewritingContext rewriting) { 552 DiagnosticSource prevSource = log.currentSource(); 553 LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); 554 try { 555 log.useSource(rewriting.env.toplevel.getSourceFile()); 556 557 JCStatement treeToAnalyze = (JCStatement)rewriting.originalTree; 558 if (rewriting.env.info.scope.owner.kind == Kind.TYP) { 559 //add a block to hoist potential dangling variable declarations 560 treeToAnalyze = make.at(Position.NOPOS) 561 .Block(Flags.SYNTHETIC, List.of((JCStatement)rewriting.originalTree)); 562 } 563 564 //TODO: to further refine the analysis, try all rewriting combinations 565 deferredAttr.attribSpeculative(treeToAnalyze, rewriting.env, attr.statInfo, new TreeRewriter(rewriting), 566 t -> rewriting.diagHandler(), argumentAttr.withLocalCacheContext()); 567 rewriting.analyzer.process(rewriting.oldTree, rewriting.replacement, rewriting.erroneous); 568 } catch (Throwable ex) { 569 Assert.error("Analyzer error when processing: " + rewriting.originalTree); 570 } finally { 571 log.useSource(prevSource.getFile()); 572 localCacheContext.leave(); 573 } 574 } 575 576 public void flush(Env<AttrContext> flushEnv) { 577 deferredAnalysisHelper.flush(flushEnv); 578 } 579 580 /** 581 * Subclass of {@link com.sun.tools.javac.tree.TreeScanner} which visit AST-nodes w/o crossing 582 * statement boundaries. 583 */ 584 class StatementScanner extends TreeScanner { 585 /** Tree rewritings (generated by analyzers). */ 586 ListBuffer<RewritingContext> rewritings = new ListBuffer<>(); 587 JCTree originalTree; 588 Env<AttrContext> env; 589 590 StatementScanner(JCTree originalTree, Env<AttrContext> env) { 591 this.originalTree = originalTree; 592 this.env = attr.copyEnv(env); 593 } 594 595 public void scan() { 596 scan(originalTree); 597 } 598 599 @Override 600 @SuppressWarnings("unchecked") 601 public void scan(JCTree tree) { 602 if (tree != null) { 603 for (StatementAnalyzer<JCTree, JCTree> analyzer : analyzers) { 604 if (analyzer.isEnabled() && 605 tree.hasTag(analyzer.tag) && 606 analyzer.match(tree)) { 607 for (JCTree t : analyzer.rewrite(tree)) { 608 rewritings.add(new RewritingContext(originalTree, tree, t, analyzer, env)); 609 } 610 break; //TODO: cover cases where multiple matching analyzers are found 611 } 612 } 613 } 614 super.scan(tree); 615 } 616 617 @Override 618 public void visitClassDef(JCClassDecl tree) { 619 //do nothing (prevents seeing same stuff twice) 620 } 621 622 @Override 623 public void visitMethodDef(JCMethodDecl tree) { 624 //do nothing (prevents seeing same stuff twice) 625 } 626 627 @Override 628 public void visitBlock(JCBlock tree) { 629 //do nothing (prevents seeing same stuff twice) 630 } 631 632 @Override 633 public void visitSwitch(JCSwitch tree) { 634 scan(tree.getExpression()); 635 } 636 637 @Override 638 public void visitForLoop(JCForLoop tree) { 639 //skip body and var decl (to prevents same statements to be analyzed twice) 640 scan(tree.getCondition()); 641 scan(tree.getUpdate()); 642 } 643 644 @Override 645 public void visitTry(JCTry tree) { 646 //skip resources (to prevents same statements to be analyzed twice) 647 scan(tree.getBlock()); 648 scan(tree.getCatches()); 649 scan(tree.getFinallyBlock()); 650 } 651 652 @Override 653 public void visitForeachLoop(JCEnhancedForLoop tree) { 654 //skip body (to prevents same statements to be analyzed twice) 655 scan(tree.getExpression()); 656 } 657 658 @Override 659 public void visitWhileLoop(JCWhileLoop tree) { 660 //skip body (to prevents same statements to be analyzed twice) 661 scan(tree.getCondition()); 662 } 663 664 @Override 665 public void visitDoLoop(JCDoWhileLoop tree) { 666 //skip body (to prevents same statements to be analyzed twice) 667 scan(tree.getCondition()); 668 } 669 670 @Override 671 public void visitIf(JCIf tree) { 672 //skip body (to prevents same statements to be analyzed twice) 673 scan(tree.getCondition()); 674 } 675 } 676 677 class RewritingContext { 678 // the whole tree being analyzed 679 JCTree originalTree; 680 // a subtree, old tree, that will be rewritten 681 JCTree oldTree; 682 // the replacement for the old tree 683 JCTree replacement; 684 // did the compiler find any error 685 boolean erroneous; 686 // the env 687 Env<AttrContext> env; 688 // the corresponding analyzer 689 StatementAnalyzer<JCTree, JCTree> analyzer; 690 691 RewritingContext( 692 JCTree originalTree, 693 JCTree oldTree, 694 JCTree replacement, 695 StatementAnalyzer<JCTree, JCTree> analyzer, 696 Env<AttrContext> env) { 697 this.originalTree = originalTree; 698 this.oldTree = oldTree; 699 this.replacement = replacement; 700 this.analyzer = analyzer; 701 this.env = attr.copyEnv(env); 702 /* this is a temporary workaround that should be removed once we have a truly independent 703 * clone operation 704 */ 705 if (originalTree.hasTag(VARDEF)) { 706 // avoid redefinition clashes 707 this.env.info.scope.remove(((JCVariableDecl)originalTree).sym); 708 } 709 } 710 711 /** 712 * Simple deferred diagnostic handler which filters out all messages and keep track of errors. 713 */ 714 Log.DeferredDiagnosticHandler diagHandler() { 715 return new Log.DeferredDiagnosticHandler(log, d -> { 716 if (d.getType() == DiagnosticType.ERROR) { 717 erroneous = true; 718 } 719 return true; 720 }); 721 } 722 } 723 724 /** 725 * Subclass of TreeCopier that maps nodes matched by analyzers onto new AST nodes. 726 */ 727 class AnalyzerCopier extends TreeCopier<Void> { 728 729 public AnalyzerCopier() { 730 super(make); 731 } 732 733 @Override @DefinedBy(Api.COMPILER_TREE) 734 public JCTree visitLambdaExpression(LambdaExpressionTree node, Void _unused) { 735 JCLambda oldLambda = (JCLambda)node; 736 JCLambda newLambda = (JCLambda)super.visitLambdaExpression(node, _unused); 737 if (oldLambda.paramKind == ParameterKind.IMPLICIT) { 738 //reset implicit lambda parameters (whose type might have been set during attr) 739 newLambda.paramKind = ParameterKind.IMPLICIT; 740 newLambda.params.forEach(p -> p.vartype = null); 741 } 742 return newLambda; 743 } 744 745 @Override @DefinedBy(Api.COMPILER_TREE) 746 public JCTree visitNewClass(NewClassTree node, Void aVoid) { 747 JCNewClass oldNewClazz = (JCNewClass)node; 748 JCNewClass newNewClazz = (JCNewClass)super.visitNewClass(node, aVoid); 749 if (!oldNewClazz.args.isEmpty() && oldNewClazz.args.head.hasTag(NULLCHK)) { 750 //workaround to Attr generating trees 751 newNewClazz.encl = ((JCUnary)newNewClazz.args.head).arg; 752 newNewClazz.args = newNewClazz.args.tail; 753 } 754 return newNewClazz; 755 } 756 } 757 758 class TreeRewriter extends AnalyzerCopier { 759 760 RewritingContext rewriting; 761 762 TreeRewriter(RewritingContext rewriting) { 763 this.rewriting = rewriting; 764 } 765 766 @Override 767 @SuppressWarnings("unchecked") 768 public <Z extends JCTree> Z copy(Z tree, Void _unused) { 769 Z newTree = super.copy(tree, null); 770 if (tree != null && tree == rewriting.oldTree) { 771 Assert.checkNonNull(rewriting.replacement); 772 newTree = (Z)rewriting.replacement; 773 } 774 return newTree; 775 } 776 } 777 }