001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle; 021 022import java.util.ArrayDeque; 023import java.util.ArrayList; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Optional; 027import java.util.Queue; 028import java.util.stream.Collectors; 029 030import org.antlr.v4.runtime.BufferedTokenStream; 031import org.antlr.v4.runtime.CommonTokenStream; 032import org.antlr.v4.runtime.ParserRuleContext; 033import org.antlr.v4.runtime.Token; 034import org.antlr.v4.runtime.tree.ParseTree; 035import org.antlr.v4.runtime.tree.TerminalNode; 036 037import com.puppycrawl.tools.checkstyle.api.TokenTypes; 038import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageLexer; 039import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParser; 040import com.puppycrawl.tools.checkstyle.grammar.java.JavaLanguageParserBaseVisitor; 041import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 042 043/** 044 * Visitor class used to build Checkstyle's Java AST from the parse tree produced by 045 * {@link JavaLanguageParser}. In each {@code visit...} method, we visit the children of a node 046 * (which correspond to subrules) or create terminal nodes (tokens), and return a subtree as a 047 * result. 048 * 049 * <p>Example:</p> 050 * 051 * <p>The following package declaration:</p> 052 * <pre> 053 * package com.puppycrawl.tools.checkstyle; 054 * </pre> 055 * 056 * <p> 057 * Will be parsed by the {@code packageDeclaration} rule from {@code JavaLanguageParser.g4}: 058 * </p> 059 * <pre> 060 * packageDeclaration 061 * : annotations[true] LITERAL_PACKAGE qualifiedName SEMI 062 * ; 063 * </pre> 064 * 065 * <p> 066 * We override the {@code visitPackageDeclaration} method generated by ANTLR in 067 * {@code JavaLanguageParser} at 068 * {@link JavaAstVisitor#visitPackageDeclaration(JavaLanguageParser.PackageDeclarationContext)} 069 * to create a subtree based on the subrules and tokens found in the {@code packageDeclaration} 070 * subrule accordingly, thus producing the following AST: 071 * </p> 072 * <pre> 073 * PACKAGE_DEF -> package 074 * |--ANNOTATIONS -> ANNOTATIONS 075 * |--DOT -> . 076 * | |--DOT -> . 077 * | | |--DOT -> . 078 * | | | |--IDENT -> com 079 * | | | `--IDENT -> puppycrawl 080 * | | `--IDENT -> tools 081 * | `--IDENT -> checkstyle 082 * `--SEMI -> ; 083 * </pre> 084 * 085 * <p> 086 * See <a href="https://github.com/checkstyle/checkstyle/pull/10434">#10434</a> 087 * for a good example of how 088 * to make changes to Checkstyle's grammar and AST. 089 * </p> 090 * 091 * <p> 092 * The order of {@code visit...} methods in {@code JavaAstVisitor.java} and production rules in 093 * {@code JavaLanguageParser.g4} should be consistent to ease maintenance. 094 * </p> 095 * 096 * @noinspection JavadocReference 097 * @noinspectionreason JavadocReference - References are valid 098 */ 099public final class JavaAstVisitor extends JavaLanguageParserBaseVisitor<DetailAstImpl> { 100 101 /** String representation of the left shift operator. */ 102 private static final String LEFT_SHIFT = "<<"; 103 104 /** String representation of the unsigned right shift operator. */ 105 private static final String UNSIGNED_RIGHT_SHIFT = ">>>"; 106 107 /** String representation of the right shift operator. */ 108 private static final String RIGHT_SHIFT = ">>"; 109 110 /** 111 * The tokens here are technically expressions, but should 112 * not return an EXPR token as their root. 113 */ 114 private static final int[] EXPRESSIONS_WITH_NO_EXPR_ROOT = { 115 TokenTypes.CTOR_CALL, 116 TokenTypes.SUPER_CTOR_CALL, 117 TokenTypes.LAMBDA, 118 }; 119 120 /** Token stream to check for hidden tokens. */ 121 private final BufferedTokenStream tokens; 122 123 /** 124 * Constructs a JavaAstVisitor with given token stream. 125 * 126 * @param tokenStream the token stream to check for hidden tokens 127 */ 128 public JavaAstVisitor(CommonTokenStream tokenStream) { 129 tokens = tokenStream; 130 } 131 132 @Override 133 public DetailAstImpl visitCompilationUnit(JavaLanguageParser.CompilationUnitContext ctx) { 134 final DetailAstImpl root; 135 // 'EOF' token is always present; therefore if we only have one child, we have an empty file 136 final boolean isEmptyFile = ctx.children.size() == 1; 137 if (isEmptyFile) { 138 root = null; 139 } 140 else { 141 // last child is 'EOF', we do not include this token in AST 142 final List<ParseTree> children = ctx.children.subList(0, ctx.children.size() - 1); 143 144 final boolean isCompactSourceFile = children.stream() 145 .anyMatch(JavaAstVisitor::isCompactMemberDeclaration); 146 147 if (isCompactSourceFile) { 148 root = createImaginary(TokenTypes.COMPACT_COMPILATION_UNIT); 149 } 150 else { 151 root = createImaginary(TokenTypes.COMPILATION_UNIT); 152 } 153 processChildren(root, children); 154 } 155 return root; 156 } 157 158 /** 159 * Checks whether the given parse-tree node is a compact member declaration. 160 * 161 * @param child a parse-tree child of the compilation unit 162 * @return true if the node is a compact member declaration 163 */ 164 private static boolean isCompactMemberDeclaration(ParseTree child) { 165 return child instanceof JavaLanguageParser.CompactMemberDeclarationContext; 166 } 167 168 @Override 169 public DetailAstImpl visitPackageDeclaration( 170 JavaLanguageParser.PackageDeclarationContext ctx) { 171 final DetailAstImpl packageDeclaration = 172 create(TokenTypes.PACKAGE_DEF, (Token) ctx.LITERAL_PACKAGE().getPayload()); 173 packageDeclaration.addChild(visit(ctx.annotations())); 174 packageDeclaration.addChild(visit(ctx.qualifiedName())); 175 packageDeclaration.addChild(create(ctx.SEMI())); 176 return packageDeclaration; 177 } 178 179 @Override 180 public DetailAstImpl visitImportDec(JavaLanguageParser.ImportDecContext ctx) { 181 final DetailAstImpl importRoot = create(ctx.start); 182 183 // Static import 184 final TerminalNode literalStaticNode = ctx.LITERAL_STATIC(); 185 if (literalStaticNode != null) { 186 importRoot.setType(TokenTypes.STATIC_IMPORT); 187 importRoot.addChild(create(literalStaticNode)); 188 } 189 190 // Module import 191 final TerminalNode literalModuleNode = ctx.LITERAL_MODULE(); 192 if (literalModuleNode != null) { 193 importRoot.setType(TokenTypes.MODULE_IMPORT); 194 importRoot.addChild(create(literalModuleNode)); 195 } 196 197 // Handle star imports 198 final boolean isStarImport = ctx.STAR() != null; 199 if (isStarImport) { 200 final DetailAstImpl dot = create(ctx.DOT()); 201 dot.addChild(visit(ctx.qualifiedName())); 202 dot.addChild(create(ctx.STAR())); 203 importRoot.addChild(dot); 204 } 205 else { 206 importRoot.addChild(visit(ctx.qualifiedName())); 207 } 208 209 importRoot.addChild(create(ctx.SEMI())); 210 return importRoot; 211 } 212 213 @Override 214 public DetailAstImpl visitSingleSemiImport(JavaLanguageParser.SingleSemiImportContext ctx) { 215 return create(ctx.SEMI()); 216 } 217 218 @Override 219 public DetailAstImpl visitModuleDeclaration( 220 JavaLanguageParser.ModuleDeclarationContext ctx) { 221 final DetailAstImpl moduleDeclaration = createImaginary(TokenTypes.MODULE_DEF); 222 processChildren(moduleDeclaration, ctx.children); 223 return moduleDeclaration; 224 } 225 226 @Override 227 public DetailAstImpl visitDirectiveBlock(JavaLanguageParser.DirectiveBlockContext ctx) { 228 final DetailAstImpl directiveBlock = createImaginary(TokenTypes.DIRECTIVE_BLOCK); 229 processChildren(directiveBlock, ctx.children); 230 return directiveBlock; 231 } 232 233 @Override 234 public DetailAstImpl visitRequiresDirective( 235 JavaLanguageParser.RequiresDirectiveContext ctx) { 236 return createNodeFromFirstToken(TokenTypes.REQUIRES, ctx); 237 } 238 239 @Override 240 public DetailAstImpl visitExportsDirective( 241 JavaLanguageParser.ExportsDirectiveContext ctx) { 242 return createNodeFromFirstToken(TokenTypes.EXPORTS, ctx); 243 } 244 245 @Override 246 public DetailAstImpl visitOpensDirective(JavaLanguageParser.OpensDirectiveContext ctx) { 247 return createNodeFromFirstToken(TokenTypes.OPENS, ctx); 248 } 249 250 @Override 251 public DetailAstImpl visitUsesDirective(JavaLanguageParser.UsesDirectiveContext ctx) { 252 return createNodeFromFirstToken(TokenTypes.USES, ctx); 253 } 254 255 @Override 256 public DetailAstImpl visitProvidesDirective( 257 JavaLanguageParser.ProvidesDirectiveContext ctx) { 258 return createNodeFromFirstToken(TokenTypes.PROVIDES, ctx); 259 } 260 261 @Override 262 public DetailAstImpl visitRequiresModifier( 263 JavaLanguageParser.RequiresModifierContext ctx) { 264 return flattenedTree(ctx); 265 } 266 267 @Override 268 public DetailAstImpl visitToClause(JavaLanguageParser.ToClauseContext ctx) { 269 return createNodeFromFirstToken(TokenTypes.TO, ctx); 270 } 271 272 @Override 273 public DetailAstImpl visitWithClause(JavaLanguageParser.WithClauseContext ctx) { 274 return createNodeFromFirstToken(TokenTypes.WITH, ctx); 275 } 276 277 @Override 278 public DetailAstImpl visitTypeDeclaration(JavaLanguageParser.TypeDeclarationContext ctx) { 279 final DetailAstImpl typeDeclaration; 280 if (ctx.type == null) { 281 typeDeclaration = create(ctx.semi.getFirst()); 282 ctx.semi.subList(1, ctx.semi.size()) 283 .forEach(semi -> addLastSibling(typeDeclaration, create(semi))); 284 } 285 else { 286 typeDeclaration = visit(ctx.type); 287 } 288 return typeDeclaration; 289 } 290 291 @Override 292 public DetailAstImpl visitModifier(JavaLanguageParser.ModifierContext ctx) { 293 return flattenedTree(ctx); 294 } 295 296 @Override 297 public DetailAstImpl visitVariableModifier(JavaLanguageParser.VariableModifierContext ctx) { 298 return flattenedTree(ctx); 299 } 300 301 @Override 302 public DetailAstImpl visitClassDeclaration(JavaLanguageParser.ClassDeclarationContext ctx) { 303 return createTypeDeclaration(ctx, TokenTypes.CLASS_DEF, ctx.mods); 304 } 305 306 @Override 307 public DetailAstImpl visitRecordDeclaration(JavaLanguageParser.RecordDeclarationContext ctx) { 308 return createTypeDeclaration(ctx, TokenTypes.RECORD_DEF, ctx.mods); 309 } 310 311 @Override 312 public DetailAstImpl visitRecordComponentsList( 313 JavaLanguageParser.RecordComponentsListContext ctx) { 314 final DetailAstImpl lparen = create(ctx.LPAREN()); 315 316 // We make a "RECORD_COMPONENTS" node whether components exist or not 317 if (ctx.recordComponents() == null) { 318 addLastSibling(lparen, createImaginary(TokenTypes.RECORD_COMPONENTS)); 319 } 320 else { 321 addLastSibling(lparen, visit(ctx.recordComponents())); 322 } 323 addLastSibling(lparen, create(ctx.RPAREN())); 324 return lparen; 325 } 326 327 @Override 328 public DetailAstImpl visitRecordComponents(JavaLanguageParser.RecordComponentsContext ctx) { 329 final DetailAstImpl recordComponents = createImaginary(TokenTypes.RECORD_COMPONENTS); 330 processChildren(recordComponents, ctx.children); 331 return recordComponents; 332 } 333 334 @Override 335 public DetailAstImpl visitRecordComponent(JavaLanguageParser.RecordComponentContext ctx) { 336 final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF); 337 processChildren(recordComponent, ctx.children); 338 return recordComponent; 339 } 340 341 @Override 342 public DetailAstImpl visitLastRecordComponent( 343 JavaLanguageParser.LastRecordComponentContext ctx) { 344 final DetailAstImpl recordComponent = createImaginary(TokenTypes.RECORD_COMPONENT_DEF); 345 processChildren(recordComponent, ctx.children); 346 return recordComponent; 347 } 348 349 @Override 350 public DetailAstImpl visitRecordBody(JavaLanguageParser.RecordBodyContext ctx) { 351 final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); 352 processChildren(objBlock, ctx.children); 353 return objBlock; 354 } 355 356 @Override 357 public DetailAstImpl visitCompactConstructorDeclaration( 358 JavaLanguageParser.CompactConstructorDeclarationContext ctx) { 359 final DetailAstImpl compactConstructor = createImaginary(TokenTypes.COMPACT_CTOR_DEF); 360 compactConstructor.addChild(createModifiers(ctx.mods)); 361 compactConstructor.addChild(visit(ctx.id())); 362 compactConstructor.addChild(visit(ctx.constructorBlock())); 363 return compactConstructor; 364 } 365 366 @Override 367 public DetailAstImpl visitClassExtends(JavaLanguageParser.ClassExtendsContext ctx) { 368 final DetailAstImpl classExtends = create(ctx.EXTENDS_CLAUSE()); 369 classExtends.addChild(visit(ctx.type)); 370 return classExtends; 371 } 372 373 @Override 374 public DetailAstImpl visitImplementsClause(JavaLanguageParser.ImplementsClauseContext ctx) { 375 final DetailAstImpl classImplements = create(TokenTypes.IMPLEMENTS_CLAUSE, 376 (Token) ctx.LITERAL_IMPLEMENTS().getPayload()); 377 classImplements.addChild(visit(ctx.typeList())); 378 return classImplements; 379 } 380 381 @Override 382 public DetailAstImpl visitTypeParameters(JavaLanguageParser.TypeParametersContext ctx) { 383 final DetailAstImpl typeParameters = createImaginary(TokenTypes.TYPE_PARAMETERS); 384 typeParameters.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); 385 // Exclude '<' and '>' 386 processChildren(typeParameters, ctx.children.subList(1, ctx.children.size() - 1)); 387 typeParameters.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); 388 return typeParameters; 389 } 390 391 @Override 392 public DetailAstImpl visitTypeParameter(JavaLanguageParser.TypeParameterContext ctx) { 393 final DetailAstImpl typeParameter = createImaginary(TokenTypes.TYPE_PARAMETER); 394 processChildren(typeParameter, ctx.children); 395 return typeParameter; 396 } 397 398 @Override 399 public DetailAstImpl visitTypeUpperBounds(JavaLanguageParser.TypeUpperBoundsContext ctx) { 400 // In this case, we call 'extends` TYPE_UPPER_BOUNDS 401 final DetailAstImpl typeUpperBounds = create(TokenTypes.TYPE_UPPER_BOUNDS, 402 (Token) ctx.EXTENDS_CLAUSE().getPayload()); 403 // 'extends' is child[0] 404 processChildren(typeUpperBounds, ctx.children.subList(1, ctx.children.size())); 405 return typeUpperBounds; 406 } 407 408 @Override 409 public DetailAstImpl visitTypeBound(JavaLanguageParser.TypeBoundContext ctx) { 410 final DetailAstImpl typeBoundType = visit(ctx.typeBoundType(0)); 411 final Iterator<JavaLanguageParser.TypeBoundTypeContext> typeBoundTypeIterator = 412 ctx.typeBoundType().listIterator(1); 413 ctx.BAND().forEach(band -> { 414 addLastSibling(typeBoundType, create(TokenTypes.TYPE_EXTENSION_AND, 415 (Token) band.getPayload())); 416 addLastSibling(typeBoundType, visit(typeBoundTypeIterator.next())); 417 }); 418 return typeBoundType; 419 } 420 421 @Override 422 public DetailAstImpl visitTypeBoundType(JavaLanguageParser.TypeBoundTypeContext ctx) { 423 return flattenedTree(ctx); 424 } 425 426 @Override 427 public DetailAstImpl visitEnumDeclaration(JavaLanguageParser.EnumDeclarationContext ctx) { 428 return createTypeDeclaration(ctx, TokenTypes.ENUM_DEF, ctx.mods); 429 } 430 431 @Override 432 public DetailAstImpl visitEnumBody(JavaLanguageParser.EnumBodyContext ctx) { 433 final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); 434 processChildren(objBlock, ctx.children); 435 return objBlock; 436 } 437 438 @Override 439 public DetailAstImpl visitEnumConstants(JavaLanguageParser.EnumConstantsContext ctx) { 440 return flattenedTree(ctx); 441 } 442 443 @Override 444 public DetailAstImpl visitEnumConstant(JavaLanguageParser.EnumConstantContext ctx) { 445 final DetailAstImpl enumConstant = 446 createImaginary(TokenTypes.ENUM_CONSTANT_DEF); 447 processChildren(enumConstant, ctx.children); 448 return enumConstant; 449 } 450 451 @Override 452 public DetailAstImpl visitEnumBodyDeclarations( 453 JavaLanguageParser.EnumBodyDeclarationsContext ctx) { 454 return flattenedTree(ctx); 455 } 456 457 @Override 458 public DetailAstImpl visitInterfaceDeclaration( 459 JavaLanguageParser.InterfaceDeclarationContext ctx) { 460 return createTypeDeclaration(ctx, TokenTypes.INTERFACE_DEF, ctx.mods); 461 } 462 463 @Override 464 public DetailAstImpl visitInterfaceExtends(JavaLanguageParser.InterfaceExtendsContext ctx) { 465 final DetailAstImpl interfaceExtends = create(ctx.EXTENDS_CLAUSE()); 466 interfaceExtends.addChild(visit(ctx.typeList())); 467 return interfaceExtends; 468 } 469 470 @Override 471 public DetailAstImpl visitClassBody(JavaLanguageParser.ClassBodyContext ctx) { 472 final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); 473 processChildren(objBlock, ctx.children); 474 return objBlock; 475 } 476 477 @Override 478 public DetailAstImpl visitInterfaceBody(JavaLanguageParser.InterfaceBodyContext ctx) { 479 final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); 480 processChildren(objBlock, ctx.children); 481 return objBlock; 482 } 483 484 @Override 485 public DetailAstImpl visitEmptyClass(JavaLanguageParser.EmptyClassContext ctx) { 486 return flattenedTree(ctx); 487 } 488 489 @Override 490 public DetailAstImpl visitClassBlock(JavaLanguageParser.ClassBlockContext ctx) { 491 final DetailAstImpl classBlock; 492 if (ctx.LITERAL_STATIC() == null) { 493 // We call it an INSTANCE_INIT 494 classBlock = createImaginary(TokenTypes.INSTANCE_INIT); 495 } 496 else { 497 classBlock = create(TokenTypes.STATIC_INIT, (Token) ctx.LITERAL_STATIC().getPayload()); 498 classBlock.setText(TokenUtil.getTokenName(TokenTypes.STATIC_INIT)); 499 } 500 classBlock.addChild(visit(ctx.block())); 501 return classBlock; 502 } 503 504 @Override 505 public DetailAstImpl visitMethodDeclaration(JavaLanguageParser.MethodDeclarationContext ctx) { 506 final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF); 507 methodDef.addChild(createModifiers(ctx.mods)); 508 509 // Process all children except C style array declarators 510 processChildren(methodDef, ctx.children.stream() 511 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) 512 .toList()); 513 514 // We add C style array declarator brackets to TYPE ast 515 final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE); 516 ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); 517 518 return methodDef; 519 } 520 521 @Override 522 public DetailAstImpl visitMethodBody(JavaLanguageParser.MethodBodyContext ctx) { 523 return flattenedTree(ctx); 524 } 525 526 @Override 527 public DetailAstImpl visitThrowsList(JavaLanguageParser.ThrowsListContext ctx) { 528 final DetailAstImpl throwsRoot = create(ctx.LITERAL_THROWS()); 529 throwsRoot.addChild(visit(ctx.qualifiedNameList())); 530 return throwsRoot; 531 } 532 533 @Override 534 public DetailAstImpl visitConstructorDeclaration( 535 JavaLanguageParser.ConstructorDeclarationContext ctx) { 536 final DetailAstImpl constructorDeclaration = createImaginary(TokenTypes.CTOR_DEF); 537 constructorDeclaration.addChild(createModifiers(ctx.mods)); 538 processChildren(constructorDeclaration, ctx.children); 539 return constructorDeclaration; 540 } 541 542 @Override 543 public DetailAstImpl visitFieldDeclaration(JavaLanguageParser.FieldDeclarationContext ctx) { 544 final DetailAstImpl dummyNode = new DetailAstImpl(); 545 // Since the TYPE AST is built by visitVariableDeclarator(), we skip it here (child [0]) 546 // We also append the SEMI token to the first child [size() - 1], 547 // until https://github.com/checkstyle/checkstyle/issues/3151 548 processChildren(dummyNode, ctx.children.subList(1, ctx.children.size() - 1)); 549 dummyNode.getFirstChild().addChild(create(ctx.SEMI())); 550 return dummyNode.getFirstChild(); 551 } 552 553 @Override 554 public DetailAstImpl visitInterfaceBodyDeclaration( 555 JavaLanguageParser.InterfaceBodyDeclarationContext ctx) { 556 final DetailAstImpl returnTree; 557 if (ctx.SEMI() == null) { 558 returnTree = visit(ctx.interfaceMemberDeclaration()); 559 } 560 else { 561 returnTree = create(ctx.SEMI()); 562 } 563 return returnTree; 564 } 565 566 @Override 567 public DetailAstImpl visitInterfaceMethodDeclaration( 568 JavaLanguageParser.InterfaceMethodDeclarationContext ctx) { 569 final DetailAstImpl methodDef = createImaginary(TokenTypes.METHOD_DEF); 570 methodDef.addChild(createModifiers(ctx.mods)); 571 572 // Process all children except C style array declarators and modifiers 573 final List<ParseTree> children = ctx.children 574 .stream() 575 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) 576 .toList(); 577 processChildren(methodDef, children); 578 579 // We add C style array declarator brackets to TYPE ast 580 final DetailAstImpl typeAst = (DetailAstImpl) methodDef.findFirstToken(TokenTypes.TYPE); 581 ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); 582 583 return methodDef; 584 } 585 586 @Override 587 public DetailAstImpl visitVariableDeclarators( 588 JavaLanguageParser.VariableDeclaratorsContext ctx) { 589 return flattenedTree(ctx); 590 } 591 592 @Override 593 public DetailAstImpl visitVariableDeclarator( 594 JavaLanguageParser.VariableDeclaratorContext ctx) { 595 final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF); 596 variableDef.addChild(createModifiers(ctx.mods)); 597 598 final DetailAstImpl type = visit(ctx.type); 599 variableDef.addChild(type); 600 variableDef.addChild(visit(ctx.id())); 601 602 // Add C style array declarator brackets to TYPE ast 603 ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child))); 604 605 // If this is an assignment statement, ASSIGN becomes the parent of EXPR 606 final TerminalNode assignNode = ctx.ASSIGN(); 607 if (assignNode != null) { 608 final DetailAstImpl assign = create(assignNode); 609 variableDef.addChild(assign); 610 assign.addChild(visit(ctx.variableInitializer())); 611 } 612 return variableDef; 613 } 614 615 @Override 616 public DetailAstImpl visitVariableDeclaratorId( 617 JavaLanguageParser.VariableDeclaratorIdContext ctx) { 618 final DetailAstImpl root = new DetailAstImpl(); 619 root.addChild(createModifiers(ctx.mods)); 620 final DetailAstImpl type = visit(ctx.type); 621 root.addChild(type); 622 623 final DetailAstImpl declaratorId; 624 if (ctx.LITERAL_THIS() == null) { 625 declaratorId = visit(ctx.qualifiedName()); 626 } 627 else if (ctx.DOT() == null) { 628 declaratorId = create(ctx.LITERAL_THIS()); 629 } 630 else { 631 declaratorId = create(ctx.DOT()); 632 declaratorId.addChild(visit(ctx.qualifiedName())); 633 declaratorId.addChild(create(ctx.LITERAL_THIS())); 634 } 635 636 root.addChild(declaratorId); 637 ctx.arrayDeclarator().forEach(child -> type.addChild(visit(child))); 638 639 return root.getFirstChild(); 640 } 641 642 @Override 643 public DetailAstImpl visitArrayInitializer(JavaLanguageParser.ArrayInitializerContext ctx) { 644 final DetailAstImpl arrayInitializer = create(TokenTypes.ARRAY_INIT, ctx.start); 645 // ARRAY_INIT was child[0] 646 processChildren(arrayInitializer, ctx.children.subList(1, ctx.children.size())); 647 return arrayInitializer; 648 } 649 650 @Override 651 public DetailAstImpl visitClassOrInterfaceType( 652 JavaLanguageParser.ClassOrInterfaceTypeContext ctx) { 653 final DetailAstPair currentAST = new DetailAstPair(); 654 DetailAstPair.addAstChild(currentAST, visit(ctx.id())); 655 DetailAstPair.addAstChild(currentAST, visit(ctx.typeArguments())); 656 657 // This is how we build the annotations/ qualified name/ type parameters tree 658 for (ParserRuleContext extendedContext : ctx.extended) { 659 final DetailAstImpl dot = create(extendedContext.start); 660 DetailAstPair.makeAstRoot(currentAST, dot); 661 extendedContext.children 662 .forEach(child -> DetailAstPair.addAstChild(currentAST, visit(child))); 663 } 664 665 // Create imaginary 'TYPE' parent if specified 666 final DetailAstImpl returnTree; 667 if (ctx.createImaginaryNode) { 668 returnTree = createImaginary(TokenTypes.TYPE); 669 returnTree.addChild(currentAST.root); 670 } 671 else { 672 returnTree = currentAST.root; 673 } 674 return returnTree; 675 } 676 677 @Override 678 public DetailAstImpl visitSimpleTypeArgument( 679 JavaLanguageParser.SimpleTypeArgumentContext ctx) { 680 final DetailAstImpl typeArgument = 681 createImaginary(TokenTypes.TYPE_ARGUMENT); 682 typeArgument.addChild(visit(ctx.typeType())); 683 return typeArgument; 684 } 685 686 @Override 687 public DetailAstImpl visitWildCardTypeArgument( 688 JavaLanguageParser.WildCardTypeArgumentContext ctx) { 689 final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); 690 typeArgument.addChild(visit(ctx.annotations())); 691 typeArgument.addChild(create(TokenTypes.WILDCARD_TYPE, 692 (Token) ctx.QUESTION().getPayload())); 693 694 if (ctx.upperBound != null) { 695 final DetailAstImpl upperBound = create(TokenTypes.TYPE_UPPER_BOUNDS, ctx.upperBound); 696 upperBound.addChild(visit(ctx.typeType())); 697 typeArgument.addChild(upperBound); 698 } 699 else if (ctx.lowerBound != null) { 700 final DetailAstImpl lowerBound = create(TokenTypes.TYPE_LOWER_BOUNDS, ctx.lowerBound); 701 lowerBound.addChild(visit(ctx.typeType())); 702 typeArgument.addChild(lowerBound); 703 } 704 705 return typeArgument; 706 } 707 708 @Override 709 public DetailAstImpl visitQualifiedNameList(JavaLanguageParser.QualifiedNameListContext ctx) { 710 return flattenedTree(ctx); 711 } 712 713 @Override 714 public DetailAstImpl visitFormalParameters(JavaLanguageParser.FormalParametersContext ctx) { 715 final DetailAstImpl lparen = create(ctx.LPAREN()); 716 717 // We make a "PARAMETERS" node whether parameters exist or not 718 if (ctx.formalParameterList() == null) { 719 addLastSibling(lparen, createImaginary(TokenTypes.PARAMETERS)); 720 } 721 else { 722 addLastSibling(lparen, visit(ctx.formalParameterList())); 723 } 724 addLastSibling(lparen, create(ctx.RPAREN())); 725 return lparen; 726 } 727 728 @Override 729 public DetailAstImpl visitFormalParameterList( 730 JavaLanguageParser.FormalParameterListContext ctx) { 731 final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS); 732 processChildren(parameters, ctx.children); 733 return parameters; 734 } 735 736 @Override 737 public DetailAstImpl visitFormalParameter(JavaLanguageParser.FormalParameterContext ctx) { 738 final DetailAstImpl variableDeclaratorId = 739 visitVariableDeclaratorId(ctx.variableDeclaratorId()); 740 final DetailAstImpl parameterDef = createImaginary(TokenTypes.PARAMETER_DEF); 741 parameterDef.addChild(variableDeclaratorId); 742 return parameterDef; 743 } 744 745 @Override 746 public DetailAstImpl visitLastFormalParameter( 747 JavaLanguageParser.LastFormalParameterContext ctx) { 748 final DetailAstImpl parameterDef = 749 createImaginary(TokenTypes.PARAMETER_DEF); 750 parameterDef.addChild(visit(ctx.variableDeclaratorId())); 751 final DetailAstImpl ident = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.IDENT); 752 ident.addPreviousSibling(create(ctx.ELLIPSIS())); 753 // We attach annotations on ellipses in varargs to the 'TYPE' ast 754 final DetailAstImpl type = (DetailAstImpl) parameterDef.findFirstToken(TokenTypes.TYPE); 755 type.addChild(visit(ctx.annotations())); 756 return parameterDef; 757 } 758 759 @Override 760 public DetailAstImpl visitQualifiedName(JavaLanguageParser.QualifiedNameContext ctx) { 761 final DetailAstImpl ast = visit(ctx.id()); 762 final DetailAstPair currentAst = new DetailAstPair(); 763 DetailAstPair.addAstChild(currentAst, ast); 764 765 for (ParserRuleContext extendedContext : ctx.extended) { 766 final DetailAstImpl dot = create(extendedContext.start); 767 DetailAstPair.makeAstRoot(currentAst, dot); 768 final List<ParseTree> childList = extendedContext 769 .children.subList(1, extendedContext.children.size()); 770 processChildren(dot, childList); 771 } 772 return currentAst.getRoot(); 773 } 774 775 @Override 776 public DetailAstImpl visitLiteral(JavaLanguageParser.LiteralContext ctx) { 777 return flattenedTree(ctx); 778 } 779 780 @Override 781 public DetailAstImpl visitIntegerLiteral(JavaLanguageParser.IntegerLiteralContext ctx) { 782 final int[] longTypes = { 783 JavaLanguageLexer.DECIMAL_LITERAL_LONG, 784 JavaLanguageLexer.HEX_LITERAL_LONG, 785 JavaLanguageLexer.OCT_LITERAL_LONG, 786 JavaLanguageLexer.BINARY_LITERAL_LONG, 787 }; 788 789 final int tokenType; 790 if (TokenUtil.isOfType(ctx.start.getType(), longTypes)) { 791 tokenType = TokenTypes.NUM_LONG; 792 } 793 else { 794 tokenType = TokenTypes.NUM_INT; 795 } 796 797 return create(tokenType, ctx.start); 798 } 799 800 @Override 801 public DetailAstImpl visitFloatLiteral(JavaLanguageParser.FloatLiteralContext ctx) { 802 final DetailAstImpl floatLiteral; 803 if (TokenUtil.isOfType(ctx.start.getType(), 804 JavaLanguageLexer.DOUBLE_LITERAL, JavaLanguageLexer.HEX_DOUBLE_LITERAL)) { 805 floatLiteral = create(TokenTypes.NUM_DOUBLE, ctx.start); 806 } 807 else { 808 floatLiteral = create(TokenTypes.NUM_FLOAT, ctx.start); 809 } 810 return floatLiteral; 811 } 812 813 @Override 814 public DetailAstImpl visitTextBlockLiteral(JavaLanguageParser.TextBlockLiteralContext ctx) { 815 final DetailAstImpl textBlockLiteralBegin = create(ctx.TEXT_BLOCK_LITERAL_BEGIN()); 816 textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_CONTENT())); 817 textBlockLiteralBegin.addChild(create(ctx.TEXT_BLOCK_LITERAL_END())); 818 return textBlockLiteralBegin; 819 } 820 821 @Override 822 public DetailAstImpl visitAnnotations(JavaLanguageParser.AnnotationsContext ctx) { 823 final DetailAstImpl annotations; 824 825 if (!ctx.createImaginaryNode && ctx.anno.isEmpty()) { 826 // There are no annotations, and we don't want to create the empty node 827 annotations = null; 828 } 829 else { 830 // There are annotations, or we just want the empty node 831 annotations = createImaginary(TokenTypes.ANNOTATIONS); 832 processChildren(annotations, ctx.anno); 833 } 834 835 return annotations; 836 } 837 838 @Override 839 public DetailAstImpl visitAnnotation(JavaLanguageParser.AnnotationContext ctx) { 840 final DetailAstImpl annotation = createImaginary(TokenTypes.ANNOTATION); 841 processChildren(annotation, ctx.children); 842 return annotation; 843 } 844 845 @Override 846 public DetailAstImpl visitElementValuePairs(JavaLanguageParser.ElementValuePairsContext ctx) { 847 return flattenedTree(ctx); 848 } 849 850 @Override 851 public DetailAstImpl visitElementValuePair(JavaLanguageParser.ElementValuePairContext ctx) { 852 final DetailAstImpl elementValuePair = 853 createImaginary(TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR); 854 processChildren(elementValuePair, ctx.children); 855 return elementValuePair; 856 } 857 858 @Override 859 public DetailAstImpl visitElementValue(JavaLanguageParser.ElementValueContext ctx) { 860 return flattenedTree(ctx); 861 } 862 863 @Override 864 public DetailAstImpl visitElementValueArrayInitializer( 865 JavaLanguageParser.ElementValueArrayInitializerContext ctx) { 866 final DetailAstImpl arrayInit = 867 create(TokenTypes.ANNOTATION_ARRAY_INIT, (Token) ctx.LCURLY().getPayload()); 868 processChildren(arrayInit, ctx.children.subList(1, ctx.children.size())); 869 return arrayInit; 870 } 871 872 @Override 873 public DetailAstImpl visitAnnotationTypeDeclaration( 874 JavaLanguageParser.AnnotationTypeDeclarationContext ctx) { 875 return createTypeDeclaration(ctx, TokenTypes.ANNOTATION_DEF, ctx.mods); 876 } 877 878 @Override 879 public DetailAstImpl visitAnnotationTypeBody( 880 JavaLanguageParser.AnnotationTypeBodyContext ctx) { 881 final DetailAstImpl objBlock = createImaginary(TokenTypes.OBJBLOCK); 882 processChildren(objBlock, ctx.children); 883 return objBlock; 884 } 885 886 @Override 887 public DetailAstImpl visitAnnotationTypeElementDeclaration( 888 JavaLanguageParser.AnnotationTypeElementDeclarationContext ctx) { 889 final DetailAstImpl returnTree; 890 if (ctx.SEMI() == null) { 891 returnTree = visit(ctx.annotationTypeElementRest()); 892 } 893 else { 894 returnTree = create(ctx.SEMI()); 895 } 896 return returnTree; 897 } 898 899 @Override 900 public DetailAstImpl visitAnnotationField(JavaLanguageParser.AnnotationFieldContext ctx) { 901 final DetailAstImpl dummyNode = new DetailAstImpl(); 902 // Since the TYPE AST is built by visitAnnotationMethodOrConstantRest(), we skip it 903 // here (child [0]) 904 processChildren(dummyNode, List.of(ctx.children.get(1))); 905 // We also append the SEMI token to the first child [size() - 1], 906 // until https://github.com/checkstyle/checkstyle/issues/3151 907 dummyNode.getFirstChild().addChild(create(ctx.SEMI())); 908 return dummyNode.getFirstChild(); 909 } 910 911 @Override 912 public DetailAstImpl visitAnnotationType(JavaLanguageParser.AnnotationTypeContext ctx) { 913 return flattenedTree(ctx); 914 } 915 916 @Override 917 public DetailAstImpl visitAnnotationMethodRest( 918 JavaLanguageParser.AnnotationMethodRestContext ctx) { 919 final DetailAstImpl annotationFieldDef = 920 createImaginary(TokenTypes.ANNOTATION_FIELD_DEF); 921 annotationFieldDef.addChild(createModifiers(ctx.mods)); 922 annotationFieldDef.addChild(visit(ctx.type)); 923 924 // Process all children except C style array declarators 925 processChildren(annotationFieldDef, ctx.children.stream() 926 .filter(child -> !(child instanceof JavaLanguageParser.ArrayDeclaratorContext)) 927 .toList()); 928 929 // We add C style array declarator brackets to TYPE ast 930 final DetailAstImpl typeAst = 931 (DetailAstImpl) annotationFieldDef.findFirstToken(TokenTypes.TYPE); 932 ctx.cStyleArrDec.forEach(child -> typeAst.addChild(visit(child))); 933 934 return annotationFieldDef; 935 } 936 937 @Override 938 public DetailAstImpl visitDefaultValue(JavaLanguageParser.DefaultValueContext ctx) { 939 final DetailAstImpl defaultValue = create(ctx.LITERAL_DEFAULT()); 940 defaultValue.addChild(visit(ctx.elementValue())); 941 return defaultValue; 942 } 943 944 @Override 945 public DetailAstImpl visitConstructorBlock(JavaLanguageParser.ConstructorBlockContext ctx) { 946 final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start); 947 // SLIST was child [0] 948 processChildren(slist, ctx.children.subList(1, ctx.children.size())); 949 return slist; 950 } 951 952 @Override 953 public DetailAstImpl visitExplicitCtorCall(JavaLanguageParser.ExplicitCtorCallContext ctx) { 954 final DetailAstImpl root; 955 if (ctx.LITERAL_THIS() == null) { 956 root = create(TokenTypes.SUPER_CTOR_CALL, (Token) ctx.LITERAL_SUPER().getPayload()); 957 } 958 else { 959 root = create(TokenTypes.CTOR_CALL, (Token) ctx.LITERAL_THIS().getPayload()); 960 } 961 root.addChild(visit(ctx.typeArguments())); 962 root.addChild(visit(ctx.arguments())); 963 root.addChild(create(ctx.SEMI())); 964 return root; 965 } 966 967 @Override 968 public DetailAstImpl visitPrimaryCtorCall(JavaLanguageParser.PrimaryCtorCallContext ctx) { 969 final DetailAstImpl primaryCtorCall = create(TokenTypes.SUPER_CTOR_CALL, 970 (Token) ctx.LITERAL_SUPER().getPayload()); 971 // filter 'LITERAL_SUPER' 972 processChildren(primaryCtorCall, ctx.children.stream() 973 .filter(child -> !child.equals(ctx.LITERAL_SUPER())) 974 .toList()); 975 return primaryCtorCall; 976 } 977 978 @Override 979 public DetailAstImpl visitBlock(JavaLanguageParser.BlockContext ctx) { 980 final DetailAstImpl slist = create(TokenTypes.SLIST, ctx.start); 981 // SLIST was child [0] 982 processChildren(slist, ctx.children.subList(1, ctx.children.size())); 983 return slist; 984 } 985 986 @Override 987 public DetailAstImpl visitLocalVar(JavaLanguageParser.LocalVarContext ctx) { 988 return flattenedTree(ctx); 989 } 990 991 @Override 992 public DetailAstImpl visitBlockStat(JavaLanguageParser.BlockStatContext ctx) { 993 return flattenedTree(ctx); 994 } 995 996 @Override 997 public DetailAstImpl visitAssertExp(JavaLanguageParser.AssertExpContext ctx) { 998 final DetailAstImpl assertExp = create(ctx.ASSERT()); 999 // child[0] is 'ASSERT' 1000 processChildren(assertExp, ctx.children.subList(1, ctx.children.size())); 1001 return assertExp; 1002 } 1003 1004 @Override 1005 public DetailAstImpl visitIfStat(JavaLanguageParser.IfStatContext ctx) { 1006 final DetailAstImpl ifStat = create(ctx.LITERAL_IF()); 1007 // child[0] is 'LITERAL_IF' 1008 processChildren(ifStat, ctx.children.subList(1, ctx.children.size())); 1009 return ifStat; 1010 } 1011 1012 @Override 1013 public DetailAstImpl visitForStat(JavaLanguageParser.ForStatContext ctx) { 1014 final DetailAstImpl forInit = create(ctx.start); 1015 // child[0] is LITERAL_FOR 1016 processChildren(forInit, ctx.children.subList(1, ctx.children.size())); 1017 return forInit; 1018 } 1019 1020 @Override 1021 public DetailAstImpl visitWhileStat(JavaLanguageParser.WhileStatContext ctx) { 1022 final DetailAstImpl whileStatement = create(ctx.start); 1023 // 'LITERAL_WHILE' is child[0] 1024 processChildren(whileStatement, ctx.children.subList(1, ctx.children.size())); 1025 return whileStatement; 1026 } 1027 1028 @Override 1029 public DetailAstImpl visitDoStat(JavaLanguageParser.DoStatContext ctx) { 1030 final DetailAstImpl doStatement = create(ctx.start); 1031 // 'LITERAL_DO' is child[0] 1032 doStatement.addChild(visit(ctx.statement())); 1033 // We make 'LITERAL_WHILE' into 'DO_WHILE' 1034 doStatement.addChild(create(TokenTypes.DO_WHILE, (Token) ctx.LITERAL_WHILE().getPayload())); 1035 doStatement.addChild(visit(ctx.parExpression())); 1036 doStatement.addChild(create(ctx.SEMI())); 1037 return doStatement; 1038 } 1039 1040 @Override 1041 public DetailAstImpl visitTryStat(JavaLanguageParser.TryStatContext ctx) { 1042 final DetailAstImpl tryStat = create(ctx.start); 1043 // child[0] is 'LITERAL_TRY' 1044 processChildren(tryStat, ctx.children.subList(1, ctx.children.size())); 1045 return tryStat; 1046 } 1047 1048 @Override 1049 public DetailAstImpl visitTryWithResourceStat( 1050 JavaLanguageParser.TryWithResourceStatContext ctx) { 1051 final DetailAstImpl tryWithResources = create(ctx.LITERAL_TRY()); 1052 // child[0] is 'LITERAL_TRY' 1053 processChildren(tryWithResources, ctx.children.subList(1, ctx.children.size())); 1054 return tryWithResources; 1055 } 1056 1057 @Override 1058 public DetailAstImpl visitYieldStat(JavaLanguageParser.YieldStatContext ctx) { 1059 final DetailAstImpl yieldParent = create(ctx.LITERAL_YIELD()); 1060 // LITERAL_YIELD is child[0] 1061 processChildren(yieldParent, ctx.children.subList(1, ctx.children.size())); 1062 return yieldParent; 1063 } 1064 1065 @Override 1066 public DetailAstImpl visitSyncStat(JavaLanguageParser.SyncStatContext ctx) { 1067 final DetailAstImpl syncStatement = create(ctx.start); 1068 // child[0] is 'LITERAL_SYNCHRONIZED' 1069 processChildren(syncStatement, ctx.children.subList(1, ctx.children.size())); 1070 return syncStatement; 1071 } 1072 1073 @Override 1074 public DetailAstImpl visitReturnStat(JavaLanguageParser.ReturnStatContext ctx) { 1075 final DetailAstImpl returnStat = create(ctx.LITERAL_RETURN()); 1076 // child[0] is 'LITERAL_RETURN' 1077 processChildren(returnStat, ctx.children.subList(1, ctx.children.size())); 1078 return returnStat; 1079 } 1080 1081 @Override 1082 public DetailAstImpl visitThrowStat(JavaLanguageParser.ThrowStatContext ctx) { 1083 final DetailAstImpl throwStat = create(ctx.LITERAL_THROW()); 1084 // child[0] is 'LITERAL_THROW' 1085 processChildren(throwStat, ctx.children.subList(1, ctx.children.size())); 1086 return throwStat; 1087 } 1088 1089 @Override 1090 public DetailAstImpl visitBreakStat(JavaLanguageParser.BreakStatContext ctx) { 1091 final DetailAstImpl literalBreak = create(ctx.LITERAL_BREAK()); 1092 // child[0] is 'LITERAL_BREAK' 1093 processChildren(literalBreak, ctx.children.subList(1, ctx.children.size())); 1094 return literalBreak; 1095 } 1096 1097 @Override 1098 public DetailAstImpl visitContinueStat(JavaLanguageParser.ContinueStatContext ctx) { 1099 final DetailAstImpl continueStat = create(ctx.LITERAL_CONTINUE()); 1100 // child[0] is 'LITERAL_CONTINUE' 1101 processChildren(continueStat, ctx.children.subList(1, ctx.children.size())); 1102 return continueStat; 1103 } 1104 1105 @Override 1106 public DetailAstImpl visitEmptyStat(JavaLanguageParser.EmptyStatContext ctx) { 1107 return create(TokenTypes.EMPTY_STAT, ctx.start); 1108 } 1109 1110 @Override 1111 public DetailAstImpl visitExpStat(JavaLanguageParser.ExpStatContext ctx) { 1112 final DetailAstImpl expStatRoot = visit(ctx.statementExpression); 1113 addLastSibling(expStatRoot, create(ctx.SEMI())); 1114 return expStatRoot; 1115 } 1116 1117 @Override 1118 public DetailAstImpl visitLabelStat(JavaLanguageParser.LabelStatContext ctx) { 1119 final DetailAstImpl labelStat = create(TokenTypes.LABELED_STAT, 1120 (Token) ctx.COLON().getPayload()); 1121 labelStat.addChild(visit(ctx.id())); 1122 labelStat.addChild(visit(ctx.statement())); 1123 return labelStat; 1124 } 1125 1126 @Override 1127 public DetailAstImpl visitSwitchExpressionOrStatement( 1128 JavaLanguageParser.SwitchExpressionOrStatementContext ctx) { 1129 final DetailAstImpl switchStat = create(ctx.LITERAL_SWITCH()); 1130 switchStat.addChild(visit(ctx.parExpression())); 1131 switchStat.addChild(create(ctx.LCURLY())); 1132 switchStat.addChild(visit(ctx.switchBlock())); 1133 switchStat.addChild(create(ctx.RCURLY())); 1134 return switchStat; 1135 } 1136 1137 @Override 1138 public DetailAstImpl visitSwitchRules(JavaLanguageParser.SwitchRulesContext ctx) { 1139 final DetailAstImpl dummyRoot = new DetailAstImpl(); 1140 ctx.switchLabeledRule().forEach(switchLabeledRuleContext -> { 1141 final DetailAstImpl switchRule = visit(switchLabeledRuleContext); 1142 final DetailAstImpl switchRuleParent = createImaginary(TokenTypes.SWITCH_RULE); 1143 switchRuleParent.addChild(switchRule); 1144 dummyRoot.addChild(switchRuleParent); 1145 }); 1146 return dummyRoot.getFirstChild(); 1147 } 1148 1149 @Override 1150 public DetailAstImpl visitSwitchBlocks(JavaLanguageParser.SwitchBlocksContext ctx) { 1151 final DetailAstImpl dummyRoot = new DetailAstImpl(); 1152 ctx.groups.forEach(group -> dummyRoot.addChild(visit(group))); 1153 1154 // Add any empty switch labels to end of statement in one 'CASE_GROUP' 1155 if (!ctx.emptyLabels.isEmpty()) { 1156 final DetailAstImpl emptyLabelParent = 1157 createImaginary(TokenTypes.CASE_GROUP); 1158 ctx.emptyLabels.forEach(label -> emptyLabelParent.addChild(visit(label))); 1159 dummyRoot.addChild(emptyLabelParent); 1160 } 1161 return dummyRoot.getFirstChild(); 1162 } 1163 1164 @Override 1165 public DetailAstImpl visitSwitchLabeledExpression( 1166 JavaLanguageParser.SwitchLabeledExpressionContext ctx) { 1167 return flattenedTree(ctx); 1168 } 1169 1170 @Override 1171 public DetailAstImpl visitSwitchLabeledBlock( 1172 JavaLanguageParser.SwitchLabeledBlockContext ctx) { 1173 return flattenedTree(ctx); 1174 } 1175 1176 @Override 1177 public DetailAstImpl visitSwitchLabeledThrow( 1178 JavaLanguageParser.SwitchLabeledThrowContext ctx) { 1179 final DetailAstImpl switchLabel = visit(ctx.switchLabel()); 1180 addLastSibling(switchLabel, create(ctx.LAMBDA())); 1181 final DetailAstImpl literalThrow = create(ctx.LITERAL_THROW()); 1182 literalThrow.addChild(visit(ctx.expression())); 1183 literalThrow.addChild(create(ctx.SEMI())); 1184 addLastSibling(switchLabel, literalThrow); 1185 return switchLabel; 1186 } 1187 1188 @Override 1189 public DetailAstImpl visitElseStat(JavaLanguageParser.ElseStatContext ctx) { 1190 final DetailAstImpl elseStat = create(ctx.LITERAL_ELSE()); 1191 // child[0] is 'LITERAL_ELSE' 1192 processChildren(elseStat, ctx.children.subList(1, ctx.children.size())); 1193 return elseStat; 1194 } 1195 1196 @Override 1197 public DetailAstImpl visitCatchClause(JavaLanguageParser.CatchClauseContext ctx) { 1198 final DetailAstImpl catchClause = create(TokenTypes.LITERAL_CATCH, 1199 (Token) ctx.LITERAL_CATCH().getPayload()); 1200 // 'LITERAL_CATCH' is child[0] 1201 processChildren(catchClause, ctx.children.subList(1, ctx.children.size())); 1202 return catchClause; 1203 } 1204 1205 @Override 1206 public DetailAstImpl visitCatchParameter(JavaLanguageParser.CatchParameterContext ctx) { 1207 final DetailAstImpl catchParameterDef = createImaginary(TokenTypes.PARAMETER_DEF); 1208 catchParameterDef.addChild(createModifiers(ctx.mods)); 1209 // filter mods 1210 processChildren(catchParameterDef, ctx.children.stream() 1211 .filter(child -> !(child instanceof JavaLanguageParser.VariableModifierContext)) 1212 .toList()); 1213 return catchParameterDef; 1214 } 1215 1216 @Override 1217 public DetailAstImpl visitCatchType(JavaLanguageParser.CatchTypeContext ctx) { 1218 final DetailAstImpl type = createImaginary(TokenTypes.TYPE); 1219 processChildren(type, ctx.children); 1220 return type; 1221 } 1222 1223 @Override 1224 public DetailAstImpl visitFinallyBlock(JavaLanguageParser.FinallyBlockContext ctx) { 1225 final DetailAstImpl finallyBlock = create(ctx.LITERAL_FINALLY()); 1226 // child[0] is 'LITERAL_FINALLY' 1227 processChildren(finallyBlock, ctx.children.subList(1, ctx.children.size())); 1228 return finallyBlock; 1229 } 1230 1231 @Override 1232 public DetailAstImpl visitResourceSpecification( 1233 JavaLanguageParser.ResourceSpecificationContext ctx) { 1234 final DetailAstImpl resourceSpecification = 1235 createImaginary(TokenTypes.RESOURCE_SPECIFICATION); 1236 processChildren(resourceSpecification, ctx.children); 1237 return resourceSpecification; 1238 } 1239 1240 @Override 1241 public DetailAstImpl visitResources(JavaLanguageParser.ResourcesContext ctx) { 1242 final DetailAstImpl firstResource = visit(ctx.resource(0)); 1243 final DetailAstImpl resources = createImaginary(TokenTypes.RESOURCES); 1244 resources.addChild(firstResource); 1245 processChildren(resources, ctx.children.subList(1, ctx.children.size())); 1246 return resources; 1247 } 1248 1249 @Override 1250 public DetailAstImpl visitResourceDeclaration( 1251 JavaLanguageParser.ResourceDeclarationContext ctx) { 1252 final DetailAstImpl resource = createImaginary(TokenTypes.RESOURCE); 1253 resource.addChild(visit(ctx.variableDeclaratorId())); 1254 1255 final DetailAstImpl assign = create(ctx.ASSIGN()); 1256 resource.addChild(assign); 1257 assign.addChild(visit(ctx.expression())); 1258 return resource; 1259 } 1260 1261 @Override 1262 public DetailAstImpl visitVariableAccess(JavaLanguageParser.VariableAccessContext ctx) { 1263 final DetailAstImpl resource = createImaginary(TokenTypes.RESOURCE); 1264 1265 final DetailAstImpl childNode; 1266 if (ctx.LITERAL_THIS() == null) { 1267 childNode = visit(ctx.id()); 1268 } 1269 else { 1270 childNode = create(ctx.LITERAL_THIS()); 1271 } 1272 1273 if (ctx.accessList.isEmpty()) { 1274 resource.addChild(childNode); 1275 } 1276 else { 1277 final DetailAstPair currentAst = new DetailAstPair(); 1278 ctx.accessList.forEach(fieldAccess -> { 1279 DetailAstPair.addAstChild(currentAst, visit(fieldAccess.expr())); 1280 DetailAstPair.makeAstRoot(currentAst, create(fieldAccess.DOT())); 1281 }); 1282 resource.addChild(currentAst.getRoot()); 1283 resource.getFirstChild().addChild(childNode); 1284 } 1285 return resource; 1286 } 1287 1288 @Override 1289 public DetailAstImpl visitSwitchBlockStatementGroup( 1290 JavaLanguageParser.SwitchBlockStatementGroupContext ctx) { 1291 final DetailAstImpl caseGroup = createImaginary(TokenTypes.CASE_GROUP); 1292 processChildren(caseGroup, ctx.switchLabel()); 1293 final DetailAstImpl sList = createImaginary(TokenTypes.SLIST); 1294 processChildren(sList, ctx.slists); 1295 caseGroup.addChild(sList); 1296 return caseGroup; 1297 } 1298 1299 @Override 1300 public DetailAstImpl visitCaseLabel(JavaLanguageParser.CaseLabelContext ctx) { 1301 final DetailAstImpl caseLabel = create(ctx.LITERAL_CASE()); 1302 // child [0] is 'LITERAL_CASE' 1303 processChildren(caseLabel, ctx.children.subList(1, ctx.children.size())); 1304 return caseLabel; 1305 } 1306 1307 @Override 1308 public DetailAstImpl visitDefaultLabel(JavaLanguageParser.DefaultLabelContext ctx) { 1309 final DetailAstImpl defaultLabel = create(ctx.LITERAL_DEFAULT()); 1310 if (ctx.COLON() != null) { 1311 defaultLabel.addChild(create(ctx.COLON())); 1312 } 1313 return defaultLabel; 1314 } 1315 1316 @Override 1317 public DetailAstImpl visitCaseConstants(JavaLanguageParser.CaseConstantsContext ctx) { 1318 return flattenedTree(ctx); 1319 } 1320 1321 @Override 1322 public DetailAstImpl visitCaseConstant(JavaLanguageParser.CaseConstantContext ctx) { 1323 return flattenedTree(ctx); 1324 } 1325 1326 @Override 1327 public DetailAstImpl visitEnhancedFor(JavaLanguageParser.EnhancedForContext ctx) { 1328 final DetailAstImpl leftParen = create(ctx.LPAREN()); 1329 final DetailAstImpl enhancedForControl = 1330 visit(ctx.getChild(1)); 1331 final DetailAstImpl forEachClause = createImaginary(TokenTypes.FOR_EACH_CLAUSE); 1332 forEachClause.addChild(enhancedForControl); 1333 addLastSibling(leftParen, forEachClause); 1334 addLastSibling(leftParen, create(ctx.RPAREN())); 1335 return leftParen; 1336 } 1337 1338 @Override 1339 public DetailAstImpl visitForFor(JavaLanguageParser.ForForContext ctx) { 1340 final DetailAstImpl dummyRoot = new DetailAstImpl(); 1341 dummyRoot.addChild(create(ctx.LPAREN())); 1342 1343 if (ctx.forInit() == null) { 1344 final DetailAstImpl imaginaryForInitParent = 1345 createImaginary(TokenTypes.FOR_INIT); 1346 dummyRoot.addChild(imaginaryForInitParent); 1347 } 1348 else { 1349 dummyRoot.addChild(visit(ctx.forInit())); 1350 } 1351 1352 dummyRoot.addChild(create(ctx.SEMI(0))); 1353 1354 final DetailAstImpl forCondParent = createImaginary(TokenTypes.FOR_CONDITION); 1355 forCondParent.addChild(visit(ctx.forCond)); 1356 dummyRoot.addChild(forCondParent); 1357 dummyRoot.addChild(create(ctx.SEMI(1))); 1358 1359 final DetailAstImpl forItParent = createImaginary(TokenTypes.FOR_ITERATOR); 1360 forItParent.addChild(visit(ctx.forUpdate)); 1361 dummyRoot.addChild(forItParent); 1362 1363 dummyRoot.addChild(create(ctx.RPAREN())); 1364 1365 return dummyRoot.getFirstChild(); 1366 } 1367 1368 @Override 1369 public DetailAstImpl visitForInit(JavaLanguageParser.ForInitContext ctx) { 1370 final DetailAstImpl forInit = createImaginary(TokenTypes.FOR_INIT); 1371 processChildren(forInit, ctx.children); 1372 return forInit; 1373 } 1374 1375 @Override 1376 public DetailAstImpl visitEnhancedForControl( 1377 JavaLanguageParser.EnhancedForControlContext ctx) { 1378 final DetailAstImpl variableDeclaratorId = 1379 visit(ctx.variableDeclaratorId()); 1380 final DetailAstImpl variableDef = createImaginary(TokenTypes.VARIABLE_DEF); 1381 variableDef.addChild(variableDeclaratorId); 1382 1383 addLastSibling(variableDef, create(ctx.COLON())); 1384 addLastSibling(variableDef, visit(ctx.expression())); 1385 return variableDef; 1386 } 1387 1388 @Override 1389 public DetailAstImpl visitEnhancedForControlWithRecordPattern( 1390 JavaLanguageParser.EnhancedForControlWithRecordPatternContext ctx) { 1391 final DetailAstImpl recordPattern = 1392 visit(ctx.pattern()); 1393 addLastSibling(recordPattern, create(ctx.COLON())); 1394 addLastSibling(recordPattern, visit(ctx.expression())); 1395 return recordPattern; 1396 } 1397 1398 @Override 1399 public DetailAstImpl visitParExpression(JavaLanguageParser.ParExpressionContext ctx) { 1400 return flattenedTree(ctx); 1401 } 1402 1403 @Override 1404 public DetailAstImpl visitExpressionList(JavaLanguageParser.ExpressionListContext ctx) { 1405 final DetailAstImpl elist = createImaginary(TokenTypes.ELIST); 1406 processChildren(elist, ctx.children); 1407 return elist; 1408 } 1409 1410 @Override 1411 public DetailAstImpl visitExpression(JavaLanguageParser.ExpressionContext ctx) { 1412 return buildExpressionNode(ctx.expr()); 1413 } 1414 1415 @Override 1416 public DetailAstImpl visitRefOp(JavaLanguageParser.RefOpContext ctx) { 1417 final DetailAstImpl bop = create(ctx.bop); 1418 final DetailAstImpl leftChild = visit(ctx.expr()); 1419 final DetailAstImpl rightChild = create(TokenTypes.IDENT, ctx.stop); 1420 bop.addChild(leftChild); 1421 bop.addChild(rightChild); 1422 return bop; 1423 } 1424 1425 @Override 1426 public DetailAstImpl visitSuperExp(JavaLanguageParser.SuperExpContext ctx) { 1427 final DetailAstImpl bop = create(ctx.bop); 1428 bop.addChild(visit(ctx.expr())); 1429 bop.addChild(create(ctx.LITERAL_SUPER())); 1430 DetailAstImpl superSuffixParent = visit(ctx.superSuffix()); 1431 1432 if (superSuffixParent == null) { 1433 superSuffixParent = bop; 1434 } 1435 else { 1436 DetailAstImpl firstChild = superSuffixParent; 1437 while (firstChild.getFirstChild() != null) { 1438 firstChild = firstChild.getFirstChild(); 1439 } 1440 firstChild.addPreviousSibling(bop); 1441 } 1442 1443 return superSuffixParent; 1444 } 1445 1446 @Override 1447 public DetailAstImpl visitInstanceOfExp(JavaLanguageParser.InstanceOfExpContext ctx) { 1448 final DetailAstImpl literalInstanceOf = create(ctx.LITERAL_INSTANCEOF()); 1449 literalInstanceOf.addChild(visit(ctx.expr())); 1450 final ParseTree patternOrType = ctx.getChild(2); 1451 final DetailAstImpl patternDef = visit(patternOrType); 1452 literalInstanceOf.addChild(patternDef); 1453 return literalInstanceOf; 1454 } 1455 1456 @Override 1457 public DetailAstImpl visitBitShift(JavaLanguageParser.BitShiftContext ctx) { 1458 final DetailAstImpl shiftOperation; 1459 1460 // We determine the type of shift operation in the parser, instead of the 1461 // lexer as in older grammars. This makes it easier to parse type parameters 1462 // and less than/ greater than operators in general. 1463 if (ctx.LT().size() == LEFT_SHIFT.length()) { 1464 shiftOperation = create(TokenTypes.SL, (Token) ctx.LT(0).getPayload()); 1465 shiftOperation.setText(LEFT_SHIFT); 1466 } 1467 else if (ctx.GT().size() == UNSIGNED_RIGHT_SHIFT.length()) { 1468 shiftOperation = create(TokenTypes.BSR, (Token) ctx.GT(0).getPayload()); 1469 shiftOperation.setText(UNSIGNED_RIGHT_SHIFT); 1470 } 1471 else { 1472 shiftOperation = create(TokenTypes.SR, (Token) ctx.GT(0).getPayload()); 1473 shiftOperation.setText(RIGHT_SHIFT); 1474 } 1475 1476 shiftOperation.addChild(visit(ctx.expr(0))); 1477 shiftOperation.addChild(visit(ctx.expr(1))); 1478 return shiftOperation; 1479 } 1480 1481 @Override 1482 public DetailAstImpl visitNewExp(JavaLanguageParser.NewExpContext ctx) { 1483 final DetailAstImpl newExp = create(ctx.LITERAL_NEW()); 1484 // child [0] is LITERAL_NEW 1485 processChildren(newExp, ctx.children.subList(1, ctx.children.size())); 1486 return newExp; 1487 } 1488 1489 @Override 1490 public DetailAstImpl visitPrefix(JavaLanguageParser.PrefixContext ctx) { 1491 final int tokenType = switch (ctx.prefix.getType()) { 1492 case JavaLanguageLexer.PLUS -> TokenTypes.UNARY_PLUS; 1493 case JavaLanguageLexer.MINUS -> TokenTypes.UNARY_MINUS; 1494 default -> ctx.prefix.getType(); 1495 }; 1496 final DetailAstImpl prefix = create(tokenType, ctx.prefix); 1497 prefix.addChild(visit(ctx.expr())); 1498 return prefix; 1499 } 1500 1501 @Override 1502 public DetailAstImpl visitCastExp(JavaLanguageParser.CastExpContext ctx) { 1503 final DetailAstImpl cast = create(TokenTypes.TYPECAST, (Token) ctx.LPAREN().getPayload()); 1504 // child [0] is LPAREN 1505 processChildren(cast, ctx.children.subList(1, ctx.children.size())); 1506 return cast; 1507 } 1508 1509 @Override 1510 public DetailAstImpl visitIndexOp(JavaLanguageParser.IndexOpContext ctx) { 1511 // LBRACK -> INDEX_OP is root of this AST 1512 final DetailAstImpl indexOp = create(TokenTypes.INDEX_OP, 1513 (Token) ctx.LBRACK().getPayload()); 1514 1515 // add expression(IDENT) on LHS 1516 indexOp.addChild(visit(ctx.expr(0))); 1517 1518 // create imaginary node for expression on RHS 1519 final DetailAstImpl expr = visit(ctx.expr(1)); 1520 final DetailAstImpl imaginaryExpr = createImaginary(TokenTypes.EXPR); 1521 imaginaryExpr.addChild(expr); 1522 indexOp.addChild(imaginaryExpr); 1523 1524 // complete AST by adding RBRACK 1525 indexOp.addChild(create(ctx.RBRACK())); 1526 return indexOp; 1527 } 1528 1529 @Override 1530 public DetailAstImpl visitInvOp(JavaLanguageParser.InvOpContext ctx) { 1531 final DetailAstPair currentAst = new DetailAstPair(); 1532 1533 final DetailAstImpl returnAst = visit(ctx.expr()); 1534 DetailAstPair.addAstChild(currentAst, returnAst); 1535 DetailAstPair.makeAstRoot(currentAst, create(ctx.bop)); 1536 1537 DetailAstPair.addAstChild(currentAst, 1538 visit(ctx.nonWildcardTypeArguments())); 1539 DetailAstPair.addAstChild(currentAst, visit(ctx.id())); 1540 final DetailAstImpl lparen = create(TokenTypes.METHOD_CALL, 1541 (Token) ctx.LPAREN().getPayload()); 1542 DetailAstPair.makeAstRoot(currentAst, lparen); 1543 1544 // We always add an 'ELIST' node 1545 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1546 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1547 1548 DetailAstPair.addAstChild(currentAst, expressionList); 1549 DetailAstPair.addAstChild(currentAst, create(ctx.RPAREN())); 1550 1551 return currentAst.root; 1552 } 1553 1554 @Override 1555 public DetailAstImpl visitInitExp(JavaLanguageParser.InitExpContext ctx) { 1556 final DetailAstImpl dot = create(ctx.bop); 1557 dot.addChild(visit(ctx.expr())); 1558 final DetailAstImpl literalNew = create(ctx.LITERAL_NEW()); 1559 literalNew.addChild(visit(ctx.nonWildcardTypeArguments())); 1560 literalNew.addChild(visit(ctx.innerCreator())); 1561 dot.addChild(literalNew); 1562 return dot; 1563 } 1564 1565 @Override 1566 public DetailAstImpl visitSimpleMethodCall(JavaLanguageParser.SimpleMethodCallContext ctx) { 1567 final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, 1568 (Token) ctx.LPAREN().getPayload()); 1569 methodCall.addChild(visit(ctx.id())); 1570 // We always add an 'ELIST' node 1571 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1572 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1573 1574 methodCall.addChild(expressionList); 1575 methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); 1576 return methodCall; 1577 } 1578 1579 @Override 1580 public DetailAstImpl visitLambdaExp(JavaLanguageParser.LambdaExpContext ctx) { 1581 final DetailAstImpl lambda = create(ctx.LAMBDA()); 1582 lambda.addChild(visit(ctx.lambdaParameters())); 1583 1584 final JavaLanguageParser.BlockContext blockContext = ctx.block(); 1585 final DetailAstImpl rightHandLambdaChild; 1586 if (blockContext != null) { 1587 rightHandLambdaChild = visit(blockContext); 1588 } 1589 else { 1590 // Lambda expression child is built the same way that we build 1591 // the initial expression node in visitExpression, i.e. with 1592 // an imaginary EXPR node. This results in nested EXPR nodes 1593 // in the AST. 1594 rightHandLambdaChild = buildExpressionNode(ctx.expr()); 1595 } 1596 lambda.addChild(rightHandLambdaChild); 1597 return lambda; 1598 } 1599 1600 @Override 1601 public DetailAstImpl visitThisExp(JavaLanguageParser.ThisExpContext ctx) { 1602 final DetailAstImpl bop = create(ctx.bop); 1603 bop.addChild(visit(ctx.expr())); 1604 bop.addChild(create(ctx.LITERAL_THIS())); 1605 return bop; 1606 } 1607 1608 @Override 1609 public DetailAstImpl visitPrimaryExp(JavaLanguageParser.PrimaryExpContext ctx) { 1610 return flattenedTree(ctx); 1611 } 1612 1613 @Override 1614 public DetailAstImpl visitPostfix(JavaLanguageParser.PostfixContext ctx) { 1615 final DetailAstImpl postfix; 1616 if (ctx.postfix.getType() == JavaLanguageLexer.INC) { 1617 postfix = create(TokenTypes.POST_INC, ctx.postfix); 1618 } 1619 else { 1620 postfix = create(TokenTypes.POST_DEC, ctx.postfix); 1621 } 1622 postfix.addChild(visit(ctx.expr())); 1623 return postfix; 1624 } 1625 1626 @Override 1627 public DetailAstImpl visitMethodRef(JavaLanguageParser.MethodRefContext ctx) { 1628 final DetailAstImpl doubleColon = create(TokenTypes.METHOD_REF, 1629 (Token) ctx.DOUBLE_COLON().getPayload()); 1630 final List<ParseTree> children = ctx.children.stream() 1631 .filter(child -> !child.equals(ctx.DOUBLE_COLON())) 1632 .toList(); 1633 processChildren(doubleColon, children); 1634 return doubleColon; 1635 } 1636 1637 @Override 1638 public DetailAstImpl visitTernaryOp(JavaLanguageParser.TernaryOpContext ctx) { 1639 final DetailAstImpl root = create(ctx.QUESTION()); 1640 processChildren(root, ctx.children.stream() 1641 .filter(child -> !child.equals(ctx.QUESTION())) 1642 .toList()); 1643 return root; 1644 } 1645 1646 @Override 1647 public DetailAstImpl visitBinOp(JavaLanguageParser.BinOpContext ctx) { 1648 final DetailAstImpl bop = create(ctx.bop); 1649 1650 // To improve performance, we iterate through binary operations 1651 // since they are frequently deeply nested. 1652 final List<JavaLanguageParser.BinOpContext> binOpList = new ArrayList<>(); 1653 ParseTree firstExpression = ctx.expr(0); 1654 while (firstExpression instanceof JavaLanguageParser.BinOpContext) { 1655 // Get all nested binOps 1656 binOpList.add((JavaLanguageParser.BinOpContext) firstExpression); 1657 firstExpression = ((JavaLanguageParser.BinOpContext) firstExpression).expr(0); 1658 } 1659 1660 if (binOpList.isEmpty()) { 1661 final DetailAstImpl leftChild = visit(ctx.children.getFirst()); 1662 bop.addChild(leftChild); 1663 } 1664 else { 1665 // Map all descendants to individual AST's since we can parallelize this 1666 // operation 1667 final Queue<DetailAstImpl> descendantList = binOpList.parallelStream() 1668 .map(this::getInnerBopAst) 1669 .collect(Collectors.toCollection(ArrayDeque::new)); 1670 1671 bop.addChild(descendantList.poll()); 1672 DetailAstImpl pointer = bop.getFirstChild(); 1673 // Build tree 1674 for (DetailAstImpl descendant : descendantList) { 1675 pointer.getFirstChild().addPreviousSibling(descendant); 1676 pointer = descendant; 1677 } 1678 } 1679 1680 bop.addChild(visit(ctx.children.get(2))); 1681 return bop; 1682 } 1683 1684 /** 1685 * Builds the binary operation (binOp) AST. 1686 * 1687 * @param descendant the BinOpContext to build AST from 1688 * @return binOp AST 1689 */ 1690 private DetailAstImpl getInnerBopAst(JavaLanguageParser.BinOpContext descendant) { 1691 final DetailAstImpl innerBop = create(descendant.bop); 1692 final JavaLanguageParser.ExprContext expr = descendant.expr(0); 1693 if (!(expr instanceof JavaLanguageParser.BinOpContext)) { 1694 innerBop.addChild(visit(expr)); 1695 } 1696 innerBop.addChild(visit(descendant.expr(1))); 1697 return innerBop; 1698 } 1699 1700 @Override 1701 public DetailAstImpl visitMethodCall(JavaLanguageParser.MethodCallContext ctx) { 1702 final DetailAstImpl methodCall = create(TokenTypes.METHOD_CALL, 1703 (Token) ctx.LPAREN().getPayload()); 1704 // We always add an 'ELIST' node 1705 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 1706 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 1707 1708 final DetailAstImpl dot = create(ctx.DOT()); 1709 dot.addChild(visit(ctx.expr())); 1710 dot.addChild(visit(ctx.id())); 1711 methodCall.addChild(dot); 1712 methodCall.addChild(expressionList); 1713 methodCall.addChild(create((Token) ctx.RPAREN().getPayload())); 1714 return methodCall; 1715 } 1716 1717 @Override 1718 public DetailAstImpl visitTypeCastParameters( 1719 JavaLanguageParser.TypeCastParametersContext ctx) { 1720 final DetailAstImpl typeType = visit(ctx.typeType(0)); 1721 for (int i = 0; i < ctx.BAND().size(); i++) { 1722 addLastSibling(typeType, create(TokenTypes.TYPE_EXTENSION_AND, 1723 (Token) ctx.BAND(i).getPayload())); 1724 addLastSibling(typeType, visit(ctx.typeType(i + 1))); 1725 } 1726 return typeType; 1727 } 1728 1729 @Override 1730 public DetailAstImpl visitSingleLambdaParam(JavaLanguageParser.SingleLambdaParamContext ctx) { 1731 return flattenedTree(ctx); 1732 } 1733 1734 @Override 1735 public DetailAstImpl visitFormalLambdaParam(JavaLanguageParser.FormalLambdaParamContext ctx) { 1736 final DetailAstImpl lparen = create(ctx.LPAREN()); 1737 1738 // We add an 'PARAMETERS' node here whether it exists or not 1739 final DetailAstImpl parameters = Optional.ofNullable(visit(ctx.formalParameterList())) 1740 .orElseGet(() -> createImaginary(TokenTypes.PARAMETERS)); 1741 addLastSibling(lparen, parameters); 1742 addLastSibling(lparen, create(ctx.RPAREN())); 1743 return lparen; 1744 } 1745 1746 @Override 1747 public DetailAstImpl visitMultiLambdaParam(JavaLanguageParser.MultiLambdaParamContext ctx) { 1748 final DetailAstImpl lparen = create(ctx.LPAREN()); 1749 addLastSibling(lparen, visit(ctx.multiLambdaParams())); 1750 addLastSibling(lparen, create(ctx.RPAREN())); 1751 return lparen; 1752 } 1753 1754 @Override 1755 public DetailAstImpl visitMultiLambdaParams(JavaLanguageParser.MultiLambdaParamsContext ctx) { 1756 final DetailAstImpl parameters = createImaginary(TokenTypes.PARAMETERS); 1757 parameters.addChild(createLambdaParameter(ctx.id(0))); 1758 1759 for (int i = 0; i < ctx.COMMA().size(); i++) { 1760 parameters.addChild(create(ctx.COMMA(i))); 1761 parameters.addChild(createLambdaParameter(ctx.id(i + 1))); 1762 } 1763 return parameters; 1764 } 1765 1766 /** 1767 * Creates a 'PARAMETER_DEF' node for a lambda expression, with 1768 * imaginary modifier and type nodes. 1769 * 1770 * @param ctx the IdContext to create imaginary nodes for 1771 * @return DetailAstImpl of lambda parameter 1772 */ 1773 private DetailAstImpl createLambdaParameter(JavaLanguageParser.IdContext ctx) { 1774 final DetailAstImpl ident = visitId(ctx); 1775 final DetailAstImpl parameter = createImaginary(TokenTypes.PARAMETER_DEF); 1776 final DetailAstImpl modifiers = createImaginary(TokenTypes.MODIFIERS); 1777 final DetailAstImpl type = createImaginary(TokenTypes.TYPE); 1778 parameter.addChild(modifiers); 1779 parameter.addChild(type); 1780 parameter.addChild(ident); 1781 return parameter; 1782 } 1783 1784 @Override 1785 public DetailAstImpl visitParenPrimary(JavaLanguageParser.ParenPrimaryContext ctx) { 1786 return flattenedTree(ctx); 1787 } 1788 1789 @Override 1790 public DetailAstImpl visitTokenPrimary(JavaLanguageParser.TokenPrimaryContext ctx) { 1791 return flattenedTree(ctx); 1792 } 1793 1794 @Override 1795 public DetailAstImpl visitClassRefPrimary(JavaLanguageParser.ClassRefPrimaryContext ctx) { 1796 final DetailAstImpl dot = create(ctx.DOT()); 1797 final DetailAstImpl primaryTypeNoArray = visit(ctx.type); 1798 dot.addChild(primaryTypeNoArray); 1799 if (TokenUtil.isOfType(primaryTypeNoArray, TokenTypes.DOT)) { 1800 // We append '[]' to the qualified name 'TYPE' `ast 1801 ctx.arrayDeclarator() 1802 .forEach(child -> primaryTypeNoArray.addChild(visit(child))); 1803 } 1804 else { 1805 ctx.arrayDeclarator() 1806 .forEach(child -> addLastSibling(primaryTypeNoArray, visit(child))); 1807 } 1808 dot.addChild(create(ctx.LITERAL_CLASS())); 1809 return dot; 1810 } 1811 1812 @Override 1813 public DetailAstImpl visitPrimitivePrimary(JavaLanguageParser.PrimitivePrimaryContext ctx) { 1814 final DetailAstImpl dot = create(ctx.DOT()); 1815 final DetailAstImpl primaryTypeNoArray = visit(ctx.type); 1816 dot.addChild(primaryTypeNoArray); 1817 ctx.arrayDeclarator().forEach(child -> dot.addChild(visit(child))); 1818 dot.addChild(create(ctx.LITERAL_CLASS())); 1819 return dot; 1820 } 1821 1822 @Override 1823 public DetailAstImpl visitCreator(JavaLanguageParser.CreatorContext ctx) { 1824 return flattenedTree(ctx); 1825 } 1826 1827 @Override 1828 public DetailAstImpl visitCreatedNameObject(JavaLanguageParser.CreatedNameObjectContext ctx) { 1829 final DetailAstPair currentAST = new DetailAstPair(); 1830 DetailAstPair.addAstChild(currentAST, visit(ctx.annotations())); 1831 DetailAstPair.addAstChild(currentAST, visit(ctx.id())); 1832 DetailAstPair.addAstChild(currentAST, visit(ctx.typeArgumentsOrDiamond())); 1833 1834 // This is how we build the type arguments/ qualified name tree 1835 for (ParserRuleContext extendedContext : ctx.extended) { 1836 final DetailAstImpl dot = create(extendedContext.start); 1837 DetailAstPair.makeAstRoot(currentAST, dot); 1838 final List<ParseTree> childList = extendedContext 1839 .children.subList(1, extendedContext.children.size()); 1840 processChildren(dot, childList); 1841 } 1842 1843 return currentAST.root; 1844 } 1845 1846 @Override 1847 public DetailAstImpl visitCreatedNamePrimitive( 1848 JavaLanguageParser.CreatedNamePrimitiveContext ctx) { 1849 return flattenedTree(ctx); 1850 } 1851 1852 @Override 1853 public DetailAstImpl visitInnerCreator(JavaLanguageParser.InnerCreatorContext ctx) { 1854 return flattenedTree(ctx); 1855 } 1856 1857 @Override 1858 public DetailAstImpl visitArrayCreatorRest(JavaLanguageParser.ArrayCreatorRestContext ctx) { 1859 final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, 1860 (Token) ctx.LBRACK().getPayload()); 1861 final JavaLanguageParser.ExpressionContext expression = ctx.expression(); 1862 final TerminalNode rbrack = ctx.RBRACK(); 1863 // child[0] is LBRACK 1864 for (int i = 1; i < ctx.children.size(); i++) { 1865 if (ctx.children.get(i) == rbrack) { 1866 arrayDeclarator.addChild(create(rbrack)); 1867 } 1868 else if (ctx.children.get(i) == expression) { 1869 // Handle '[8]', etc. 1870 arrayDeclarator.addChild(visit(expression)); 1871 } 1872 else { 1873 addLastSibling(arrayDeclarator, visit(ctx.children.get(i))); 1874 } 1875 } 1876 return arrayDeclarator; 1877 } 1878 1879 @Override 1880 public DetailAstImpl visitBracketsWithExp(JavaLanguageParser.BracketsWithExpContext ctx) { 1881 final DetailAstImpl dummyRoot = new DetailAstImpl(); 1882 dummyRoot.addChild(visit(ctx.annotations())); 1883 final DetailAstImpl arrayDeclarator = 1884 create(TokenTypes.ARRAY_DECLARATOR, (Token) ctx.LBRACK().getPayload()); 1885 arrayDeclarator.addChild(visit(ctx.expression())); 1886 arrayDeclarator.addChild(create(ctx.stop)); 1887 dummyRoot.addChild(arrayDeclarator); 1888 return dummyRoot.getFirstChild(); 1889 } 1890 1891 @Override 1892 public DetailAstImpl visitClassCreatorRest(JavaLanguageParser.ClassCreatorRestContext ctx) { 1893 return flattenedTree(ctx); 1894 } 1895 1896 @Override 1897 public DetailAstImpl visitDiamond(JavaLanguageParser.DiamondContext ctx) { 1898 final DetailAstImpl typeArguments = 1899 createImaginary(TokenTypes.TYPE_ARGUMENTS); 1900 typeArguments.addChild(create(TokenTypes.GENERIC_START, 1901 (Token) ctx.LT().getPayload())); 1902 typeArguments.addChild(create(TokenTypes.GENERIC_END, 1903 (Token) ctx.GT().getPayload())); 1904 return typeArguments; 1905 } 1906 1907 @Override 1908 public DetailAstImpl visitTypeArgs(JavaLanguageParser.TypeArgsContext ctx) { 1909 return flattenedTree(ctx); 1910 } 1911 1912 @Override 1913 public DetailAstImpl visitNonWildcardDiamond( 1914 JavaLanguageParser.NonWildcardDiamondContext ctx) { 1915 final DetailAstImpl typeArguments = 1916 createImaginary(TokenTypes.TYPE_ARGUMENTS); 1917 typeArguments.addChild(create(TokenTypes.GENERIC_START, 1918 (Token) ctx.LT().getPayload())); 1919 typeArguments.addChild(create(TokenTypes.GENERIC_END, 1920 (Token) ctx.GT().getPayload())); 1921 return typeArguments; 1922 } 1923 1924 @Override 1925 public DetailAstImpl visitNonWildcardTypeArguments( 1926 JavaLanguageParser.NonWildcardTypeArgumentsContext ctx) { 1927 final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); 1928 typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); 1929 typeArguments.addChild(visit(ctx.typeArgumentsTypeList())); 1930 typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); 1931 return typeArguments; 1932 } 1933 1934 @Override 1935 public DetailAstImpl visitTypeArgumentsTypeList( 1936 JavaLanguageParser.TypeArgumentsTypeListContext ctx) { 1937 final DetailAstImpl firstIdent = visit(ctx.typeType(0)); 1938 final DetailAstImpl firstTypeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); 1939 firstTypeArgument.addChild(firstIdent); 1940 1941 for (int i = 0; i < ctx.COMMA().size(); i++) { 1942 addLastSibling(firstTypeArgument, create(ctx.COMMA(i))); 1943 final DetailAstImpl ident = visit(ctx.typeType(i + 1)); 1944 final DetailAstImpl typeArgument = createImaginary(TokenTypes.TYPE_ARGUMENT); 1945 typeArgument.addChild(ident); 1946 addLastSibling(firstTypeArgument, typeArgument); 1947 } 1948 return firstTypeArgument; 1949 } 1950 1951 @Override 1952 public DetailAstImpl visitTypeList(JavaLanguageParser.TypeListContext ctx) { 1953 return flattenedTree(ctx); 1954 } 1955 1956 @Override 1957 public DetailAstImpl visitTypeType(JavaLanguageParser.TypeTypeContext ctx) { 1958 final DetailAstImpl type = createImaginary(TokenTypes.TYPE); 1959 processChildren(type, ctx.children); 1960 1961 final DetailAstImpl returnTree; 1962 if (ctx.createImaginaryNode) { 1963 returnTree = type; 1964 } 1965 else { 1966 returnTree = type.getFirstChild(); 1967 } 1968 return returnTree; 1969 } 1970 1971 @Override 1972 public DetailAstImpl visitArrayDeclarator(JavaLanguageParser.ArrayDeclaratorContext ctx) { 1973 final DetailAstImpl arrayDeclarator = create(TokenTypes.ARRAY_DECLARATOR, 1974 (Token) ctx.LBRACK().getPayload()); 1975 arrayDeclarator.addChild(create(ctx.RBRACK())); 1976 1977 final DetailAstImpl returnTree; 1978 final DetailAstImpl annotations = visit(ctx.anno); 1979 if (annotations == null) { 1980 returnTree = arrayDeclarator; 1981 } 1982 else { 1983 returnTree = annotations; 1984 addLastSibling(returnTree, arrayDeclarator); 1985 } 1986 return returnTree; 1987 } 1988 1989 @Override 1990 public DetailAstImpl visitPrimitiveType(JavaLanguageParser.PrimitiveTypeContext ctx) { 1991 return create(ctx.start); 1992 } 1993 1994 @Override 1995 public DetailAstImpl visitTypeArguments(JavaLanguageParser.TypeArgumentsContext ctx) { 1996 final DetailAstImpl typeArguments = createImaginary(TokenTypes.TYPE_ARGUMENTS); 1997 typeArguments.addChild(create(TokenTypes.GENERIC_START, (Token) ctx.LT().getPayload())); 1998 // Exclude '<' and '>' 1999 processChildren(typeArguments, ctx.children.subList(1, ctx.children.size() - 1)); 2000 typeArguments.addChild(create(TokenTypes.GENERIC_END, (Token) ctx.GT().getPayload())); 2001 return typeArguments; 2002 } 2003 2004 @Override 2005 public DetailAstImpl visitSuperSuffixDot(JavaLanguageParser.SuperSuffixDotContext ctx) { 2006 final DetailAstImpl root; 2007 if (ctx.LPAREN() == null) { 2008 root = create(ctx.DOT()); 2009 root.addChild(visit(ctx.id())); 2010 } 2011 else { 2012 root = create(TokenTypes.METHOD_CALL, (Token) ctx.LPAREN().getPayload()); 2013 2014 final DetailAstImpl dot = create(ctx.DOT()); 2015 dot.addChild(visit(ctx.id())); 2016 root.addChild(dot); 2017 2018 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 2019 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 2020 root.addChild(expressionList); 2021 2022 root.addChild(create(ctx.RPAREN())); 2023 } 2024 2025 return root; 2026 } 2027 2028 @Override 2029 public DetailAstImpl visitArguments(JavaLanguageParser.ArgumentsContext ctx) { 2030 final DetailAstImpl lparen = create(ctx.LPAREN()); 2031 2032 // We always add an 'ELIST' node 2033 final DetailAstImpl expressionList = Optional.ofNullable(visit(ctx.expressionList())) 2034 .orElseGet(() -> createImaginary(TokenTypes.ELIST)); 2035 addLastSibling(lparen, expressionList); 2036 addLastSibling(lparen, create(ctx.RPAREN())); 2037 return lparen; 2038 } 2039 2040 @Override 2041 public DetailAstImpl visitPattern(JavaLanguageParser.PatternContext ctx) { 2042 final JavaLanguageParser.InnerPatternContext innerPattern = ctx.innerPattern(); 2043 final ParserRuleContext primaryPattern = innerPattern.primaryPattern(); 2044 final ParserRuleContext recordPattern = innerPattern.recordPattern(); 2045 2046 final DetailAstImpl pattern; 2047 2048 if (recordPattern != null) { 2049 pattern = visit(recordPattern); 2050 } 2051 else if (primaryPattern != null) { 2052 // For simple type pattern like 'Integer i`, we do not add `PATTERN_DEF` parent 2053 pattern = visit(primaryPattern); 2054 } 2055 else { 2056 pattern = createImaginary(TokenTypes.PATTERN_DEF); 2057 pattern.addChild(visit(ctx.getChild(0))); 2058 } 2059 return pattern; 2060 } 2061 2062 @Override 2063 public DetailAstImpl visitInnerPattern(JavaLanguageParser.InnerPatternContext ctx) { 2064 return flattenedTree(ctx); 2065 } 2066 2067 @Override 2068 public DetailAstImpl visitGuardedPattern(JavaLanguageParser.GuardedPatternContext ctx) { 2069 final DetailAstImpl guardAstNode = flattenedTree(ctx.guard()); 2070 guardAstNode.addChild(visit(ctx.primaryPattern())); 2071 guardAstNode.addChild(visit(ctx.expression())); 2072 return guardAstNode; 2073 } 2074 2075 @Override 2076 public DetailAstImpl visitRecordPatternDef(JavaLanguageParser.RecordPatternDefContext ctx) { 2077 return flattenedTree(ctx); 2078 } 2079 2080 @Override 2081 public DetailAstImpl visitTypePatternDef( 2082 JavaLanguageParser.TypePatternDefContext ctx) { 2083 final DetailAstImpl type = visit(ctx.type); 2084 final DetailAstImpl patternVariableDef = createImaginary(TokenTypes.PATTERN_VARIABLE_DEF); 2085 patternVariableDef.addChild(createModifiers(ctx.mods)); 2086 patternVariableDef.addChild(type); 2087 patternVariableDef.addChild(visit(ctx.id())); 2088 return patternVariableDef; 2089 } 2090 2091 @Override 2092 public DetailAstImpl visitUnnamedPatternDef(JavaLanguageParser.UnnamedPatternDefContext ctx) { 2093 return create(TokenTypes.UNNAMED_PATTERN_DEF, ctx.start); 2094 } 2095 2096 @Override 2097 public DetailAstImpl visitRecordPattern(JavaLanguageParser.RecordPatternContext ctx) { 2098 final DetailAstImpl recordPattern = createImaginary(TokenTypes.RECORD_PATTERN_DEF); 2099 recordPattern.addChild(createModifiers(ctx.mods)); 2100 processChildren(recordPattern, 2101 ctx.children.subList(ctx.mods.size(), ctx.children.size())); 2102 return recordPattern; 2103 } 2104 2105 @Override 2106 public DetailAstImpl visitRecordComponentPatternList( 2107 JavaLanguageParser.RecordComponentPatternListContext ctx) { 2108 final DetailAstImpl recordComponents = 2109 createImaginary(TokenTypes.RECORD_PATTERN_COMPONENTS); 2110 processChildren(recordComponents, ctx.children); 2111 return recordComponents; 2112 } 2113 2114 @Override 2115 public DetailAstImpl visitPermittedSubclassesAndInterfaces( 2116 JavaLanguageParser.PermittedSubclassesAndInterfacesContext ctx) { 2117 final DetailAstImpl literalPermits = 2118 create(TokenTypes.PERMITS_CLAUSE, (Token) ctx.LITERAL_PERMITS().getPayload()); 2119 // 'LITERAL_PERMITS' is child[0] 2120 processChildren(literalPermits, ctx.children.subList(1, ctx.children.size())); 2121 return literalPermits; 2122 } 2123 2124 @Override 2125 public DetailAstImpl visitId(JavaLanguageParser.IdContext ctx) { 2126 return create(TokenTypes.IDENT, ctx.start); 2127 } 2128 2129 /** 2130 * Builds the AST for a particular node, then returns a "flattened" tree 2131 * of siblings. This method should be used in rule contexts such as 2132 * {@code variableDeclarators}, where we have both terminals and non-terminals. 2133 * 2134 * @param ctx the ParserRuleContext to base tree on 2135 * @return flattened DetailAstImpl 2136 */ 2137 private DetailAstImpl flattenedTree(ParserRuleContext ctx) { 2138 final DetailAstImpl dummyNode = new DetailAstImpl(); 2139 processChildren(dummyNode, ctx.children); 2140 return dummyNode.getFirstChild(); 2141 } 2142 2143 /** 2144 * Adds all the children from the given ParseTree or JavaParserContext 2145 * list to the parent DetailAstImpl. 2146 * 2147 * @param parent the DetailAstImpl to add children to 2148 * @param children the list of children to add 2149 */ 2150 private void processChildren(DetailAstImpl parent, List<? extends ParseTree> children) { 2151 children.forEach(child -> { 2152 if (child instanceof TerminalNode node) { 2153 // Child is a token, create a new DetailAstImpl and add it to parent 2154 parent.addChild(create(node)); 2155 } 2156 else { 2157 // Child is another rule context; visit it, create token, and add to parent 2158 parent.addChild(visit(child)); 2159 } 2160 }); 2161 } 2162 2163 /** 2164 * Create a DetailAstImpl from a given token and token type. This method 2165 * should be used for imaginary nodes only, i.e. {@literal 'OBJBLOCK -> OBJBLOCK'}, 2166 * where the text on the RHS matches the text on the LHS. 2167 * 2168 * @param tokenType the token type of this DetailAstImpl 2169 * @return new DetailAstImpl of given type 2170 */ 2171 private static DetailAstImpl createImaginary(int tokenType) { 2172 final DetailAstImpl detailAst = new DetailAstImpl(); 2173 detailAst.setType(tokenType); 2174 detailAst.setText(TokenUtil.getTokenName(tokenType)); 2175 return detailAst; 2176 } 2177 2178 /** 2179 * Create a DetailAstImpl from a given token. This method should be 2180 * used for terminal nodes, i.e. {@code LCURLY}, when we are building 2181 * an AST for a specific token, regardless of position. 2182 * 2183 * @param token the token to build the DetailAstImpl from 2184 * @return new DetailAstImpl of given type 2185 */ 2186 private DetailAstImpl create(Token token) { 2187 final int tokenIndex = token.getTokenIndex(); 2188 final List<Token> tokensToLeft = 2189 tokens.getHiddenTokensToLeft(tokenIndex, JavaLanguageLexer.COMMENTS); 2190 final List<Token> tokensToRight = 2191 tokens.getHiddenTokensToRight(tokenIndex, JavaLanguageLexer.COMMENTS); 2192 2193 final DetailAstImpl detailAst = new DetailAstImpl(); 2194 detailAst.initialize(token); 2195 if (tokensToLeft != null) { 2196 detailAst.setHiddenBefore(tokensToLeft); 2197 } 2198 if (tokensToRight != null) { 2199 detailAst.setHiddenAfter(tokensToRight); 2200 } 2201 return detailAst; 2202 } 2203 2204 /** 2205 * Create a DetailAstImpl from a given TerminalNode. This method should be 2206 * used for terminal nodes, i.e. {@code @}. 2207 * 2208 * @param node the TerminalNode to build the DetailAstImpl from 2209 * @return new DetailAstImpl of given type 2210 */ 2211 private DetailAstImpl create(TerminalNode node) { 2212 return create((Token) node.getPayload()); 2213 } 2214 2215 /** 2216 * Create a DetailAstImpl from a given token and token type. This method 2217 * should be used for literal nodes only, i.e. {@literal 'PACKAGE_DEF -> package'}. 2218 * 2219 * @param tokenType the token type of this DetailAstImpl 2220 * @param startToken the first token that appears in this DetailAstImpl. 2221 * @return new DetailAstImpl of given type 2222 */ 2223 private DetailAstImpl create(int tokenType, Token startToken) { 2224 final DetailAstImpl ast = create(startToken); 2225 ast.setType(tokenType); 2226 return ast; 2227 } 2228 2229 /** 2230 * Creates a type declaration DetailAstImpl from a given rule context. 2231 * 2232 * @param ctx ParserRuleContext we are in 2233 * @param type the type declaration to create 2234 * @param modifierList respective modifiers 2235 * @return type declaration DetailAstImpl 2236 */ 2237 private DetailAstImpl createTypeDeclaration(ParserRuleContext ctx, int type, 2238 List<? extends ParseTree> modifierList) { 2239 final DetailAstImpl typeDeclaration = createImaginary(type); 2240 typeDeclaration.addChild(createModifiers(modifierList)); 2241 processChildren(typeDeclaration, ctx.children); 2242 return typeDeclaration; 2243 } 2244 2245 /** 2246 * Builds the modifiers AST. 2247 * 2248 * @param modifierList the list of modifier contexts 2249 * @return "MODIFIERS" ast 2250 */ 2251 private DetailAstImpl createModifiers(List<? extends ParseTree> modifierList) { 2252 final DetailAstImpl mods = createImaginary(TokenTypes.MODIFIERS); 2253 processChildren(mods, modifierList); 2254 return mods; 2255 } 2256 2257 /** 2258 * Creates a node rooted at the first token of the given context, i.e. the 2259 * first token becomes the node (with the given token type) and all remaining 2260 * children are added to it. This is used for module directives and clauses, 2261 * where the leading keyword token becomes the node, e.g. 2262 * {@literal 'requires' -> REQUIRES}. 2263 * 2264 * @param tokenType the token type of the resulting DetailAstImpl 2265 * @param ctx the ParserRuleContext whose first token roots the node 2266 * @return the resulting DetailAstImpl 2267 */ 2268 private DetailAstImpl createNodeFromFirstToken(int tokenType, ParserRuleContext ctx) { 2269 final DetailAstImpl node = create(tokenType, ctx.start); 2270 processChildren(node, ctx.children.subList(1, ctx.children.size())); 2271 return node; 2272 } 2273 2274 /** 2275 * Add new sibling to the end of existing siblings. 2276 * 2277 * @param self DetailAstImpl to add last sibling to 2278 * @param sibling DetailAstImpl sibling to add 2279 */ 2280 private static void addLastSibling(DetailAstImpl self, DetailAstImpl sibling) { 2281 DetailAstImpl nextSibling = self; 2282 if (nextSibling != null) { 2283 while (nextSibling.getNextSibling() != null) { 2284 nextSibling = nextSibling.getNextSibling(); 2285 } 2286 nextSibling.setNextSibling(sibling); 2287 } 2288 } 2289 2290 @Override 2291 public DetailAstImpl visit(ParseTree tree) { 2292 DetailAstImpl ast = null; 2293 if (tree != null) { 2294 ast = tree.accept(this); 2295 } 2296 return ast; 2297 } 2298 2299 /** 2300 * Builds an expression node. This is used to build the root of an expression with 2301 * an imaginary {@code EXPR} node. 2302 * 2303 * @param exprNode expression to build node for 2304 * @return expression DetailAstImpl node 2305 */ 2306 private DetailAstImpl buildExpressionNode(ParseTree exprNode) { 2307 final DetailAstImpl expression = visit(exprNode); 2308 2309 final DetailAstImpl exprRoot; 2310 if (TokenUtil.isOfType(expression, EXPRESSIONS_WITH_NO_EXPR_ROOT)) { 2311 exprRoot = expression; 2312 } 2313 else { 2314 // create imaginary 'EXPR' node as root of expression 2315 exprRoot = createImaginary(TokenTypes.EXPR); 2316 exprRoot.addChild(expression); 2317 } 2318 return exprRoot; 2319 } 2320 2321 /** 2322 * Used to swap and organize DetailAstImpl subtrees. 2323 */ 2324 private static final class DetailAstPair { 2325 2326 /** The root DetailAstImpl of this pair. */ 2327 private DetailAstImpl root; 2328 2329 /** The child (potentially with siblings) of this pair. */ 2330 private DetailAstImpl child; 2331 2332 /** 2333 * Creates a new {@code DetailAstPair} instance. 2334 */ 2335 private DetailAstPair() { 2336 // no code by default 2337 } 2338 2339 /** 2340 * Moves child reference to the last child. 2341 */ 2342 private void advanceChildToEnd() { 2343 while (child.getNextSibling() != null) { 2344 child = child.getNextSibling(); 2345 } 2346 } 2347 2348 /** 2349 * Returns the root node. 2350 * 2351 * @return the root node 2352 */ 2353 private DetailAstImpl getRoot() { 2354 return root; 2355 } 2356 2357 /** 2358 * This method is used to replace the {@code ^} (set as root node) ANTLR2 2359 * operator. 2360 * 2361 * @param pair the DetailAstPair to use for swapping nodes 2362 * @param ast the new root 2363 */ 2364 private static void makeAstRoot(DetailAstPair pair, DetailAstImpl ast) { 2365 ast.addChild(pair.root); 2366 pair.child = pair.root; 2367 pair.advanceChildToEnd(); 2368 pair.root = ast; 2369 } 2370 2371 /** 2372 * Adds a child (or new root) to the given DetailAstPair. 2373 * 2374 * @param pair the DetailAstPair to add child to 2375 * @param ast the child to add 2376 */ 2377 private static void addAstChild(DetailAstPair pair, DetailAstImpl ast) { 2378 if (ast != null) { 2379 if (pair.root == null) { 2380 pair.root = ast; 2381 } 2382 else { 2383 pair.child.setNextSibling(ast); 2384 } 2385 pair.child = ast; 2386 } 2387 } 2388 } 2389 2390}