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