Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.groovy.parser.antlr4; import groovy.lang.Tuple2; import groovy.transform.Trait; import org.antlr.v4.runtime.ANTLRErrorListener; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.misc.ParseCancellationException; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.groovy.parser.antlr4.GroovyParser.AdditiveExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.AndExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.AnnotatedQualifiedClassNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.AnnotationContext; import org.apache.groovy.parser.antlr4.GroovyParser.AnnotationNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.AnnotationsOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.AnonymousInnerClassDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.ArgumentsContext; import org.apache.groovy.parser.antlr4.GroovyParser.ArrayInitializerContext; import org.apache.groovy.parser.antlr4.GroovyParser.AssertStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.AssignmentExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.BlockContext; import org.apache.groovy.parser.antlr4.GroovyParser.BlockStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.BlockStatementsContext; import org.apache.groovy.parser.antlr4.GroovyParser.BlockStatementsOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.BooleanLiteralAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.BreakStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.BuiltInTypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.CastExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.CastParExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.CatchClauseContext; import org.apache.groovy.parser.antlr4.GroovyParser.CatchTypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassBodyContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassBodyDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassOrInterfaceModifierContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassOrInterfaceModifiersContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassOrInterfaceModifiersOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassOrInterfaceTypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassicalForControlContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClassifiedModifiersContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClosureContext; import org.apache.groovy.parser.antlr4.GroovyParser.ClosureOrLambdaExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.CommandArgumentContext; import org.apache.groovy.parser.antlr4.GroovyParser.CommandExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.CommandExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.CompilationUnitContext; import org.apache.groovy.parser.antlr4.GroovyParser.ConditionalExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ConditionalStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.ContinueStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.CreatedNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.CreatorContext; import org.apache.groovy.parser.antlr4.GroovyParser.DimsContext; import org.apache.groovy.parser.antlr4.GroovyParser.DimsOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.DoWhileStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.DynamicMemberNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.ElementValueArrayInitializerContext; import org.apache.groovy.parser.antlr4.GroovyParser.ElementValueContext; import org.apache.groovy.parser.antlr4.GroovyParser.ElementValuePairContext; import org.apache.groovy.parser.antlr4.GroovyParser.ElementValuePairsContext; import org.apache.groovy.parser.antlr4.GroovyParser.ElementValuesContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnhancedArgumentListContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnhancedArgumentListElementContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnhancedForControlContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnhancedStatementExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnumConstantContext; import org.apache.groovy.parser.antlr4.GroovyParser.EnumConstantsContext; import org.apache.groovy.parser.antlr4.GroovyParser.EqualityExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ExclusiveOrExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.ExpressionInParContext; import org.apache.groovy.parser.antlr4.GroovyParser.ExpressionListContext; import org.apache.groovy.parser.antlr4.GroovyParser.ExpressionListElementContext; import org.apache.groovy.parser.antlr4.GroovyParser.FieldDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.FinallyBlockContext; import org.apache.groovy.parser.antlr4.GroovyParser.FloatingPointLiteralAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ForControlContext; import org.apache.groovy.parser.antlr4.GroovyParser.ForInitContext; import org.apache.groovy.parser.antlr4.GroovyParser.ForStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ForUpdateContext; import org.apache.groovy.parser.antlr4.GroovyParser.FormalParameterContext; import org.apache.groovy.parser.antlr4.GroovyParser.FormalParameterListContext; import org.apache.groovy.parser.antlr4.GroovyParser.FormalParametersContext; import org.apache.groovy.parser.antlr4.GroovyParser.GroovyParserRuleContext; import org.apache.groovy.parser.antlr4.GroovyParser.GstringContext; import org.apache.groovy.parser.antlr4.GroovyParser.GstringPathContext; import org.apache.groovy.parser.antlr4.GroovyParser.GstringValueContext; import org.apache.groovy.parser.antlr4.GroovyParser.IdentifierContext; import org.apache.groovy.parser.antlr4.GroovyParser.IdentifierPrmrAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.IfElseStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.ImportDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.InclusiveOrExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.IndexPropertyArgsContext; import org.apache.groovy.parser.antlr4.GroovyParser.IntegerLiteralAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.KeywordsContext; import org.apache.groovy.parser.antlr4.GroovyParser.LabeledStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.LambdaBodyContext; import org.apache.groovy.parser.antlr4.GroovyParser.ListContext; import org.apache.groovy.parser.antlr4.GroovyParser.LocalVariableDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.LogicalAndExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.LogicalOrExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.LoopStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.MapContext; import org.apache.groovy.parser.antlr4.GroovyParser.MapEntryContext; import org.apache.groovy.parser.antlr4.GroovyParser.MapEntryLabelContext; import org.apache.groovy.parser.antlr4.GroovyParser.MapEntryListContext; import org.apache.groovy.parser.antlr4.GroovyParser.MemberDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.MethodBodyContext; import org.apache.groovy.parser.antlr4.GroovyParser.MethodDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.MethodNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.ModifierContext; import org.apache.groovy.parser.antlr4.GroovyParser.ModifiersContext; import org.apache.groovy.parser.antlr4.GroovyParser.ModifiersOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.MultipleAssignmentExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.MultiplicativeExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.NamePartContext; import org.apache.groovy.parser.antlr4.GroovyParser.NamedPropertyArgsContext; import org.apache.groovy.parser.antlr4.GroovyParser.NewPrmrAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.NonWildcardTypeArgumentsContext; import org.apache.groovy.parser.antlr4.GroovyParser.NullLiteralAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.PackageDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.ParExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.PathElementContext; import org.apache.groovy.parser.antlr4.GroovyParser.PathExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.PostfixExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.PowerExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.PrimitiveTypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.QualifiedClassNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.QualifiedClassNameListContext; import org.apache.groovy.parser.antlr4.GroovyParser.QualifiedNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.QualifiedStandardClassNameContext; import org.apache.groovy.parser.antlr4.GroovyParser.RegexExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.RelationalExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ResourceContext; import org.apache.groovy.parser.antlr4.GroovyParser.ResourceListContext; import org.apache.groovy.parser.antlr4.GroovyParser.ResourcesContext; import org.apache.groovy.parser.antlr4.GroovyParser.ReturnStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ReturnTypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.ScriptStatementsContext; import org.apache.groovy.parser.antlr4.GroovyParser.ShiftExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.StandardLambdaExpressionContext; import org.apache.groovy.parser.antlr4.GroovyParser.StandardLambdaParametersContext; import org.apache.groovy.parser.antlr4.GroovyParser.StatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.StringLiteralContext; import org.apache.groovy.parser.antlr4.GroovyParser.SuperPrmrAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.SwitchBlockStatementGroupContext; import org.apache.groovy.parser.antlr4.GroovyParser.SwitchLabelContext; import org.apache.groovy.parser.antlr4.GroovyParser.SwitchStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.SynchronizedStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ThisFormalParameterContext; import org.apache.groovy.parser.antlr4.GroovyParser.ThisPrmrAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.ThrowStmtAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.TryCatchStatementContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeArgumentContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeArgumentsContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeArgumentsOrDiamondContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeBoundContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeListContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeNamePairContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeNamePairsContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeParameterContext; import org.apache.groovy.parser.antlr4.GroovyParser.TypeParametersContext; import org.apache.groovy.parser.antlr4.GroovyParser.UnaryAddExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.UnaryNotExprAltContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableDeclarationContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableDeclaratorContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableDeclaratorIdContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableDeclaratorsContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableInitializerContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableInitializersContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableModifierContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableModifiersContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableModifiersOptContext; import org.apache.groovy.parser.antlr4.GroovyParser.VariableNamesContext; import org.apache.groovy.parser.antlr4.GroovyParser.WhileStmtAltContext; import org.apache.groovy.parser.antlr4.internal.DescriptiveErrorStrategy; import org.apache.groovy.parser.antlr4.internal.atnmanager.AtnManager; import org.apache.groovy.parser.antlr4.util.StringUtils; import org.apache.groovy.util.Maps; import org.codehaus.groovy.GroovyBugError; import org.codehaus.groovy.antlr.EnumHelper; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.EnumConstantClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.ImportNode; import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.ModifierNode; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.NodeMetaDataHandler; import org.codehaus.groovy.ast.PackageNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.ArrayExpression; import org.codehaus.groovy.ast.expr.AttributeExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; import org.codehaus.groovy.ast.expr.BooleanExpression; import org.codehaus.groovy.ast.expr.CastExpression; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ClosureListExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.groovy.ast.expr.DeclarationExpression; import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; import org.codehaus.groovy.ast.expr.EmptyExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.GStringExpression; import org.codehaus.groovy.ast.expr.LambdaExpression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapEntryExpression; import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.MethodPointerExpression; import org.codehaus.groovy.ast.expr.MethodReferenceExpression; import org.codehaus.groovy.ast.expr.NamedArgumentListExpression; import org.codehaus.groovy.ast.expr.NotExpression; import org.codehaus.groovy.ast.expr.PostfixExpression; import org.codehaus.groovy.ast.expr.PrefixExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.expr.RangeExpression; import org.codehaus.groovy.ast.expr.SpreadExpression; import org.codehaus.groovy.ast.expr.SpreadMapExpression; import org.codehaus.groovy.ast.expr.TernaryExpression; import org.codehaus.groovy.ast.expr.TupleExpression; import org.codehaus.groovy.ast.expr.UnaryMinusExpression; import org.codehaus.groovy.ast.expr.UnaryPlusExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.AssertStatement; import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.BreakStatement; import org.codehaus.groovy.ast.stmt.CaseStatement; import org.codehaus.groovy.ast.stmt.CatchStatement; import org.codehaus.groovy.ast.stmt.ContinueStatement; import org.codehaus.groovy.ast.stmt.DoWhileStatement; import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.ast.stmt.ForStatement; import org.codehaus.groovy.ast.stmt.IfStatement; import org.codehaus.groovy.ast.stmt.ReturnStatement; import org.codehaus.groovy.ast.stmt.Statement; import org.codehaus.groovy.ast.stmt.SwitchStatement; import org.codehaus.groovy.ast.stmt.SynchronizedStatement; import org.codehaus.groovy.ast.stmt.ThrowStatement; import org.codehaus.groovy.ast.stmt.TryCatchStatement; import org.codehaus.groovy.ast.stmt.WhileStatement; import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilePhase; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.messages.SyntaxErrorMessage; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.StringGroovyMethods; import org.codehaus.groovy.syntax.Numbers; import org.codehaus.groovy.syntax.SyntaxException; import org.codehaus.groovy.syntax.Types; import org.objectweb.asm.Opcodes; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import static groovy.lang.Tuple.tuple; import static org.apache.groovy.parser.antlr4.GroovyParser.ADD; import static org.apache.groovy.parser.antlr4.GroovyParser.AS; import static org.apache.groovy.parser.antlr4.GroovyParser.CASE; import static org.apache.groovy.parser.antlr4.GroovyParser.DEC; import static org.apache.groovy.parser.antlr4.GroovyParser.DEF; import static org.apache.groovy.parser.antlr4.GroovyParser.DEFAULT; import static org.apache.groovy.parser.antlr4.GroovyParser.GE; import static org.apache.groovy.parser.antlr4.GroovyParser.GT; import static org.apache.groovy.parser.antlr4.GroovyParser.IN; import static org.apache.groovy.parser.antlr4.GroovyParser.INC; import static org.apache.groovy.parser.antlr4.GroovyParser.INSTANCEOF; import static org.apache.groovy.parser.antlr4.GroovyParser.LE; import static org.apache.groovy.parser.antlr4.GroovyParser.LT; import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_IN; import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_INSTANCEOF; import static org.apache.groovy.parser.antlr4.GroovyParser.PRIVATE; import static org.apache.groovy.parser.antlr4.GroovyParser.STATIC; import static org.apache.groovy.parser.antlr4.GroovyParser.SUB; import static org.apache.groovy.parser.antlr4.GroovyParser.VAR; import static org.apache.groovy.parser.antlr4.util.PositionConfigureUtils.configureAST; import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveType; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last; /** * Builds the AST from the parse tree generated by Antlr4. */ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements GroovyParserVisitor<Object> { public AstBuilder(SourceUnit sourceUnit, CompilerConfiguration compilerConfiguration) { this.sourceUnit = sourceUnit; this.moduleNode = new ModuleNode(sourceUnit); CharStream charStream = createCharStream(sourceUnit); this.lexer = new GroovyLangLexer(charStream); this.parser = new GroovyLangParser(new CommonTokenStream(this.lexer)); this.parser.setErrorHandler(new DescriptiveErrorStrategy(charStream)); this.groovydocManager = new GroovydocManager(compilerConfiguration); this.tryWithResourcesASTTransformation = new TryWithResourcesASTTransformation(this); } private CharStream createCharStream(SourceUnit sourceUnit) { CharStream charStream; try { charStream = CharStreams.fromReader(new BufferedReader(sourceUnit.getSource().getReader()), sourceUnit.getName()); } catch (IOException e) { throw new RuntimeException("Error occurred when reading source code.", e); } return charStream; } private GroovyParserRuleContext buildCST() throws CompilationFailedException { GroovyParserRuleContext result; try { // parsing have to wait util clearing is complete. AtnManager.READ_LOCK.lock(); try { result = buildCST(PredictionMode.SLL); } catch (Throwable t) { // if some syntax error occurred in the lexer, no need to retry the powerful LL mode if (t instanceof GroovySyntaxError && GroovySyntaxError.LEXER == ((GroovySyntaxError) t).getSource()) { throw t; } result = buildCST(PredictionMode.LL); } finally { AtnManager.READ_LOCK.unlock(); } } catch (Throwable t) { throw convertException(t); } return result; } private GroovyParserRuleContext buildCST(PredictionMode predictionMode) { parser.getInterpreter().setPredictionMode(predictionMode); if (PredictionMode.SLL.equals(predictionMode)) { this.removeErrorListeners(); } else { parser.getInputStream().seek(0); this.addErrorListeners(); } return parser.compilationUnit(); } private CompilationFailedException convertException(Throwable t) { CompilationFailedException cfe; if (t instanceof CompilationFailedException) { cfe = (CompilationFailedException) t; } else if (t instanceof ParseCancellationException) { cfe = createParsingFailedException(t.getCause()); } else { cfe = createParsingFailedException(t); } return cfe; } public ModuleNode buildAST() { try { return (ModuleNode) this.visit(this.buildCST()); } catch (Throwable t) { throw convertException(t); } } @Override public ModuleNode visitCompilationUnit(CompilationUnitContext ctx) { this.visit(ctx.packageDeclaration()); this.visitScriptStatements(ctx.scriptStatements()).forEach(e -> { if (e instanceof DeclarationListStatement) { // local variable declaration ((DeclarationListStatement) e).getDeclarationStatements().forEach(moduleNode::addStatement); } else if (e instanceof Statement) { moduleNode.addStatement((Statement) e); } else if (e instanceof MethodNode) { // script method moduleNode.addMethod((MethodNode) e); } }); this.classNodeList.forEach(moduleNode::addClass); if (this.isPackageInfoDeclaration()) { this.addPackageInfoClassNode(); } else { // if groovy source file only contains blank(including EOF), add "return null" to the AST if (this.isBlankScript()) { this.addEmptyReturnStatement(); } } this.configureScriptClassNode(); if (null != this.numberFormatError) { throw createParsingFailedException(this.numberFormatError.getV2().getMessage(), this.numberFormatError.getV1()); } return moduleNode; } @Override public List<ASTNode> visitScriptStatements(ScriptStatementsContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } return ctx.scriptStatement().stream().map(e -> (ASTNode) visit(e)).collect(Collectors.toList()); } @Override public PackageNode visitPackageDeclaration(PackageDeclarationContext ctx) { String packageName = this.visitQualifiedName(ctx.qualifiedName()); moduleNode.setPackageName(packageName + DOT_STR); PackageNode packageNode = moduleNode.getPackage(); packageNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); return configureAST(packageNode, ctx); } @Override public ImportNode visitImportDeclaration(ImportDeclarationContext ctx) { ImportNode importNode; boolean hasStatic = asBoolean(ctx.STATIC()); boolean hasStar = asBoolean(ctx.MUL()); boolean hasAlias = asBoolean(ctx.alias); List<AnnotationNode> annotationNodeList = this.visitAnnotationsOpt(ctx.annotationsOpt()); if (hasStatic) { if (hasStar) { // e.g. import static java.lang.Math.* String qualifiedName = this.visitQualifiedName(ctx.qualifiedName()); ClassNode type = ClassHelper.make(qualifiedName); configureAST(type, ctx); moduleNode.addStaticStarImport(type.getText(), type, annotationNodeList); importNode = last(moduleNode.getStaticStarImports().values()); } else { // e.g. import static java.lang.Math.pow List<GroovyParserRuleContext> identifierList = new LinkedList<>( ctx.qualifiedName().qualifiedNameElement()); int identifierListSize = identifierList.size(); String name = identifierList.get(identifierListSize - 1).getText(); ClassNode classNode = ClassHelper.make(identifierList.stream().limit(identifierListSize - 1) .map(ParseTree::getText).collect(Collectors.joining(DOT_STR))); String alias = hasAlias ? ctx.alias.getText() : name; configureAST(classNode, ctx); moduleNode.addStaticImport(classNode, name, alias, annotationNodeList); importNode = last(moduleNode.getStaticImports().values()); } } else { if (hasStar) { // e.g. import java.util.* String qualifiedName = this.visitQualifiedName(ctx.qualifiedName()); moduleNode.addStarImport(qualifiedName + DOT_STR, annotationNodeList); importNode = last(moduleNode.getStarImports()); } else { // e.g. import java.util.Map String qualifiedName = this.visitQualifiedName(ctx.qualifiedName()); String name = last(ctx.qualifiedName().qualifiedNameElement()).getText(); ClassNode classNode = ClassHelper.make(qualifiedName); String alias = hasAlias ? ctx.alias.getText() : name; configureAST(classNode, ctx); moduleNode.addImport(alias, classNode, annotationNodeList); importNode = last(moduleNode.getImports()); } } return configureAST(importNode, ctx); } // statement { -------------------------------------------------------------------- @Override public AssertStatement visitAssertStatement(AssertStatementContext ctx) { visitingAssertStatementCnt++; Expression conditionExpression = (Expression) this.visit(ctx.ce); if (conditionExpression instanceof BinaryExpression) { BinaryExpression binaryExpression = (BinaryExpression) conditionExpression; if (binaryExpression.getOperation().getType() == Types.ASSIGN) { throw createParsingFailedException("Assignment expression is not allowed in the assert statement", conditionExpression); } } BooleanExpression booleanExpression = configureAST(new BooleanExpression(conditionExpression), conditionExpression); if (!asBoolean(ctx.me)) { return configureAST(new AssertStatement(booleanExpression), ctx); } AssertStatement result = configureAST( new AssertStatement(booleanExpression, (Expression) this.visit(ctx.me)), ctx); visitingAssertStatementCnt--; return result; } @Override public Statement visitConditionalStatement(ConditionalStatementContext ctx) { if (asBoolean(ctx.ifElseStatement())) { return configureAST(this.visitIfElseStatement(ctx.ifElseStatement()), ctx); } else if (asBoolean(ctx.switchStatement())) { return configureAST(this.visitSwitchStatement(ctx.switchStatement()), ctx); } throw createParsingFailedException("Unsupported conditional statement", ctx); } @Override public IfStatement visitIfElseStatement(IfElseStatementContext ctx) { Expression conditionExpression = this.visitExpressionInPar(ctx.expressionInPar()); BooleanExpression booleanExpression = configureAST(new BooleanExpression(conditionExpression), conditionExpression); Statement ifBlock = this.unpackStatement((Statement) this.visit(ctx.tb)); Statement elseBlock = this .unpackStatement(asBoolean(ctx.ELSE()) ? (Statement) this.visit(ctx.fb) : EmptyStatement.INSTANCE); return configureAST(new IfStatement(booleanExpression, ifBlock, elseBlock), ctx); } @Override public Statement visitLoopStmtAlt(LoopStmtAltContext ctx) { visitingLoopStatementCnt++; Statement result = configureAST((Statement) this.visit(ctx.loopStatement()), ctx); visitingLoopStatementCnt--; return result; } @Override public ForStatement visitForStmtAlt(ForStmtAltContext ctx) { Tuple2<Parameter, Expression> controlTuple = this.visitForControl(ctx.forControl()); Statement loopBlock = this.unpackStatement((Statement) this.visit(ctx.statement())); return configureAST(new ForStatement(controlTuple.getV1(), controlTuple.getV2(), asBoolean(loopBlock) ? loopBlock : EmptyStatement.INSTANCE), ctx); } @Override public Tuple2<Parameter, Expression> visitForControl(ForControlContext ctx) { if (asBoolean(ctx.enhancedForControl())) { // e.g. for(int i in 0..<10) {} return this.visitEnhancedForControl(ctx.enhancedForControl()); } if (asBoolean(ctx.classicalForControl())) { // e.g. for(int i = 0; i < 10; i++) {} return this.visitClassicalForControl(ctx.classicalForControl()); } throw createParsingFailedException("Unsupported for control: " + ctx.getText(), ctx); } @Override public Expression visitForInit(ForInitContext ctx) { if (!asBoolean(ctx)) { return EmptyExpression.INSTANCE; } if (asBoolean(ctx.localVariableDeclaration())) { DeclarationListStatement declarationListStatement = this .visitLocalVariableDeclaration(ctx.localVariableDeclaration()); List<? extends Expression> declarationExpressionList = declarationListStatement .getDeclarationExpressions(); if (declarationExpressionList.size() == 1) { return configureAST((Expression) declarationExpressionList.get(0), ctx); } else { return configureAST(new ClosureListExpression((List<Expression>) declarationExpressionList), ctx); } } if (asBoolean(ctx.expressionList())) { return this.translateExpressionList(ctx.expressionList()); } throw createParsingFailedException("Unsupported for init: " + ctx.getText(), ctx); } @Override public Expression visitForUpdate(ForUpdateContext ctx) { if (!asBoolean(ctx)) { return EmptyExpression.INSTANCE; } return this.translateExpressionList(ctx.expressionList()); } private Expression translateExpressionList(ExpressionListContext ctx) { List<Expression> expressionList = this.visitExpressionList(ctx); if (expressionList.size() == 1) { return configureAST(expressionList.get(0), ctx); } else { return configureAST(new ClosureListExpression(expressionList), ctx); } } @Override public Tuple2<Parameter, Expression> visitEnhancedForControl(EnhancedForControlContext ctx) { Parameter parameter = configureAST( new Parameter(this.visitType(ctx.type()), this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName()), ctx.variableDeclaratorId()); // FIXME Groovy will ignore variableModifier of parameter in the for control // In order to make the new parser behave same with the old one, we do not process variableModifier* return tuple(parameter, (Expression) this.visit(ctx.expression())); } @Override public Tuple2<Parameter, Expression> visitClassicalForControl(ClassicalForControlContext ctx) { ClosureListExpression closureListExpression = new ClosureListExpression(); closureListExpression.addExpression(this.visitForInit(ctx.forInit())); closureListExpression.addExpression( asBoolean(ctx.expression()) ? (Expression) this.visit(ctx.expression()) : EmptyExpression.INSTANCE); closureListExpression.addExpression(this.visitForUpdate(ctx.forUpdate())); return tuple(ForStatement.FOR_LOOP_DUMMY, closureListExpression); } @Override public WhileStatement visitWhileStmtAlt(WhileStmtAltContext ctx) { Tuple2<BooleanExpression, Statement> conditionAndBlock = createLoopConditionExpressionAndBlock( ctx.expressionInPar(), ctx.statement()); return configureAST( new WhileStatement(conditionAndBlock.getV1(), asBoolean(conditionAndBlock.getV2()) ? conditionAndBlock.getV2() : EmptyStatement.INSTANCE), ctx); } @Override public DoWhileStatement visitDoWhileStmtAlt(DoWhileStmtAltContext ctx) { Tuple2<BooleanExpression, Statement> conditionAndBlock = createLoopConditionExpressionAndBlock( ctx.expressionInPar(), ctx.statement()); return configureAST( new DoWhileStatement(conditionAndBlock.getV1(), asBoolean(conditionAndBlock.getV2()) ? conditionAndBlock.getV2() : EmptyStatement.INSTANCE), ctx); } private Tuple2<BooleanExpression, Statement> createLoopConditionExpressionAndBlock(ExpressionInParContext eipc, StatementContext sc) { Expression conditionExpression = this.visitExpressionInPar(eipc); BooleanExpression booleanExpression = configureAST(new BooleanExpression(conditionExpression), conditionExpression); Statement loopBlock = this.unpackStatement((Statement) this.visit(sc)); return tuple(booleanExpression, loopBlock); } @Override public Statement visitTryCatchStatement(TryCatchStatementContext ctx) { boolean resourcesExists = asBoolean(ctx.resources()); boolean catchExists = asBoolean(ctx.catchClause()); boolean finallyExists = asBoolean(ctx.finallyBlock()); if (!(resourcesExists || catchExists || finallyExists)) { throw createParsingFailedException( "Either a catch or finally clause or both is required for a try-catch-finally statement", ctx); } TryCatchStatement tryCatchStatement = new TryCatchStatement((Statement) this.visit(ctx.block()), this.visitFinallyBlock(ctx.finallyBlock())); if (resourcesExists) { this.visitResources(ctx.resources()).forEach(tryCatchStatement::addResource); } ctx.catchClause().stream().map(this::visitCatchClause).reduce(new LinkedList<CatchStatement>(), (r, e) -> { r.addAll(e); // merge several LinkedList<CatchStatement> instances into one LinkedList<CatchStatement> instance return r; }).forEach(tryCatchStatement::addCatch); return configureAST(tryWithResourcesASTTransformation.transform(configureAST(tryCatchStatement, ctx)), ctx); } @Override public List<ExpressionStatement> visitResources(ResourcesContext ctx) { return this.visitResourceList(ctx.resourceList()); } @Override public List<ExpressionStatement> visitResourceList(ResourceListContext ctx) { return ctx.resource().stream().map(this::visitResource).collect(Collectors.toList()); } @Override public ExpressionStatement visitResource(ResourceContext ctx) { if (asBoolean(ctx.localVariableDeclaration())) { List<ExpressionStatement> declarationStatements = this .visitLocalVariableDeclaration(ctx.localVariableDeclaration()).getDeclarationStatements(); if (declarationStatements.size() > 1) { throw createParsingFailedException("Multi resources can not be declared in one statement", ctx); } return declarationStatements.get(0); } else if (asBoolean(ctx.expression())) { Expression expression = (Expression) this.visit(ctx.expression()); boolean isVariableDeclaration = expression instanceof BinaryExpression && Types.ASSIGN == ((BinaryExpression) expression).getOperation().getType() && ((BinaryExpression) expression).getLeftExpression() instanceof VariableExpression; boolean isVariableAccess = expression instanceof VariableExpression; if (!(isVariableDeclaration || isVariableAccess)) { throw createParsingFailedException( "Only variable declarations or variable access are allowed to declare resource", ctx); } BinaryExpression assignmentExpression; if (isVariableDeclaration) { assignmentExpression = (BinaryExpression) expression; } else if (isVariableAccess) { assignmentExpression = tryWithResourcesASTTransformation.transformResourceAccess(expression); } else { throw createParsingFailedException("Unsupported resource declaration", ctx); } return configureAST(new ExpressionStatement( configureAST( new DeclarationExpression( configureAST( new VariableExpression( assignmentExpression.getLeftExpression().getText()), assignmentExpression.getLeftExpression()), assignmentExpression.getOperation(), assignmentExpression.getRightExpression()), ctx)), ctx); } throw createParsingFailedException("Unsupported resource declaration: " + ctx.getText(), ctx); } /** * Multi-catch(1..*) clause will be unpacked to several normal catch clauses, so the return type is List * * @param ctx the parse tree * @return a list of CatchStatement instances */ @Override public List<CatchStatement> visitCatchClause(CatchClauseContext ctx) { // FIXME Groovy will ignore variableModifier of parameter in the catch clause // In order to make the new parser behave same with the old one, we do not process variableModifier* return this.visitCatchType(ctx.catchType()).stream().map(e -> configureAST(new CatchStatement( // FIXME The old parser does not set location info for the parameter of the catch clause. // we could make it better //this.configureAST(new Parameter(e, this.visitIdentifier(ctx.identifier())), ctx.Identifier()), new Parameter(e, this.visitIdentifier(ctx.identifier())), this.visitBlock(ctx.block())), ctx)) .collect(Collectors.toList()); } @Override public List<ClassNode> visitCatchType(CatchTypeContext ctx) { if (!asBoolean(ctx)) { return Collections.singletonList(ClassHelper.OBJECT_TYPE); } return ctx.qualifiedClassName().stream().map(this::visitQualifiedClassName).collect(Collectors.toList()); } @Override public Statement visitFinallyBlock(FinallyBlockContext ctx) { if (!asBoolean(ctx)) { return EmptyStatement.INSTANCE; } return configureAST(this.createBlockStatement((Statement) this.visit(ctx.block())), ctx); } @Override public SwitchStatement visitSwitchStatement(SwitchStatementContext ctx) { visitingSwitchStatementCnt++; List<Statement> statementList = ctx.switchBlockStatementGroup().stream() .map(this::visitSwitchBlockStatementGroup).reduce(new LinkedList<>(), (r, e) -> { r.addAll(e); return r; }); List<CaseStatement> caseStatementList = new LinkedList<>(); List<Statement> defaultStatementList = new LinkedList<>(); statementList.forEach(e -> { if (e instanceof CaseStatement) { caseStatementList.add((CaseStatement) e); } else if (isTrue(e, IS_SWITCH_DEFAULT)) { defaultStatementList.add(e); } }); int defaultStatementListSize = defaultStatementList.size(); if (defaultStatementListSize > 1) { throw createParsingFailedException( "switch statement should have only one default case, which should appear at last", defaultStatementList.get(0)); } if (defaultStatementListSize > 0 && last(statementList) instanceof CaseStatement) { throw createParsingFailedException("default case should appear at last", defaultStatementList.get(0)); } SwitchStatement result = configureAST( new SwitchStatement(this.visitExpressionInPar(ctx.expressionInPar()), caseStatementList, defaultStatementListSize == 0 ? EmptyStatement.INSTANCE : defaultStatementList.get(0)), ctx); visitingSwitchStatementCnt--; return result; } @Override @SuppressWarnings("unchecked") public List<Statement> visitSwitchBlockStatementGroup(SwitchBlockStatementGroupContext ctx) { int labelCnt = ctx.switchLabel().size(); List<Token> firstLabelHolder = new ArrayList<>(1); return (List<Statement>) ctx.switchLabel().stream().map(e -> (Object) this.visitSwitchLabel(e)) .reduce(new ArrayList<Statement>(4), (r, e) -> { List<Statement> statementList = (List<Statement>) r; Tuple2<Token, Expression> tuple = (Tuple2<Token, Expression>) e; boolean isLast = labelCnt - 1 == statementList.size(); switch (tuple.getV1().getType()) { case CASE: { if (!asBoolean(statementList)) { firstLabelHolder.add(tuple.getV1()); } statementList.add(configureAST(new CaseStatement(tuple.getV2(), // check whether processing the last label. if yes, block statement should be attached. isLast ? this.visitBlockStatements(ctx.blockStatements()) : EmptyStatement.INSTANCE), firstLabelHolder.get(0))); break; } case DEFAULT: { BlockStatement blockStatement = this.visitBlockStatements(ctx.blockStatements()); blockStatement.putNodeMetaData(IS_SWITCH_DEFAULT, true); statementList.add( // this.configureAST(blockStatement, tuple.getKey()) blockStatement); break; } } return statementList; }); } @Override public Tuple2<Token, Expression> visitSwitchLabel(SwitchLabelContext ctx) { if (asBoolean(ctx.CASE())) { return tuple(ctx.CASE().getSymbol(), (Expression) this.visit(ctx.expression())); } else if (asBoolean(ctx.DEFAULT())) { return tuple(ctx.DEFAULT().getSymbol(), EmptyExpression.INSTANCE); } throw createParsingFailedException("Unsupported switch label: " + ctx.getText(), ctx); } @Override public SynchronizedStatement visitSynchronizedStmtAlt(SynchronizedStmtAltContext ctx) { return configureAST(new SynchronizedStatement(this.visitExpressionInPar(ctx.expressionInPar()), this.visitBlock(ctx.block())), ctx); } @Override public ReturnStatement visitReturnStmtAlt(ReturnStmtAltContext ctx) { return configureAST( new ReturnStatement(asBoolean(ctx.expression()) ? (Expression) this.visit(ctx.expression()) : ConstantExpression.EMPTY_EXPRESSION), ctx); } @Override public ThrowStatement visitThrowStmtAlt(ThrowStmtAltContext ctx) { return configureAST(new ThrowStatement((Expression) this.visit(ctx.expression())), ctx); } @Override public Statement visitLabeledStmtAlt(LabeledStmtAltContext ctx) { Statement statement = (Statement) this.visit(ctx.statement()); statement.addStatementLabel(this.visitIdentifier(ctx.identifier())); return statement; // this.configureAST(statement, ctx); } @Override public BreakStatement visitBreakStatement(BreakStatementContext ctx) { if (0 == visitingLoopStatementCnt && 0 == visitingSwitchStatementCnt) { throw createParsingFailedException("break statement is only allowed inside loops or switches", ctx); } String label = asBoolean(ctx.identifier()) ? this.visitIdentifier(ctx.identifier()) : null; return configureAST(new BreakStatement(label), ctx); } @Override public ContinueStatement visitContinueStatement(ContinueStatementContext ctx) { if (0 == visitingLoopStatementCnt) { throw createParsingFailedException("continue statement is only allowed inside loops", ctx); } String label = asBoolean(ctx.identifier()) ? this.visitIdentifier(ctx.identifier()) : null; return configureAST(new ContinueStatement(label), ctx); } // } statement -------------------------------------------------------------------- @Override public ClassNode visitTypeDeclaration(TypeDeclarationContext ctx) { if (asBoolean(ctx.classDeclaration())) { // e.g. class A {} ctx.classDeclaration().putNodeMetaData(TYPE_DECLARATION_MODIFIERS, this.visitClassOrInterfaceModifiersOpt(ctx.classOrInterfaceModifiersOpt())); return configureAST(this.visitClassDeclaration(ctx.classDeclaration()), ctx); } throw createParsingFailedException("Unsupported type declaration: " + ctx.getText(), ctx); } private void initUsingGenerics(ClassNode classNode) { if (classNode.isUsingGenerics()) { return; } if (!classNode.isEnum()) { classNode.setUsingGenerics(classNode.getSuperClass().isUsingGenerics()); } if (!classNode.isUsingGenerics() && null != classNode.getInterfaces()) { for (ClassNode anInterface : classNode.getInterfaces()) { classNode.setUsingGenerics(classNode.isUsingGenerics() || anInterface.isUsingGenerics()); if (classNode.isUsingGenerics()) break; } } } @Override public ClassNode visitClassDeclaration(ClassDeclarationContext ctx) { String packageName = moduleNode.getPackageName(); packageName = null != packageName ? packageName : ""; List<ModifierNode> modifierNodeList = ctx.getNodeMetaData(TYPE_DECLARATION_MODIFIERS); Objects.requireNonNull(modifierNodeList, "modifierNodeList should not be null"); ModifierManager modifierManager = new ModifierManager(this, modifierNodeList); int modifiers = modifierManager.getClassModifiersOpValue(); boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0); modifiers &= ~Opcodes.ACC_SYNTHETIC; final ClassNode outerClass = classNodeStack.peek(); ClassNode classNode; String className = this.visitIdentifier(ctx.identifier()); if (VAR_STR.equals(className)) { throw createParsingFailedException("var cannot be used for type declarations", ctx.identifier()); } if (asBoolean(ctx.ENUM())) { classNode = EnumHelper.makeEnumNode(asBoolean(outerClass) ? className : packageName + className, modifiers, null, outerClass); } else { if (asBoolean(outerClass)) { classNode = new InnerClassNode(outerClass, outerClass.getName() + "$" + className, modifiers | (outerClass.isInterface() ? Opcodes.ACC_STATIC : 0), ClassHelper.OBJECT_TYPE); } else { classNode = new ClassNode(packageName + className, modifiers, ClassHelper.OBJECT_TYPE); } } configureAST(classNode, ctx); classNode.putNodeMetaData(CLASS_NAME, className); classNode.setSyntheticPublic(syntheticPublic); if (asBoolean(ctx.TRAIT())) { attachTraitAnnotation(classNode); } classNode.addAnnotations(modifierManager.getAnnotations()); classNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters())); boolean isInterface = asBoolean(ctx.INTERFACE()) && !asBoolean(ctx.AT()); boolean isInterfaceWithDefaultMethods = false; // declaring interface with default method if (isInterface && this.containsDefaultMethods(ctx)) { isInterfaceWithDefaultMethods = true; attachTraitAnnotation(classNode); classNode.putNodeMetaData(IS_INTERFACE_WITH_DEFAULT_METHODS, true); } if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()) || isInterfaceWithDefaultMethods) { // class OR trait OR interface with default methods classNode.setSuperClass(this.visitType(ctx.sc)); classNode.setInterfaces(this.visitTypeList(ctx.is)); this.initUsingGenerics(classNode); } else if (isInterface) { // interface(NOT annotation) classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT); classNode.setSuperClass(ClassHelper.OBJECT_TYPE); classNode.setInterfaces(this.visitTypeList(ctx.scs)); this.initUsingGenerics(classNode); this.hackMixins(classNode); } else if (asBoolean(ctx.ENUM())) { // enum classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_ENUM | Opcodes.ACC_FINAL); classNode.setInterfaces(this.visitTypeList(ctx.is)); this.initUsingGenerics(classNode); } else if (asBoolean(ctx.AT())) { // annotation classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_ANNOTATION); classNode.addInterface(ClassHelper.Annotation_TYPE); this.hackMixins(classNode); } else { throw createParsingFailedException("Unsupported class declaration: " + ctx.getText(), ctx); } // we put the class already in output to avoid the most inner classes // will be used as first class later in the loader. The first class // there determines what GCL#parseClass for example will return, so we // have here to ensure it won't be the inner class if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT())) { classNodeList.add(classNode); } classNodeStack.push(classNode); ctx.classBody().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitClassBody(ctx.classBody()); classNodeStack.pop(); if (!(asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT()))) { classNodeList.add(classNode); } groovydocManager.handle(classNode, ctx); return classNode; } private void attachTraitAnnotation(ClassNode classNode) { classNode.addAnnotation(new AnnotationNode(ClassHelper.make(Trait.class))); } @SuppressWarnings("unchecked") private boolean containsDefaultMethods(ClassDeclarationContext ctx) { List<MethodDeclarationContext> methodDeclarationContextList = (List<MethodDeclarationContext>) ctx .classBody().classBodyDeclaration().stream().map(ClassBodyDeclarationContext::memberDeclaration) .filter(Objects::nonNull).map(e -> (Object) e.methodDeclaration()).filter(Objects::nonNull) .reduce(new LinkedList<MethodDeclarationContext>(), (r, e) -> { MethodDeclarationContext methodDeclarationContext = (MethodDeclarationContext) e; if (createModifierManager(methodDeclarationContext).containsAny(DEFAULT)) { ((List) r).add(methodDeclarationContext); } return r; }); return !methodDeclarationContextList.isEmpty(); } @Override public Void visitClassBody(ClassBodyContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); if (asBoolean(ctx.enumConstants())) { ctx.enumConstants().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitEnumConstants(ctx.enumConstants()); } ctx.classBodyDeclaration().forEach(e -> { e.putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitClassBodyDeclaration(e); }); return null; } @Override public List<FieldNode> visitEnumConstants(EnumConstantsContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); return ctx.enumConstant().stream().map(e -> { e.putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); return this.visitEnumConstant(e); }).collect(Collectors.toList()); } @Override public FieldNode visitEnumConstant(EnumConstantContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); InnerClassNode anonymousInnerClassNode = null; if (asBoolean(ctx.anonymousInnerClassDeclaration())) { ctx.anonymousInnerClassDeclaration().putNodeMetaData(ANONYMOUS_INNER_CLASS_SUPER_CLASS, classNode); anonymousInnerClassNode = this .visitAnonymousInnerClassDeclaration(ctx.anonymousInnerClassDeclaration()); } FieldNode enumConstant = EnumHelper.addEnumConstant(classNode, this.visitIdentifier(ctx.identifier()), createEnumConstantInitExpression(ctx.arguments(), anonymousInnerClassNode)); enumConstant.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); groovydocManager.handle(enumConstant, ctx); return configureAST(enumConstant, ctx); } private Expression createEnumConstantInitExpression(ArgumentsContext ctx, InnerClassNode anonymousInnerClassNode) { if (!asBoolean(ctx) && !asBoolean(anonymousInnerClassNode)) { return null; } TupleExpression argumentListExpression = (TupleExpression) this.visitArguments(ctx); List<Expression> expressions = argumentListExpression.getExpressions(); if (expressions.size() == 1) { Expression expression = expressions.get(0); if (expression instanceof NamedArgumentListExpression) { // e.g. SOME_ENUM_CONSTANT(a: "1", b: "2") List<MapEntryExpression> mapEntryExpressionList = ((NamedArgumentListExpression) expression) .getMapEntryExpressions(); ListExpression listExpression = new ListExpression( mapEntryExpressionList.stream().map(e -> (Expression) e).collect(Collectors.toList())); if (asBoolean(anonymousInnerClassNode)) { listExpression.addExpression( configureAST(new ClassExpression(anonymousInnerClassNode), anonymousInnerClassNode)); } if (mapEntryExpressionList.size() > 1) { listExpression.setWrapped(true); } return configureAST(listExpression, ctx); } if (!asBoolean(anonymousInnerClassNode)) { if (expression instanceof ListExpression) { ListExpression listExpression = new ListExpression(); listExpression.addExpression(expression); return configureAST(listExpression, ctx); } return expression; } ListExpression listExpression = new ListExpression(); if (expression instanceof ListExpression) { ((ListExpression) expression).getExpressions().forEach(listExpression::addExpression); } else { listExpression.addExpression(expression); } listExpression.addExpression( configureAST(new ClassExpression(anonymousInnerClassNode), anonymousInnerClassNode)); return configureAST(listExpression, ctx); } ListExpression listExpression = new ListExpression(expressions); if (asBoolean(anonymousInnerClassNode)) { listExpression.addExpression( configureAST(new ClassExpression(anonymousInnerClassNode), anonymousInnerClassNode)); } if (asBoolean(ctx)) { listExpression.setWrapped(true); } return asBoolean(ctx) ? configureAST(listExpression, ctx) : configureAST(listExpression, anonymousInnerClassNode); } @Override public Void visitClassBodyDeclaration(ClassBodyDeclarationContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); if (asBoolean(ctx.memberDeclaration())) { ctx.memberDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitMemberDeclaration(ctx.memberDeclaration()); } else if (asBoolean(ctx.block())) { Statement statement = this.visitBlock(ctx.block()); if (asBoolean(ctx.STATIC())) { // e.g. static { } classNode.addStaticInitializerStatements(Collections.singletonList(statement), false); } else { // e.g. { } classNode.addObjectInitializerStatements( configureAST(this.createBlockStatement(statement), statement)); } } return null; } @Override public Void visitMemberDeclaration(MemberDeclarationContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); if (asBoolean(ctx.methodDeclaration())) { ctx.methodDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitMethodDeclaration(ctx.methodDeclaration()); } else if (asBoolean(ctx.fieldDeclaration())) { ctx.fieldDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitFieldDeclaration(ctx.fieldDeclaration()); } else if (asBoolean(ctx.classDeclaration())) { ctx.classDeclaration().putNodeMetaData(TYPE_DECLARATION_MODIFIERS, this.visitModifiersOpt(ctx.modifiersOpt())); ctx.classDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitClassDeclaration(ctx.classDeclaration()); } return null; } @Override public GenericsType[] visitTypeParameters(TypeParametersContext ctx) { if (!asBoolean(ctx)) { return null; } return ctx.typeParameter().stream().map(this::visitTypeParameter).toArray(GenericsType[]::new); } @Override public GenericsType visitTypeParameter(TypeParameterContext ctx) { return configureAST( new GenericsType(configureAST(ClassHelper.make(this.visitClassName(ctx.className())), ctx), this.visitTypeBound(ctx.typeBound()), null), ctx); } @Override public ClassNode[] visitTypeBound(TypeBoundContext ctx) { if (!asBoolean(ctx)) { return null; } return ctx.type().stream().map(this::visitType).toArray(ClassNode[]::new); } @Override public Void visitFieldDeclaration(FieldDeclarationContext ctx) { ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); Objects.requireNonNull(classNode, "classNode should not be null"); ctx.variableDeclaration().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode); this.visitVariableDeclaration(ctx.variableDeclaration()); return null; } private ConstructorCallExpression checkThisAndSuperConstructorCall(Statement statement) { if (!(statement instanceof BlockStatement)) { // method code must be a BlockStatement return null; } BlockStatement blockStatement = (BlockStatement) statement; List<Statement> statementList = blockStatement.getStatements(); for (int i = 0, n = statementList.size(); i < n; i++) { Statement s = statementList.get(i); if (s instanceof ExpressionStatement) { Expression expression = ((ExpressionStatement) s).getExpression(); if ((expression instanceof ConstructorCallExpression) && 0 != i) { return (ConstructorCallExpression) expression; } } } return null; } private ModifierManager createModifierManager(MethodDeclarationContext ctx) { List<ModifierNode> modifierNodeList = Collections.emptyList(); if (asBoolean(ctx.modifiersOpt())) { modifierNodeList = this.visitModifiersOpt(ctx.modifiersOpt()); } return new ModifierManager(this, modifierNodeList); } private void validateParametersOfMethodDeclaration(Parameter[] parameters, ClassNode classNode) { if (!classNode.isInterface()) { return; } for (Parameter parameter : parameters) { if (parameter.hasInitialExpression()) { throw createParsingFailedException( "Cannot specify default value for method parameter '" + parameter.getName() + " = " + parameter.getInitialExpression().getText() + "' inside an interface", parameter); } } } @Override public MethodNode visitMethodDeclaration(MethodDeclarationContext ctx) { ModifierManager modifierManager = createModifierManager(ctx); if (modifierManager.containsAny(VAR)) { throw createParsingFailedException("var cannot be used for method declarations", ctx); } String methodName = this.visitMethodName(ctx.methodName()); ClassNode returnType = this.visitReturnType(ctx.returnType()); Parameter[] parameters = this.visitFormalParameters(ctx.formalParameters()); ClassNode[] exceptions = this.visitQualifiedClassNameList(ctx.qualifiedClassNameList()); anonymousInnerClassesDefinedInMethodStack.push(new LinkedList<>()); Statement code = this.visitMethodBody(ctx.methodBody()); List<InnerClassNode> anonymousInnerClassList = anonymousInnerClassesDefinedInMethodStack.pop(); MethodNode methodNode; // if classNode is not null, the method declaration is for class declaration ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); if (asBoolean(classNode)) { validateParametersOfMethodDeclaration(parameters, classNode); methodNode = createConstructorOrMethodNodeForClass(ctx, modifierManager, methodName, returnType, parameters, exceptions, code, classNode); } else { // script method declaration methodNode = createScriptMethodNode(modifierManager, methodName, returnType, parameters, exceptions, code); } anonymousInnerClassList.forEach(e -> e.setEnclosingMethod(methodNode)); methodNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters())); methodNode.setSyntheticPublic(this.isSyntheticPublic(this.isAnnotationDeclaration(classNode), classNode instanceof EnumConstantClassNode, asBoolean(ctx.returnType()), modifierManager)); if (modifierManager.containsAny(STATIC)) { for (Parameter parameter : methodNode.getParameters()) { parameter.setInStaticContext(true); } methodNode.getVariableScope().setInStaticContext(true); } configureAST(methodNode, ctx); validateMethodDeclaration(ctx, methodNode, modifierManager, classNode); groovydocManager.handle(methodNode, ctx); return methodNode; } private void validateMethodDeclaration(MethodDeclarationContext ctx, MethodNode methodNode, ModifierManager modifierManager, ClassNode classNode) { if (1 == ctx.t || 2 == ctx.t || 3 == ctx.t) { // 1: normal method declaration; 2: abstract method declaration; 3: normal method declaration OR abstract method declaration if (!(asBoolean(ctx.modifiersOpt().modifiers()) || asBoolean(ctx.returnType()))) { throw createParsingFailedException("Modifiers or return type is required", ctx); } } if (1 == ctx.t) { if (!asBoolean(ctx.methodBody())) { throw createParsingFailedException("Method body is required", ctx); } } if (2 == ctx.t) { if (asBoolean(ctx.methodBody())) { throw createParsingFailedException("Abstract method should not have method body", ctx); } } boolean isAbstractMethod = methodNode.isAbstract(); boolean hasMethodBody = asBoolean(methodNode.getCode()); if (9 == ctx.ct) { // script if (isAbstractMethod || !hasMethodBody) { // method should not be declared abstract in the script throw createParsingFailedException("You cannot define " + (isAbstractMethod ? "an abstract" : "a") + " method[" + methodNode.getName() + "] " + (!hasMethodBody ? "without method body " : "") + "in the script. Try " + (isAbstractMethod ? "removing the 'abstract'" : "") + (isAbstractMethod && !hasMethodBody ? " and" : "") + (!hasMethodBody ? " adding a method body" : ""), methodNode); } } else { if (4 == ctx.ct) { // trait if (isAbstractMethod && hasMethodBody) { throw createParsingFailedException("Abstract method should not have method body", ctx); } } if (!isAbstractMethod && !hasMethodBody) { // non-abstract method without body in the non-script(e.g. class, enum, trait) is not allowed! throw createParsingFailedException("You defined a method[" + methodNode.getName() + "] without a body. Try adding a method body, or declare it abstract", methodNode); } boolean isInterfaceOrAbstractClass = asBoolean(classNode) && classNode.isAbstract() && !classNode.isAnnotationDefinition(); if (isInterfaceOrAbstractClass && !modifierManager.containsAny(DEFAULT) && isAbstractMethod && hasMethodBody) { throw createParsingFailedException("You defined an abstract method[" + methodNode.getName() + "] with a body. Try removing the method body" + (classNode.isInterface() ? ", or declare it default" : ""), methodNode); } } modifierManager.validate(methodNode); if (methodNode instanceof ConstructorNode) { modifierManager.validate((ConstructorNode) methodNode); } } private MethodNode createScriptMethodNode(ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) { MethodNode methodNode; methodNode = new MethodNode(methodName, modifierManager.containsAny(PRIVATE) ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC, returnType, parameters, exceptions, code); modifierManager.processMethodNode(methodNode); return methodNode; } private MethodNode createConstructorOrMethodNodeForClass(MethodDeclarationContext ctx, ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode) { MethodNode methodNode; String className = classNode.getNodeMetaData(CLASS_NAME); int modifiers = modifierManager.getClassMemberModifiersOpValue(); boolean hasReturnType = asBoolean(ctx.returnType()); boolean hasMethodBody = asBoolean(ctx.methodBody()); if (!hasReturnType && hasMethodBody && methodName.equals(className)) { // constructor declaration methodNode = createConstructorNodeForClass(methodName, parameters, exceptions, code, classNode, modifiers); } else { // class memeber method declaration if (!hasReturnType && hasMethodBody && (0 == modifierManager.getModifierCount())) { throw createParsingFailedException("Invalid method declaration: " + methodName, ctx); } methodNode = createMethodNodeForClass(ctx, modifierManager, methodName, returnType, parameters, exceptions, code, classNode, modifiers); } modifierManager.attachAnnotations(methodNode); return methodNode; } private MethodNode createMethodNodeForClass(MethodDeclarationContext ctx, ModifierManager modifierManager, String methodName, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode, int modifiers) { MethodNode methodNode; if (asBoolean(ctx.elementValue())) { // the code of annotation method code = configureAST(new ExpressionStatement(this.visitElementValue(ctx.elementValue())), ctx.elementValue()); } modifiers |= !modifierManager.containsAny(STATIC) && (classNode.isInterface() || (isTrue(classNode, IS_INTERFACE_WITH_DEFAULT_METHODS) && !modifierManager.containsAny(DEFAULT))) ? Opcodes.ACC_ABSTRACT : 0; checkWhetherMethodNodeWithSameSignatureExists(classNode, methodName, parameters, ctx); methodNode = classNode.addMethod(methodName, modifiers, returnType, parameters, exceptions, code); methodNode.setAnnotationDefault(asBoolean(ctx.elementValue())); return methodNode; } private void checkWhetherMethodNodeWithSameSignatureExists(ClassNode classNode, String methodName, Parameter[] parameters, MethodDeclarationContext ctx) { MethodNode sameSigMethodNode = classNode.getDeclaredMethod(methodName, parameters); if (null == sameSigMethodNode) { return; } throw createParsingFailedException( "The method " + sameSigMethodNode.getText() + " duplicates another method of the same signature", ctx); } private ConstructorNode createConstructorNodeForClass(String methodName, Parameter[] parameters, ClassNode[] exceptions, Statement code, ClassNode classNode, int modifiers) { ConstructorCallExpression thisOrSuperConstructorCallExpression = this .checkThisAndSuperConstructorCall(code); if (asBoolean(thisOrSuperConstructorCallExpression)) { throw createParsingFailedException( thisOrSuperConstructorCallExpression.getText() + " should be the first statement in the constructor[" + methodName + "]", thisOrSuperConstructorCallExpression); } return classNode.addConstructor(modifiers, parameters, exceptions, code); } @Override public String visitMethodName(MethodNameContext ctx) { if (asBoolean(ctx.identifier())) { return this.visitIdentifier(ctx.identifier()); } if (asBoolean(ctx.stringLiteral())) { return this.visitStringLiteral(ctx.stringLiteral()).getText(); } throw createParsingFailedException("Unsupported method name: " + ctx.getText(), ctx); } @Override public ClassNode visitReturnType(ReturnTypeContext ctx) { if (!asBoolean(ctx)) { return ClassHelper.OBJECT_TYPE; } if (asBoolean(ctx.type())) { return this.visitType(ctx.type()); } if (asBoolean(ctx.VOID())) { return ClassHelper.VOID_TYPE; } throw createParsingFailedException("Unsupported return type: " + ctx.getText(), ctx); } @Override public Statement visitMethodBody(MethodBodyContext ctx) { if (!asBoolean(ctx)) { return null; } return configureAST(this.visitBlock(ctx.block()), ctx); } @Override public DeclarationListStatement visitLocalVariableDeclaration(LocalVariableDeclarationContext ctx) { return configureAST(this.visitVariableDeclaration(ctx.variableDeclaration()), ctx); } private ModifierManager createModifierManager(VariableDeclarationContext ctx) { return new ModifierManager(this, this.visitClassifiedModifiers(ctx.classifiedModifiers())); } private DeclarationListStatement createMultiAssignmentDeclarationListStatement(VariableDeclarationContext ctx, ModifierManager modifierManager) { /* if (!modifierManager.contains(DEF)) { throw createParsingFailedException("keyword def is required to declare tuple, e.g. def (int a, int b) = [1, 2]", ctx); } */ return configureAST( new DeclarationListStatement( configureAST(modifierManager.attachAnnotations(new DeclarationExpression( new ArgumentListExpression(this.visitTypeNamePairs(ctx.typeNamePairs()).stream() .peek(e -> modifierManager .processVariableExpression((VariableExpression) e)) .collect(Collectors.toList())), this.createGroovyTokenByType(ctx.ASSIGN().getSymbol(), Types.ASSIGN), this.visitVariableInitializer(ctx.variableInitializer()))), ctx)), ctx); } @Override public List<ModifierNode> visitClassifiedModifiers(ClassifiedModifiersContext ctx) { List<ModifierNode> modifierNodeList = Collections.emptyList(); if (!asBoolean(ctx)) { return modifierNodeList; } if (asBoolean(ctx.variableModifiers())) { modifierNodeList = this.visitVariableModifiers(ctx.variableModifiers()); } if (asBoolean(ctx.modifiers())) { modifierNodeList = this.visitModifiers(ctx.modifiers()); } return modifierNodeList; } @Override public DeclarationListStatement visitVariableDeclaration(VariableDeclarationContext ctx) { ModifierManager modifierManager = this.createModifierManager(ctx); if (asBoolean(ctx.typeNamePairs())) { // e.g. def (int a, int b) = [1, 2] return this.createMultiAssignmentDeclarationListStatement(ctx, modifierManager); } ClassNode variableType = this.visitType(ctx.type()); ctx.variableDeclarators().putNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE, variableType); List<DeclarationExpression> declarationExpressionList = this .visitVariableDeclarators(ctx.variableDeclarators()); // if classNode is not null, the variable declaration is for class declaration. In other words, it is a field declaration ClassNode classNode = ctx.getNodeMetaData(CLASS_DECLARATION_CLASS_NODE); if (asBoolean(classNode)) { return createFieldDeclarationListStatement(ctx, modifierManager, variableType, declarationExpressionList, classNode); } declarationExpressionList.forEach(e -> { VariableExpression variableExpression = (VariableExpression) e.getLeftExpression(); modifierManager.processVariableExpression(variableExpression); modifierManager.attachAnnotations(e); }); int size = declarationExpressionList.size(); if (size > 0) { DeclarationExpression declarationExpression = declarationExpressionList.get(0); if (1 == size) { configureAST(declarationExpression, ctx); } else { // Tweak start of first declaration declarationExpression.setLineNumber(ctx.getStart().getLine()); declarationExpression.setColumnNumber(ctx.getStart().getCharPositionInLine() + 1); } } return configureAST(new DeclarationListStatement(declarationExpressionList), ctx); } private DeclarationListStatement createFieldDeclarationListStatement(VariableDeclarationContext ctx, ModifierManager modifierManager, ClassNode variableType, List<DeclarationExpression> declarationExpressionList, ClassNode classNode) { for (int i = 0, n = declarationExpressionList.size(); i < n; i++) { DeclarationExpression declarationExpression = declarationExpressionList.get(i); VariableExpression variableExpression = (VariableExpression) declarationExpression.getLeftExpression(); String fieldName = variableExpression.getName(); int modifiers = modifierManager.getClassMemberModifiersOpValue(); Expression initialValue = declarationExpression.getRightExpression() instanceof EmptyExpression ? null : declarationExpression.getRightExpression(); Object defaultValue = findDefaultValueByType(variableType); if (classNode.isInterface()) { if (!asBoolean(initialValue)) { initialValue = !asBoolean(defaultValue) ? null : new ConstantExpression(defaultValue); } modifiers |= Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL; } if (isFieldDeclaration(modifierManager, classNode)) { declareField(ctx, modifierManager, variableType, classNode, i, variableExpression, fieldName, modifiers, initialValue); } else { declareProperty(ctx, modifierManager, variableType, classNode, i, variableExpression, fieldName, modifiers, initialValue); } } return null; } private void declareProperty(VariableDeclarationContext ctx, ModifierManager modifierManager, ClassNode variableType, ClassNode classNode, int i, VariableExpression variableExpression, String fieldName, int modifiers, Expression initialValue) { if (classNode.hasProperty(fieldName)) { throw createParsingFailedException("The property '" + fieldName + "' is declared multiple times", ctx); } PropertyNode propertyNode; FieldNode fieldNode = classNode.getDeclaredField(fieldName); if (fieldNode != null && !classNode.hasProperty(fieldName)) { classNode.getFields().remove(fieldNode); propertyNode = new PropertyNode(fieldNode, modifiers | Opcodes.ACC_PUBLIC, null, null); classNode.addProperty(propertyNode); } else { propertyNode = classNode.addProperty(fieldName, modifiers | Opcodes.ACC_PUBLIC, variableType, initialValue, null, null); fieldNode = propertyNode.getField(); } fieldNode.setModifiers(modifiers & ~Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE); fieldNode.setSynthetic(!classNode.isInterface()); modifierManager.attachAnnotations(fieldNode); groovydocManager.handle(fieldNode, ctx); groovydocManager.handle(propertyNode, ctx); if (0 == i) { configureAST(fieldNode, ctx, initialValue); configureAST(propertyNode, ctx, initialValue); } else { configureAST(fieldNode, variableExpression, initialValue); configureAST(propertyNode, variableExpression, initialValue); } } private void declareField(VariableDeclarationContext ctx, ModifierManager modifierManager, ClassNode variableType, ClassNode classNode, int i, VariableExpression variableExpression, String fieldName, int modifiers, Expression initialValue) { FieldNode existingFieldNode = classNode.getDeclaredField(fieldName); if (null != existingFieldNode && !existingFieldNode.isSynthetic()) { throw createParsingFailedException("The field '" + fieldName + "' is declared multiple times", ctx); } FieldNode fieldNode; PropertyNode propertyNode = classNode.getProperty(fieldName); if (null != propertyNode && propertyNode.getField().isSynthetic()) { classNode.getFields().remove(propertyNode.getField()); fieldNode = new FieldNode(fieldName, modifiers, variableType, classNode.redirect(), initialValue); propertyNode.setField(fieldNode); classNode.addField(fieldNode); } else { fieldNode = classNode.addField(fieldName, modifiers, variableType, initialValue); } modifierManager.attachAnnotations(fieldNode); groovydocManager.handle(fieldNode, ctx); if (0 == i) { configureAST(fieldNode, ctx, initialValue); } else { configureAST(fieldNode, variableExpression, initialValue); } } private boolean isFieldDeclaration(ModifierManager modifierManager, ClassNode classNode) { return classNode.isInterface() || modifierManager.containsVisibilityModifier(); } @Override public List<Expression> visitTypeNamePairs(TypeNamePairsContext ctx) { return ctx.typeNamePair().stream().map(this::visitTypeNamePair).collect(Collectors.toList()); } @Override public VariableExpression visitTypeNamePair(TypeNamePairContext ctx) { return configureAST( new VariableExpression(this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName(), this.visitType(ctx.type())), ctx); } @Override public List<DeclarationExpression> visitVariableDeclarators(VariableDeclaratorsContext ctx) { ClassNode variableType = ctx.getNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE); Objects.requireNonNull(variableType, "variableType should not be null"); return ctx.variableDeclarator().stream().map(e -> { e.putNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE, variableType); return this.visitVariableDeclarator(e); // return this.configureAST(this.visitVariableDeclarator(e), ctx); }).collect(Collectors.toList()); } @Override public DeclarationExpression visitVariableDeclarator(VariableDeclaratorContext ctx) { ClassNode variableType = ctx.getNodeMetaData(VARIABLE_DECLARATION_VARIABLE_TYPE); Objects.requireNonNull(variableType, "variableType should not be null"); org.codehaus.groovy.syntax.Token token; if (asBoolean(ctx.ASSIGN())) { token = createGroovyTokenByType(ctx.ASSIGN().getSymbol(), Types.ASSIGN); } else { token = new org.codehaus.groovy.syntax.Token(Types.ASSIGN, ASSIGN_STR, ctx.start.getLine(), 1); } return configureAST( new DeclarationExpression( configureAST(new VariableExpression( this.visitVariableDeclaratorId(ctx.variableDeclaratorId()).getName(), variableType), ctx.variableDeclaratorId()), token, this.visitVariableInitializer(ctx.variableInitializer())), ctx); } @Override public Expression visitVariableInitializer(VariableInitializerContext ctx) { if (!asBoolean(ctx)) { return EmptyExpression.INSTANCE; } return configureAST(this.visitEnhancedStatementExpression(ctx.enhancedStatementExpression()), ctx); } @Override public List<Expression> visitVariableInitializers(VariableInitializersContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } return ctx.variableInitializer().stream().map(this::visitVariableInitializer).collect(Collectors.toList()); } private int visitingArrayInitializerCnt = 0; @Override public List<Expression> visitArrayInitializer(ArrayInitializerContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } try { visitingArrayInitializerCnt++; return this.visitVariableInitializers(ctx.variableInitializers()); } finally { visitingArrayInitializerCnt--; } } @Override public Statement visitBlock(BlockContext ctx) { if (!asBoolean(ctx)) { return this.createBlockStatement(); } return configureAST(this.visitBlockStatementsOpt(ctx.blockStatementsOpt()), ctx); } @Override public ExpressionStatement visitCommandExprAlt(CommandExprAltContext ctx) { return configureAST(new ExpressionStatement(this.visitCommandExpression(ctx.commandExpression())), ctx); } @Override public Expression visitCommandExpression(CommandExpressionContext ctx) { boolean hasArgumentList = asBoolean(ctx.enhancedArgumentList()); boolean hasCommandArgument = asBoolean(ctx.commandArgument()); if (visitingArrayInitializerCnt > 0 && (hasArgumentList || hasCommandArgument)) { // To avoid ambiguities, command chain expression should not be used in array initializer // the old parser does not support either, so no breaking changes // SEE http://groovy.329449.n5.nabble.com/parrot-Command-expressions-in-array-initializer-tt5752273.html throw createParsingFailedException("Command chain expression can not be used in array initializer", ctx); } Expression baseExpr = (Expression) this.visit(ctx.expression()); if (hasArgumentList || hasCommandArgument) { if (baseExpr instanceof BinaryExpression) { if (!"[".equals(((BinaryExpression) baseExpr).getOperation().getText()) && !isInsideParentheses(baseExpr)) { throw createParsingFailedException( "Unexpected input: '" + getOriginalText(ctx.expression()) + "'", ctx.expression()); } } } MethodCallExpression methodCallExpression = null; if (hasArgumentList) { Expression arguments = this.visitEnhancedArgumentList(ctx.enhancedArgumentList()); if (baseExpr instanceof PropertyExpression) { // e.g. obj.a 1, 2 methodCallExpression = configureAST( this.createMethodCallExpression((PropertyExpression) baseExpr, arguments), arguments); } else if (baseExpr instanceof MethodCallExpression && !isInsideParentheses(baseExpr)) { // e.g. m {} a, b OR m(...) a, b if (asBoolean(arguments)) { // The error should never be thrown. throw new GroovyBugError( "When baseExpr is a instance of MethodCallExpression, which should follow NO argumentList"); } methodCallExpression = (MethodCallExpression) baseExpr; } else if (!isInsideParentheses(baseExpr) && (baseExpr instanceof VariableExpression /* e.g. m 1, 2 */ || baseExpr instanceof GStringExpression /* e.g. "$m" 1, 2 */ || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING)) /* e.g. "m" 1, 2 */)) { methodCallExpression = configureAST(this.createMethodCallExpression(baseExpr, arguments), arguments); } else { // e.g. a[x] b, new A() b, etc. methodCallExpression = configureAST(this.createCallMethodCallExpression(baseExpr, arguments), arguments); } methodCallExpression.putNodeMetaData(IS_COMMAND_EXPRESSION, true); if (!hasCommandArgument) { return configureAST(methodCallExpression, ctx); } } if (hasCommandArgument) { baseExpr.putNodeMetaData(IS_COMMAND_EXPRESSION, true); } return configureAST((Expression) ctx.commandArgument().stream().map(e -> (Object) e) .reduce(null == methodCallExpression ? baseExpr : methodCallExpression, (r, e) -> { CommandArgumentContext commandArgumentContext = (CommandArgumentContext) e; commandArgumentContext.putNodeMetaData(CMD_EXPRESSION_BASE_EXPR, r); return this.visitCommandArgument(commandArgumentContext); }), ctx); } @Override public Expression visitCommandArgument(CommandArgumentContext ctx) { // e.g. x y a b we call "x y" as the base expression Expression baseExpr = ctx.getNodeMetaData(CMD_EXPRESSION_BASE_EXPR); Expression primaryExpr = (Expression) this.visit(ctx.primary()); if (asBoolean(ctx.enhancedArgumentList())) { // e.g. x y a b if (baseExpr instanceof PropertyExpression) { // the branch should never reach, because a.b.c will be parsed as a path expression, not a method call throw createParsingFailedException("Unsupported command argument: " + ctx.getText(), ctx); } // the following code will process "a b" of "x y a b" MethodCallExpression methodCallExpression = new MethodCallExpression(baseExpr, this.createConstantExpression(primaryExpr), this.visitEnhancedArgumentList(ctx.enhancedArgumentList())); methodCallExpression.setImplicitThis(false); return configureAST(methodCallExpression, ctx); } else if (asBoolean(ctx.pathElement())) { // e.g. x y a.b Expression pathExpression = this.createPathExpression( configureAST(new PropertyExpression(baseExpr, this.createConstantExpression(primaryExpr)), primaryExpr), ctx.pathElement()); return configureAST(pathExpression, ctx); } // e.g. x y a return configureAST(new PropertyExpression(baseExpr, primaryExpr instanceof VariableExpression ? this.createConstantExpression(primaryExpr) : primaryExpr), primaryExpr); } // expression { -------------------------------------------------------------------- @Override public ClassNode visitCastParExpression(CastParExpressionContext ctx) { return this.visitType(ctx.type()); } @Override public Expression visitParExpression(ParExpressionContext ctx) { Expression expression = this.visitExpressionInPar(ctx.expressionInPar()); Integer insideParenLevel = expression.getNodeMetaData(INSIDE_PARENTHESES_LEVEL); if (null != insideParenLevel) { insideParenLevel++; } else { insideParenLevel = 1; } expression.putNodeMetaData(INSIDE_PARENTHESES_LEVEL, insideParenLevel); return configureAST(expression, ctx); } @Override public Expression visitExpressionInPar(ExpressionInParContext ctx) { return this.visitEnhancedStatementExpression(ctx.enhancedStatementExpression()); } @Override public Expression visitEnhancedStatementExpression(EnhancedStatementExpressionContext ctx) { Expression expression; if (asBoolean(ctx.statementExpression())) { expression = ((ExpressionStatement) this.visit(ctx.statementExpression())).getExpression(); } else if (asBoolean(ctx.standardLambdaExpression())) { expression = this.visitStandardLambdaExpression(ctx.standardLambdaExpression()); } else { throw createParsingFailedException("Unsupported enhanced statement expression: " + ctx.getText(), ctx); } return configureAST(expression, ctx); } @Override public Expression visitPathExpression(PathExpressionContext ctx) { return this.createPathExpression((Expression) this.visit(ctx.primary()), ctx.pathElement()); } @Override public Expression visitPathElement(PathElementContext ctx) { Expression baseExpr = ctx.getNodeMetaData(PATH_EXPRESSION_BASE_EXPR); Objects.requireNonNull(baseExpr, "baseExpr is required!"); if (asBoolean(ctx.namePart())) { Expression namePartExpr = this.visitNamePart(ctx.namePart()); GenericsType[] genericsTypes = this.visitNonWildcardTypeArguments(ctx.nonWildcardTypeArguments()); if (asBoolean(ctx.DOT())) { boolean isSafeChain = isTrue(baseExpr, PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN); return createDotExpression(ctx, baseExpr, namePartExpr, genericsTypes, isSafeChain); } else if (asBoolean(ctx.SAFE_DOT())) { return createDotExpression(ctx, baseExpr, namePartExpr, genericsTypes, true); } else if (asBoolean(ctx.SAFE_CHAIN_DOT())) { // e.g. obj??.a OR obj??.@a Expression expression = createDotExpression(ctx, baseExpr, namePartExpr, genericsTypes, true); expression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN, true); return expression; } else if (asBoolean(ctx.METHOD_POINTER())) { // e.g. obj.&m return configureAST(new MethodPointerExpression(baseExpr, namePartExpr), ctx); } else if (asBoolean(ctx.METHOD_REFERENCE())) { // e.g. obj::m return configureAST(new MethodReferenceExpression(baseExpr, namePartExpr), ctx); } else if (asBoolean(ctx.SPREAD_DOT())) { if (asBoolean(ctx.AT())) { // e.g. obj*.@a AttributeExpression attributeExpression = new AttributeExpression(baseExpr, namePartExpr, true); attributeExpression.setSpreadSafe(true); return configureAST(attributeExpression, ctx); } else { // e.g. obj*.p PropertyExpression propertyExpression = new PropertyExpression(baseExpr, namePartExpr, true); propertyExpression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes); propertyExpression.setSpreadSafe(true); return configureAST(propertyExpression, ctx); } } } else if (asBoolean(ctx.creator())) { CreatorContext creatorContext = ctx.creator(); creatorContext.putNodeMetaData(ENCLOSING_INSTANCE_EXPRESSION, baseExpr); return configureAST(this.visitCreator(creatorContext), ctx); } else if (asBoolean(ctx.indexPropertyArgs())) { // e.g. list[1, 3, 5] Tuple2<Token, Expression> tuple = this.visitIndexPropertyArgs(ctx.indexPropertyArgs()); boolean isSafeChain = isTrue(baseExpr, PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN); return configureAST(new BinaryExpression(baseExpr, createGroovyToken(tuple.getV1()), tuple.getV2(), isSafeChain || asBoolean(ctx.indexPropertyArgs().QUESTION())), ctx); } else if (asBoolean(ctx.namedPropertyArgs())) { // this is a special way to signify a cast, e.g. Person[name: 'Daniel.Sun', location: 'Shanghai'] List<MapEntryExpression> mapEntryExpressionList = this.visitNamedPropertyArgs(ctx.namedPropertyArgs()); Expression right; Expression firstKeyExpression; int mapEntryExpressionListSize = mapEntryExpressionList.size(); if (mapEntryExpressionListSize == 0) { // expecting list of MapEntryExpressions later so use SpreadMap to smuggle empty MapExpression to later stages right = configureAST( new SpreadMapExpression(configureAST(new MapExpression(), ctx.namedPropertyArgs())), ctx.namedPropertyArgs()); } else if (mapEntryExpressionListSize == 1 && (firstKeyExpression = mapEntryExpressionList.get(0) .getKeyExpression()) instanceof SpreadMapExpression) { right = firstKeyExpression; } else { ListExpression listExpression = configureAST( new ListExpression(mapEntryExpressionList.stream().map(e -> { if (e.getKeyExpression() instanceof SpreadMapExpression) { return e.getKeyExpression(); } return e; }).collect(Collectors.toList())), ctx.namedPropertyArgs()); listExpression.setWrapped(true); right = listExpression; } return configureAST(new BinaryExpression(baseExpr, createGroovyToken(ctx.namedPropertyArgs().LBRACK().getSymbol()), right), ctx); } else if (asBoolean(ctx.arguments())) { Expression argumentsExpr = this.visitArguments(ctx.arguments()); configureAST(argumentsExpr, ctx); if (isInsideParentheses(baseExpr)) { // e.g. (obj.x)(), (obj.@x)() return configureAST(createCallMethodCallExpression(baseExpr, argumentsExpr), ctx); } if (baseExpr instanceof AttributeExpression) { // e.g. obj.@a(1, 2) AttributeExpression attributeExpression = (AttributeExpression) baseExpr; attributeExpression.setSpreadSafe(false); // whether attributeExpression is spread safe or not, we must reset it as false return configureAST(createCallMethodCallExpression(attributeExpression, argumentsExpr, true), ctx); } if (baseExpr instanceof PropertyExpression) { // e.g. obj.a(1, 2) MethodCallExpression methodCallExpression = this .createMethodCallExpression((PropertyExpression) baseExpr, argumentsExpr); return configureAST(methodCallExpression, ctx); } if (baseExpr instanceof VariableExpression) { // void and primitive type AST node must be an instance of VariableExpression String baseExprText = baseExpr.getText(); if (VOID_STR.equals(baseExprText)) { // e.g. void() return configureAST( createCallMethodCallExpression(this.createConstantExpression(baseExpr), argumentsExpr), ctx); } else if (isPrimitiveType(baseExprText)) { // e.g. int(), long(), float(), etc. throw createParsingFailedException( "Primitive type literal: " + baseExprText + " cannot be used as a method name", ctx); } } if (baseExpr instanceof VariableExpression || baseExpr instanceof GStringExpression || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING))) { // e.g. m(), "$m"(), "m"() String baseExprText = baseExpr.getText(); if (SUPER_STR.equals(baseExprText) || THIS_STR.equals(baseExprText)) { // e.g. this(...), super(...) // class declaration is not allowed in the closure, // so if this and super is inside the closure, it will not be constructor call. // e.g. src/test/org/codehaus/groovy/transform/MapConstructorTransformTest.groovy: // @MapConstructor(pre={ super(args?.first, args?.last); args = args ?: [:] }, post = { first = first?.toUpperCase() }) if (visitingClosureCnt > 0) { return configureAST(new MethodCallExpression(baseExpr, baseExprText, argumentsExpr), ctx); } return configureAST(new ConstructorCallExpression( SUPER_STR.equals(baseExprText) ? ClassNode.SUPER : ClassNode.THIS, argumentsExpr), ctx); } MethodCallExpression methodCallExpression = this.createMethodCallExpression(baseExpr, argumentsExpr); return configureAST(methodCallExpression, ctx); } // e.g. 1(), 1.1(), ((int) 1 / 2)(1, 2), {a, b -> a + b }(1, 2), m()() return configureAST(createCallMethodCallExpression(baseExpr, argumentsExpr), ctx); } else if (asBoolean(ctx.closureOrLambdaExpression())) { ClosureExpression closureExpression = this .visitClosureOrLambdaExpression(ctx.closureOrLambdaExpression()); if (baseExpr instanceof MethodCallExpression) { MethodCallExpression methodCallExpression = (MethodCallExpression) baseExpr; Expression argumentsExpression = methodCallExpression.getArguments(); if (argumentsExpression instanceof ArgumentListExpression) { // normal arguments, e.g. 1, 2 ArgumentListExpression argumentListExpression = (ArgumentListExpression) argumentsExpression; argumentListExpression.getExpressions().add(closureExpression); return configureAST(methodCallExpression, ctx); } if (argumentsExpression instanceof TupleExpression) { // named arguments, e.g. x: 1, y: 2 TupleExpression tupleExpression = (TupleExpression) argumentsExpression; NamedArgumentListExpression namedArgumentListExpression = (NamedArgumentListExpression) tupleExpression .getExpression(0); if (asBoolean(tupleExpression.getExpressions())) { methodCallExpression .setArguments( configureAST(new ArgumentListExpression(Stream .of(configureAST( new MapExpression(namedArgumentListExpression .getMapEntryExpressions()), namedArgumentListExpression), closureExpression) .collect(Collectors.toList())), tupleExpression)); } else { // the branch should never reach, because named arguments must not be empty methodCallExpression.setArguments( configureAST(new ArgumentListExpression(closureExpression), tupleExpression)); } return configureAST(methodCallExpression, ctx); } } // e.g. 1 {}, 1.1 {} if (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_NUMERIC)) { return configureAST( this.createCallMethodCallExpression(baseExpr, configureAST(new ArgumentListExpression(closureExpression), closureExpression)), ctx); } if (baseExpr instanceof PropertyExpression) { // e.g. obj.m { } PropertyExpression propertyExpression = (PropertyExpression) baseExpr; MethodCallExpression methodCallExpression = this.createMethodCallExpression(propertyExpression, configureAST(new ArgumentListExpression(closureExpression), closureExpression)); return configureAST(methodCallExpression, ctx); } // e.g. m { return 1; } MethodCallExpression methodCallExpression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, (baseExpr instanceof VariableExpression) ? this.createConstantExpression(baseExpr) : baseExpr, configureAST(new ArgumentListExpression(closureExpression), closureExpression)); return configureAST(methodCallExpression, ctx); } throw createParsingFailedException("Unsupported path element: " + ctx.getText(), ctx); } private Expression createDotExpression(PathElementContext ctx, Expression baseExpr, Expression namePartExpr, GenericsType[] genericsTypes, boolean safe) { if (asBoolean(ctx.AT())) { // e.g. obj.@a OR obj?.@a return configureAST(new AttributeExpression(baseExpr, namePartExpr, safe), ctx); } else { // e.g. obj.p OR obj?.p PropertyExpression propertyExpression = new PropertyExpression(baseExpr, namePartExpr, safe); propertyExpression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES, genericsTypes); return configureAST(propertyExpression, ctx); } } private MethodCallExpression createCallMethodCallExpression(Expression baseExpr, Expression argumentsExpr) { return createCallMethodCallExpression(baseExpr, argumentsExpr, false); } private MethodCallExpression createCallMethodCallExpression(Expression baseExpr, Expression argumentsExpr, boolean implicitThis) { MethodCallExpression methodCallExpression = new MethodCallExpression(baseExpr, CALL_STR, argumentsExpr); methodCallExpression.setImplicitThis(implicitThis); return methodCallExpression; } @Override public GenericsType[] visitNonWildcardTypeArguments(NonWildcardTypeArgumentsContext ctx) { if (!asBoolean(ctx)) { return null; } return Arrays.stream(this.visitTypeList(ctx.typeList())).map(this::createGenericsType) .toArray(GenericsType[]::new); } @Override public ClassNode[] visitTypeList(TypeListContext ctx) { if (!asBoolean(ctx)) { return ClassNode.EMPTY_ARRAY; } return ctx.type().stream().map(this::visitType).toArray(ClassNode[]::new); } @Override public Expression visitArguments(ArgumentsContext ctx) { if (asBoolean(ctx) && asBoolean(ctx.COMMA()) && !asBoolean(ctx.enhancedArgumentList())) { throw createParsingFailedException("Expression expected", ctx.COMMA()); } if (!asBoolean(ctx) || !asBoolean(ctx.enhancedArgumentList())) { return new ArgumentListExpression(); } return configureAST(this.visitEnhancedArgumentList(ctx.enhancedArgumentList()), ctx); } @Override public Expression visitEnhancedArgumentList(EnhancedArgumentListContext ctx) { if (!asBoolean(ctx)) { return null; } List<Expression> expressionList = new LinkedList<>(); List<MapEntryExpression> mapEntryExpressionList = new LinkedList<>(); ctx.enhancedArgumentListElement().stream().map(this::visitEnhancedArgumentListElement).forEach(e -> { if (e instanceof MapEntryExpression) { MapEntryExpression mapEntryExpression = (MapEntryExpression) e; validateDuplicatedNamedParameter(mapEntryExpressionList, mapEntryExpression); mapEntryExpressionList.add(mapEntryExpression); } else { expressionList.add(e); } }); if (!asBoolean(mapEntryExpressionList)) { // e.g. arguments like 1, 2 OR someArg, e -> e return configureAST(new ArgumentListExpression(expressionList), ctx); } if (!asBoolean(expressionList)) { // e.g. arguments like x: 1, y: 2 return configureAST( new TupleExpression(configureAST(new NamedArgumentListExpression(mapEntryExpressionList), ctx)), ctx); } if (asBoolean(mapEntryExpressionList) && asBoolean(expressionList)) { // e.g. arguments like x: 1, 'a', y: 2, 'b', z: 3 ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList); argumentListExpression.getExpressions().add(0, configureAST(new MapExpression(mapEntryExpressionList), ctx)); return configureAST(argumentListExpression, ctx); } throw createParsingFailedException("Unsupported argument list: " + ctx.getText(), ctx); } private void validateDuplicatedNamedParameter(List<MapEntryExpression> mapEntryExpressionList, MapEntryExpression mapEntryExpression) { Expression keyExpression = mapEntryExpression.getKeyExpression(); if (null == keyExpression) { return; } if (isInsideParentheses(keyExpression)) { return; } String parameterName = keyExpression.getText(); boolean isDuplicatedNamedParameter = mapEntryExpressionList.stream() .anyMatch(m -> m.getKeyExpression().getText().equals(parameterName)); if (!isDuplicatedNamedParameter) { return; } throw createParsingFailedException("Duplicated named parameter '" + parameterName + "' found", mapEntryExpression); } @Override public Expression visitEnhancedArgumentListElement(EnhancedArgumentListElementContext ctx) { if (asBoolean(ctx.expressionListElement())) { return configureAST(this.visitExpressionListElement(ctx.expressionListElement()), ctx); } if (asBoolean(ctx.standardLambdaExpression())) { return configureAST(this.visitStandardLambdaExpression(ctx.standardLambdaExpression()), ctx); } if (asBoolean(ctx.mapEntry())) { return configureAST(this.visitMapEntry(ctx.mapEntry()), ctx); } throw createParsingFailedException("Unsupported enhanced argument list element: " + ctx.getText(), ctx); } @Override public ConstantExpression visitStringLiteral(StringLiteralContext ctx) { String text = parseStringLiteral(ctx.StringLiteral().getText()); ConstantExpression constantExpression = new ConstantExpression(text, true); constantExpression.putNodeMetaData(IS_STRING, true); return configureAST(constantExpression, ctx); } private String parseStringLiteral(String text) { int slashyType = getSlashyType(text); boolean startsWithSlash = false; if (text.startsWith(TSQ_STR) || text.startsWith(TDQ_STR)) { text = StringUtils.removeCR(text); // remove CR in the multiline string text = StringUtils.trimQuotations(text, 3); } else if (text.startsWith(SQ_STR) || text.startsWith(DQ_STR) || (startsWithSlash = text.startsWith(SLASH_STR))) { if (startsWithSlash) { // the slashy string can span rows, so we have to remove CR for it text = StringUtils.removeCR(text); // remove CR in the multiline string } text = StringUtils.trimQuotations(text, 1); } else if (text.startsWith(DOLLAR_SLASH_STR)) { text = StringUtils.removeCR(text); text = StringUtils.trimQuotations(text, 2); } //handle escapes. return StringUtils.replaceEscapes(text, slashyType); } private int getSlashyType(String text) { return text.startsWith(SLASH_STR) ? StringUtils.SLASHY : text.startsWith(DOLLAR_SLASH_STR) ? StringUtils.DOLLAR_SLASHY : StringUtils.NONE_SLASHY; } @Override public Tuple2<Token, Expression> visitIndexPropertyArgs(IndexPropertyArgsContext ctx) { List<Expression> expressionList = this.visitExpressionList(ctx.expressionList()); if (expressionList.size() == 1) { Expression expr = expressionList.get(0); Expression indexExpr; if (expr instanceof SpreadExpression) { // e.g. a[*[1, 2]] ListExpression listExpression = new ListExpression(expressionList); listExpression.setWrapped(false); indexExpr = listExpression; } else { // e.g. a[1] indexExpr = expr; } return tuple(ctx.LBRACK().getSymbol(), indexExpr); } // e.g. a[1, 2] ListExpression listExpression = new ListExpression(expressionList); listExpression.setWrapped(true); return tuple(ctx.LBRACK().getSymbol(), configureAST(listExpression, ctx)); } @Override public List<MapEntryExpression> visitNamedPropertyArgs(NamedPropertyArgsContext ctx) { return this.visitMapEntryList(ctx.mapEntryList()); } @Override public Expression visitNamePart(NamePartContext ctx) { if (asBoolean(ctx.identifier())) { return configureAST(new ConstantExpression(this.visitIdentifier(ctx.identifier())), ctx); } else if (asBoolean(ctx.stringLiteral())) { return configureAST(this.visitStringLiteral(ctx.stringLiteral()), ctx); } else if (asBoolean(ctx.dynamicMemberName())) { return configureAST(this.visitDynamicMemberName(ctx.dynamicMemberName()), ctx); } else if (asBoolean(ctx.keywords())) { return configureAST(new ConstantExpression(ctx.keywords().getText()), ctx); } throw createParsingFailedException("Unsupported name part: " + ctx.getText(), ctx); } @Override public Expression visitDynamicMemberName(DynamicMemberNameContext ctx) { if (asBoolean(ctx.parExpression())) { return configureAST(this.visitParExpression(ctx.parExpression()), ctx); } else if (asBoolean(ctx.gstring())) { return configureAST(this.visitGstring(ctx.gstring()), ctx); } throw createParsingFailedException("Unsupported dynamic member name: " + ctx.getText(), ctx); } @Override public Expression visitPostfixExpression(PostfixExpressionContext ctx) { Expression pathExpr = this.visitPathExpression(ctx.pathExpression()); if (asBoolean(ctx.op)) { PostfixExpression postfixExpression = new PostfixExpression(pathExpr, createGroovyToken(ctx.op)); if (visitingAssertStatementCnt > 0) { // powerassert requires different column for values, so we have to copy the location of op return configureAST(postfixExpression, ctx.op); } else { return configureAST(postfixExpression, ctx); } } return configureAST(pathExpr, ctx); } @Override public Expression visitUnaryNotExprAlt(UnaryNotExprAltContext ctx) { if (asBoolean(ctx.NOT())) { return configureAST(new NotExpression((Expression) this.visit(ctx.expression())), ctx); } if (asBoolean(ctx.BITNOT())) { return configureAST(new BitwiseNegationExpression((Expression) this.visit(ctx.expression())), ctx); } throw createParsingFailedException("Unsupported unary expression: " + ctx.getText(), ctx); } @Override public CastExpression visitCastExprAlt(CastExprAltContext ctx) { return configureAST(new CastExpression(this.visitCastParExpression(ctx.castParExpression()), (Expression) this.visit(ctx.expression())), ctx); } @Override public BinaryExpression visitPowerExprAlt(PowerExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public Expression visitUnaryAddExprAlt(UnaryAddExprAltContext ctx) { ExpressionContext expressionCtx = ctx.expression(); Expression expression = (Expression) this.visit(expressionCtx); switch (ctx.op.getType()) { case ADD: { if (isNonStringConstantOutsideParentheses(expression)) { return configureAST(expression, ctx); } return configureAST(new UnaryPlusExpression(expression), ctx); } case SUB: { if (isNonStringConstantOutsideParentheses(expression)) { ConstantExpression constantExpression = (ConstantExpression) expression; try { String integerLiteralText = constantExpression.getNodeMetaData(INTEGER_LITERAL_TEXT); if (null != integerLiteralText) { ConstantExpression result = new ConstantExpression( Numbers.parseInteger(SUB_STR + integerLiteralText)); this.numberFormatError = null; // reset the numberFormatError return configureAST(result, ctx); } String floatingPointLiteralText = constantExpression .getNodeMetaData(FLOATING_POINT_LITERAL_TEXT); if (null != floatingPointLiteralText) { ConstantExpression result = new ConstantExpression( Numbers.parseDecimal(SUB_STR + floatingPointLiteralText)); this.numberFormatError = null; // reset the numberFormatError return configureAST(result, ctx); } } catch (Exception e) { throw createParsingFailedException(e.getMessage(), ctx); } throw new GroovyBugError( "Failed to find the original number literal text: " + constantExpression.getText()); } return configureAST(new UnaryMinusExpression(expression), ctx); } case INC: case DEC: return configureAST(new PrefixExpression(this.createGroovyToken(ctx.op), expression), ctx); default: throw createParsingFailedException("Unsupported unary operation: " + ctx.getText(), ctx); } } private boolean isNonStringConstantOutsideParentheses(Expression expression) { return expression instanceof ConstantExpression && !(((ConstantExpression) expression).getValue() instanceof String) && !isInsideParentheses(expression); } @Override public BinaryExpression visitMultiplicativeExprAlt(MultiplicativeExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public BinaryExpression visitAdditiveExprAlt(AdditiveExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public Expression visitShiftExprAlt(ShiftExprAltContext ctx) { Expression left = (Expression) this.visit(ctx.left); Expression right = (Expression) this.visit(ctx.right); if (asBoolean(ctx.rangeOp)) { return configureAST(new RangeExpression(left, right, !ctx.rangeOp.getText().endsWith("<")), ctx); } org.codehaus.groovy.syntax.Token op; Token antlrToken; if (asBoolean(ctx.dlOp)) { op = this.createGroovyToken(ctx.dlOp, 2); antlrToken = ctx.dlOp; } else if (asBoolean(ctx.dgOp)) { op = this.createGroovyToken(ctx.dgOp, 2); antlrToken = ctx.dgOp; } else if (asBoolean(ctx.tgOp)) { op = this.createGroovyToken(ctx.tgOp, 3); antlrToken = ctx.tgOp; } else { throw createParsingFailedException("Unsupported shift expression: " + ctx.getText(), ctx); } BinaryExpression binaryExpression = new BinaryExpression(left, op, right); if (isTrue(ctx, IS_INSIDE_CONDITIONAL_EXPRESSION)) { return configureAST(binaryExpression, antlrToken); } return configureAST(binaryExpression, ctx); } @Override public Expression visitRelationalExprAlt(RelationalExprAltContext ctx) { switch (ctx.op.getType()) { case AS: return configureAST( CastExpression.asExpression(this.visitType(ctx.type()), (Expression) this.visit(ctx.left)), ctx); case INSTANCEOF: case NOT_INSTANCEOF: ctx.type().putNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR, true); return configureAST( new BinaryExpression((Expression) this.visit(ctx.left), this.createGroovyToken(ctx.op), configureAST(new ClassExpression(this.visitType(ctx.type())), ctx.type())), ctx); case LE: case GE: case GT: case LT: case IN: case NOT_IN: { if (ctx.op.getType() == IN || ctx.op.getType() == NOT_IN) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } return configureAST(this.createBinaryExpression(ctx.left, ctx.op, ctx.right), ctx); } default: throw createParsingFailedException("Unsupported relational expression: " + ctx.getText(), ctx); } } @Override public BinaryExpression visitEqualityExprAlt(EqualityExprAltContext ctx) { return configureAST(this.createBinaryExpression(ctx.left, ctx.op, ctx.right), ctx); } @Override public BinaryExpression visitRegexExprAlt(RegexExprAltContext ctx) { return configureAST(this.createBinaryExpression(ctx.left, ctx.op, ctx.right), ctx); } @Override public BinaryExpression visitAndExprAlt(AndExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public BinaryExpression visitExclusiveOrExprAlt(ExclusiveOrExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public BinaryExpression visitInclusiveOrExprAlt(InclusiveOrExprAltContext ctx) { return this.createBinaryExpression(ctx.left, ctx.op, ctx.right, ctx); } @Override public BinaryExpression visitLogicalAndExprAlt(LogicalAndExprAltContext ctx) { return configureAST(this.createBinaryExpression(ctx.left, ctx.op, ctx.right), ctx); } @Override public BinaryExpression visitLogicalOrExprAlt(LogicalOrExprAltContext ctx) { return configureAST(this.createBinaryExpression(ctx.left, ctx.op, ctx.right), ctx); } @Override public Expression visitConditionalExprAlt(ConditionalExprAltContext ctx) { ctx.fb.putNodeMetaData(IS_INSIDE_CONDITIONAL_EXPRESSION, true); if (asBoolean(ctx.ELVIS())) { // e.g. a == 6 ?: 0 return configureAST( new ElvisOperatorExpression((Expression) this.visit(ctx.con), (Expression) this.visit(ctx.fb)), ctx); } ctx.tb.putNodeMetaData(IS_INSIDE_CONDITIONAL_EXPRESSION, true); return configureAST(new TernaryExpression( configureAST(new BooleanExpression((Expression) this.visit(ctx.con)), ctx.con), (Expression) this.visit(ctx.tb), (Expression) this.visit(ctx.fb)), ctx); } @Override public BinaryExpression visitMultipleAssignmentExprAlt(MultipleAssignmentExprAltContext ctx) { return configureAST(new BinaryExpression(this.visitVariableNames(ctx.left), this.createGroovyToken(ctx.op), ((ExpressionStatement) this.visit(ctx.right)).getExpression()), ctx); } @Override public BinaryExpression visitAssignmentExprAlt(AssignmentExprAltContext ctx) { Expression leftExpr = (Expression) this.visit(ctx.left); if (leftExpr instanceof VariableExpression && isInsideParentheses(leftExpr)) { // it is a special multiple assignment whose variable count is only one, e.g. (a) = [1] if ((Integer) leftExpr.getNodeMetaData(INSIDE_PARENTHESES_LEVEL) > 1) { throw createParsingFailedException( "Nested parenthesis is not allowed in multiple assignment, e.g. ((a)) = b", ctx); } return configureAST(new BinaryExpression(configureAST(new TupleExpression(leftExpr), ctx.left), this.createGroovyToken(ctx.op), this.visitEnhancedStatementExpression(ctx.enhancedStatementExpression())), ctx); } // the LHS expression should be a variable which is not inside any parentheses if (!((leftExpr instanceof VariableExpression // && !(THIS_STR.equals(leftExpr.getText()) || SUPER_STR.equals(leftExpr.getText())) // commented, e.g. this = value // this will be transformed to $this && !isInsideParentheses(leftExpr)) // e.g. p = 123 || leftExpr instanceof PropertyExpression // e.g. obj.p = 123 || (leftExpr instanceof BinaryExpression // && !(((BinaryExpression) leftExpr).getRightExpression() instanceof ListExpression) // commented, e.g. list[1, 2] = [11, 12] && Types.LEFT_SQUARE_BRACKET == ((BinaryExpression) leftExpr).getOperation().getType()) // e.g. map[a] = 123 OR map['a'] = 123 OR map["$a"] = 123 ) ) { throw createParsingFailedException( "The LHS of an assignment should be a variable or a field accessing expression", ctx); } return configureAST(new BinaryExpression(leftExpr, this.createGroovyToken(ctx.op), this.visitEnhancedStatementExpression(ctx.enhancedStatementExpression())), ctx); } // } expression -------------------------------------------------------------------- // primary { -------------------------------------------------------------------- @Override public Expression visitIdentifierPrmrAlt(IdentifierPrmrAltContext ctx) { if (asBoolean(ctx.typeArguments())) { ClassNode classNode = ClassHelper.make(ctx.identifier().getText()); classNode.setGenericsTypes(this.visitTypeArguments(ctx.typeArguments())); return configureAST(new ClassExpression(classNode), ctx); } return configureAST(new VariableExpression(this.visitIdentifier(ctx.identifier())), ctx); } @Override public Expression visitNewPrmrAlt(NewPrmrAltContext ctx) { return configureAST(this.visitCreator(ctx.creator()), ctx); } @Override public VariableExpression visitThisPrmrAlt(ThisPrmrAltContext ctx) { return configureAST(new VariableExpression(ctx.THIS().getText()), ctx); } @Override public VariableExpression visitSuperPrmrAlt(SuperPrmrAltContext ctx) { return configureAST(new VariableExpression(ctx.SUPER().getText()), ctx); } // } primary -------------------------------------------------------------------- @Override public Expression visitCreator(CreatorContext ctx) { ClassNode classNode = this.visitCreatedName(ctx.createdName()); if (asBoolean(ctx.arguments())) { // create instance of class Expression arguments = this.visitArguments(ctx.arguments()); Expression enclosingInstanceExpression = ctx.getNodeMetaData(ENCLOSING_INSTANCE_EXPRESSION); if (null != enclosingInstanceExpression) { if (arguments instanceof ArgumentListExpression) { ((ArgumentListExpression) arguments).getExpressions().add(0, enclosingInstanceExpression); } else if (arguments instanceof TupleExpression) { throw createParsingFailedException( "Creating instance of non-static class does not support named parameters", arguments); } else if (arguments instanceof NamedArgumentListExpression) { throw createParsingFailedException("Unexpected arguments", arguments); } else { throw createParsingFailedException("Unsupported arguments", arguments); // should never reach here } } if (asBoolean(ctx.anonymousInnerClassDeclaration())) { ctx.anonymousInnerClassDeclaration().putNodeMetaData(ANONYMOUS_INNER_CLASS_SUPER_CLASS, classNode); InnerClassNode anonymousInnerClassNode = this .visitAnonymousInnerClassDeclaration(ctx.anonymousInnerClassDeclaration()); List<InnerClassNode> anonymousInnerClassList = anonymousInnerClassesDefinedInMethodStack.peek(); if (null != anonymousInnerClassList) { // if the anonymous class is created in a script, no anonymousInnerClassList is available. anonymousInnerClassList.add(anonymousInnerClassNode); } ConstructorCallExpression constructorCallExpression = new ConstructorCallExpression( anonymousInnerClassNode, arguments); constructorCallExpression.setUsingAnonymousInnerClass(true); return configureAST(constructorCallExpression, ctx); } return configureAST(new ConstructorCallExpression(classNode, arguments), ctx); } if (asBoolean(ctx.LBRACK()) || asBoolean(ctx.dims())) { // create array ArrayExpression arrayExpression; List<List<AnnotationNode>> allDimList; if (asBoolean(ctx.arrayInitializer())) { ClassNode elementType = classNode; allDimList = this.visitDims(ctx.dims()); for (int i = 0, n = allDimList.size() - 1; i < n; i += 1) { elementType = this.createArrayType(elementType); } arrayExpression = new ArrayExpression(elementType, this.visitArrayInitializer(ctx.arrayInitializer())); } else { Expression[] empties; List<List<AnnotationNode>> emptyDimList = this.visitDimsOpt(ctx.dimsOpt()); if (asBoolean(emptyDimList)) { empties = new Expression[emptyDimList.size()]; Arrays.fill(empties, ConstantExpression.EMPTY_EXPRESSION); } else { empties = Expression.EMPTY_ARRAY; } arrayExpression = new ArrayExpression(classNode, null, Stream.concat(ctx.expression().stream().map(e -> (Expression) this.visit(e)), Arrays.stream(empties)).collect(Collectors.toList())); List<List<AnnotationNode>> exprDimList = ctx.annotationsOpt().stream() .map(this::visitAnnotationsOpt).collect(Collectors.toList()); allDimList = new ArrayList<>(exprDimList); Collections.reverse(emptyDimList); allDimList.addAll(emptyDimList); Collections.reverse(allDimList); } arrayExpression.setType(this.createArrayType(classNode, allDimList)); return configureAST(arrayExpression, ctx); } throw createParsingFailedException("Unsupported creator: " + ctx.getText(), ctx); } private static String nextAnonymousClassName(ClassNode outerClass) { int anonymousClassCount = 0; for (Iterator<InnerClassNode> it = outerClass.getInnerClasses(); it.hasNext();) { InnerClassNode innerClass = it.next(); if (innerClass.isAnonymous()) { anonymousClassCount += 1; } } return outerClass.getName() + "$" + (anonymousClassCount + 1); } @Override public InnerClassNode visitAnonymousInnerClassDeclaration(AnonymousInnerClassDeclarationContext ctx) { ClassNode superClass = Objects.requireNonNull(ctx.getNodeMetaData(ANONYMOUS_INNER_CLASS_SUPER_CLASS), "superClass should not be null"); ClassNode outerClass = Optional.ofNullable(classNodeStack.peek()).orElse(moduleNode.getScriptClassDummy()); String innerClassName = nextAnonymousClassName(outerClass); InnerClassNode anonymousInnerClass; if (1 == ctx.t) { // anonymous enum anonymousInnerClass = new EnumConstantClassNode(outerClass, innerClassName, superClass.getModifiers() | Opcodes.ACC_FINAL, superClass.getPlainNodeReference()); // and remove the final modifier from classNode to allow the sub class superClass.setModifiers(superClass.getModifiers() & ~Opcodes.ACC_FINAL); } else { // anonymous inner class anonymousInnerClass = new InnerClassNode(outerClass, innerClassName, Opcodes.ACC_PUBLIC, superClass); } anonymousInnerClass.setUsingGenerics(false); anonymousInnerClass.setAnonymous(true); anonymousInnerClass.putNodeMetaData(CLASS_NAME, innerClassName); configureAST(anonymousInnerClass, ctx); classNodeStack.push(anonymousInnerClass); ctx.classBody().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, anonymousInnerClass); this.visitClassBody(ctx.classBody()); classNodeStack.pop(); classNodeList.add(anonymousInnerClass); return anonymousInnerClass; } @Override public ClassNode visitCreatedName(CreatedNameContext ctx) { ClassNode classNode = null; if (asBoolean(ctx.qualifiedClassName())) { classNode = this.visitQualifiedClassName(ctx.qualifiedClassName()); if (asBoolean(ctx.typeArgumentsOrDiamond())) { classNode.setGenericsTypes(this.visitTypeArgumentsOrDiamond(ctx.typeArgumentsOrDiamond())); } classNode = configureAST(classNode, ctx); } else if (asBoolean(ctx.primitiveType())) { classNode = configureAST(this.visitPrimitiveType(ctx.primitiveType()), ctx); } if (!asBoolean(classNode)) { throw createParsingFailedException("Unsupported created name: " + ctx.getText(), ctx); } classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); return classNode; } @Override public MapExpression visitMap(MapContext ctx) { return configureAST(new MapExpression(this.visitMapEntryList(ctx.mapEntryList())), ctx); } @Override public List<MapEntryExpression> visitMapEntryList(MapEntryListContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } return this.createMapEntryList(ctx.mapEntry()); } private List<MapEntryExpression> createMapEntryList(List<? extends MapEntryContext> mapEntryContextList) { if (!asBoolean(mapEntryContextList)) { return Collections.emptyList(); } return mapEntryContextList.stream().map(this::visitMapEntry).collect(Collectors.toList()); } @Override public MapEntryExpression visitMapEntry(MapEntryContext ctx) { Expression keyExpr; Expression valueExpr = (Expression) this.visit(ctx.expression()); if (asBoolean(ctx.MUL())) { keyExpr = configureAST(new SpreadMapExpression(valueExpr), ctx); } else if (asBoolean(ctx.mapEntryLabel())) { keyExpr = this.visitMapEntryLabel(ctx.mapEntryLabel()); } else { throw createParsingFailedException("Unsupported map entry: " + ctx.getText(), ctx); } return configureAST(new MapEntryExpression(keyExpr, valueExpr), ctx); } @Override public Expression visitMapEntryLabel(MapEntryLabelContext ctx) { if (asBoolean(ctx.keywords())) { return configureAST(this.visitKeywords(ctx.keywords()), ctx); } else if (asBoolean(ctx.primary())) { Expression expression = (Expression) this.visit(ctx.primary()); // if the key is variable and not inside parentheses, convert it to a constant, e.g. [a:1, b:2] if (expression instanceof VariableExpression && !isInsideParentheses(expression)) { expression = configureAST(new ConstantExpression(((VariableExpression) expression).getName()), expression); } return configureAST(expression, ctx); } throw createParsingFailedException("Unsupported map entry label: " + ctx.getText(), ctx); } @Override public ConstantExpression visitKeywords(KeywordsContext ctx) { return configureAST(new ConstantExpression(ctx.getText()), ctx); } @Override public VariableExpression visitBuiltInType(BuiltInTypeContext ctx) { String text; if (asBoolean(ctx.VOID())) { text = ctx.VOID().getText(); } else if (asBoolean(ctx.BuiltInPrimitiveType())) { text = ctx.BuiltInPrimitiveType().getText(); } else { throw createParsingFailedException("Unsupported built-in type: " + ctx, ctx); } return configureAST(new VariableExpression(text), ctx); } @Override public ListExpression visitList(ListContext ctx) { if (asBoolean(ctx.COMMA()) && !asBoolean(ctx.expressionList())) { throw createParsingFailedException("Empty list constructor should not contain any comma(,)", ctx.COMMA()); } return configureAST(new ListExpression(this.visitExpressionList(ctx.expressionList())), ctx); } @Override public List<Expression> visitExpressionList(ExpressionListContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } return this.createExpressionList(ctx.expressionListElement()); } private List<Expression> createExpressionList( List<? extends ExpressionListElementContext> expressionListElementContextList) { if (!asBoolean(expressionListElementContextList)) { return Collections.emptyList(); } return expressionListElementContextList.stream().map(this::visitExpressionListElement) .collect(Collectors.toList()); } @Override public Expression visitExpressionListElement(ExpressionListElementContext ctx) { Expression expression = (Expression) this.visit(ctx.expression()); validateExpressionListElement(ctx, expression); if (asBoolean(ctx.MUL())) { return configureAST(new SpreadExpression(expression), ctx); } return configureAST(expression, ctx); } private void validateExpressionListElement(ExpressionListElementContext ctx, Expression expression) { if (!(expression instanceof MethodCallExpression && isTrue(expression, IS_COMMAND_EXPRESSION))) { return; } // statements like `foo(String a)` is invalid MethodCallExpression methodCallExpression = (MethodCallExpression) expression; String methodName = methodCallExpression.getMethodAsString(); if (methodCallExpression.isImplicitThis() && Character.isUpperCase(methodName.codePointAt(0)) || isPrimitiveType(methodName)) { throw createParsingFailedException("Invalid method declaration", ctx); } } // literal { -------------------------------------------------------------------- @Override public ConstantExpression visitIntegerLiteralAlt(IntegerLiteralAltContext ctx) { String text = ctx.IntegerLiteral().getText(); Number num = null; try { num = Numbers.parseInteger(text); } catch (Exception e) { this.numberFormatError = tuple(ctx, e); } ConstantExpression constantExpression = new ConstantExpression(num, !text.startsWith(SUB_STR)); constantExpression.putNodeMetaData(IS_NUMERIC, true); constantExpression.putNodeMetaData(INTEGER_LITERAL_TEXT, text); return configureAST(constantExpression, ctx); } @Override public ConstantExpression visitFloatingPointLiteralAlt(FloatingPointLiteralAltContext ctx) { String text = ctx.FloatingPointLiteral().getText(); Number num = null; try { num = Numbers.parseDecimal(text); } catch (Exception e) { this.numberFormatError = tuple(ctx, e); } ConstantExpression constantExpression = new ConstantExpression(num, !text.startsWith(SUB_STR)); constantExpression.putNodeMetaData(IS_NUMERIC, true); constantExpression.putNodeMetaData(FLOATING_POINT_LITERAL_TEXT, text); return configureAST(constantExpression, ctx); } @Override public ConstantExpression visitBooleanLiteralAlt(BooleanLiteralAltContext ctx) { return configureAST(new ConstantExpression("true".equals(ctx.BooleanLiteral().getText()), true), ctx); } @Override public ConstantExpression visitNullLiteralAlt(NullLiteralAltContext ctx) { return configureAST(new ConstantExpression(null), ctx); } // } literal -------------------------------------------------------------------- // gstring { -------------------------------------------------------------------- @Override public GStringExpression visitGstring(GstringContext ctx) { final List<ConstantExpression> stringLiteralList = new LinkedList<>(); final String begin = ctx.GStringBegin().getText(); final String beginQuotation = beginQuotation(begin); stringLiteralList.add( configureAST(new ConstantExpression(parseGStringBegin(ctx, beginQuotation)), ctx.GStringBegin())); List<ConstantExpression> partStrings = ctx.GStringPart().stream() .map(e -> configureAST(new ConstantExpression(parseGStringPart(e, beginQuotation)), e)) .collect(Collectors.toList()); stringLiteralList.addAll(partStrings); stringLiteralList .add(configureAST(new ConstantExpression(parseGStringEnd(ctx, beginQuotation)), ctx.GStringEnd())); List<Expression> values = ctx.gstringValue().stream().map(e -> { Expression expression = this.visitGstringValue(e); if (expression instanceof ClosureExpression && !hasArrow(e)) { List<Statement> statementList = ((BlockStatement) ((ClosureExpression) expression).getCode()) .getStatements(); if (statementList.stream().noneMatch(DefaultGroovyMethods::asBoolean)) { return configureAST(new ConstantExpression(null), e); } return configureAST( this.createCallMethodCallExpression(expression, new ArgumentListExpression(), true), e); } return expression; }).collect(Collectors.toList()); StringBuilder verbatimText = new StringBuilder(ctx.getText().length()); for (int i = 0, n = stringLiteralList.size(), s = values.size(); i < n; i++) { verbatimText.append(stringLiteralList.get(i).getValue()); if (i == s) { continue; } Expression value = values.get(i); if (!asBoolean(value)) { continue; } verbatimText.append(DOLLAR_STR); verbatimText.append(value.getText()); } return configureAST(new GStringExpression(verbatimText.toString(), stringLiteralList, values), ctx); } private boolean hasArrow(GstringValueContext e) { return asBoolean(e.closure().ARROW()); } private String parseGStringEnd(GstringContext ctx, String beginQuotation) { StringBuilder text = new StringBuilder(ctx.GStringEnd().getText()); text.insert(0, beginQuotation); return this.parseStringLiteral(text.toString()); } private String parseGStringPart(TerminalNode e, String beginQuotation) { StringBuilder text = new StringBuilder(e.getText()); text.deleteCharAt(text.length() - 1); // remove the tailing $ text.insert(0, beginQuotation).append(QUOTATION_MAP.get(beginQuotation)); return this.parseStringLiteral(text.toString()); } private String parseGStringBegin(GstringContext ctx, String beginQuotation) { StringBuilder text = new StringBuilder(ctx.GStringBegin().getText()); text.deleteCharAt(text.length() - 1); // remove the tailing $ text.append(QUOTATION_MAP.get(beginQuotation)); return this.parseStringLiteral(text.toString()); } private String beginQuotation(String text) { if (text.startsWith(TDQ_STR)) { return TDQ_STR; } else if (text.startsWith(DQ_STR)) { return DQ_STR; } else if (text.startsWith(SLASH_STR)) { return SLASH_STR; } else if (text.startsWith(DOLLAR_SLASH_STR)) { return DOLLAR_SLASH_STR; } else { return String.valueOf(text.charAt(0)); } } @Override public Expression visitGstringValue(GstringValueContext ctx) { if (asBoolean(ctx.gstringPath())) { return configureAST(this.visitGstringPath(ctx.gstringPath()), ctx); } if (asBoolean(ctx.LBRACE())) { if (asBoolean(ctx.statementExpression())) { return configureAST(((ExpressionStatement) this.visit(ctx.statementExpression())).getExpression(), ctx.statementExpression()); } else { // e.g. "${}" return configureAST(new ConstantExpression(null), ctx); } } if (asBoolean(ctx.closure())) { return configureAST(this.visitClosure(ctx.closure()), ctx); } throw createParsingFailedException("Unsupported gstring value: " + ctx.getText(), ctx); } @Override public Expression visitGstringPath(GstringPathContext ctx) { VariableExpression variableExpression = new VariableExpression(this.visitIdentifier(ctx.identifier())); if (asBoolean(ctx.GStringPathPart())) { Expression propertyExpression = ctx.GStringPathPart().stream() .map(e -> configureAST((Expression) new ConstantExpression(e.getText().substring(1)), e)) .reduce(configureAST(variableExpression, ctx.identifier()), (r, e) -> configureAST(new PropertyExpression(r, e), e)); return configureAST(propertyExpression, ctx); } return configureAST(variableExpression, ctx); } // } gstring -------------------------------------------------------------------- @Override public LambdaExpression visitStandardLambdaExpression(StandardLambdaExpressionContext ctx) { return configureAST(this.createLambda(ctx.standardLambdaParameters(), ctx.lambdaBody()), ctx); } private LambdaExpression createLambda(StandardLambdaParametersContext standardLambdaParametersContext, LambdaBodyContext lambdaBodyContext) { return new LambdaExpression(this.visitStandardLambdaParameters(standardLambdaParametersContext), this.visitLambdaBody(lambdaBodyContext)); } @Override public Parameter[] visitStandardLambdaParameters(final StandardLambdaParametersContext ctx) { if (asBoolean(ctx.variableDeclaratorId())) { VariableExpression variable = this.visitVariableDeclaratorId(ctx.variableDeclaratorId()); Parameter parameter = new Parameter(ClassHelper.OBJECT_TYPE, variable.getName()); configureAST(parameter, variable); return new Parameter[] { parameter }; } Parameter[] parameters = this.visitFormalParameters(ctx.formalParameters()); return (parameters.length > 0 ? parameters : null); } @Override public Statement visitLambdaBody(LambdaBodyContext ctx) { if (asBoolean(ctx.statementExpression())) { return configureAST((ExpressionStatement) this.visit(ctx.statementExpression()), ctx); } if (asBoolean(ctx.block())) { return configureAST(this.visitBlock(ctx.block()), ctx); } throw createParsingFailedException("Unsupported lambda body: " + ctx.getText(), ctx); } @Override public ClosureExpression visitClosure(ClosureContext ctx) { visitingClosureCnt++; Parameter[] parameters = asBoolean(ctx.formalParameterList()) ? this.visitFormalParameterList(ctx.formalParameterList()) : null; if (!asBoolean(ctx.ARROW())) { parameters = Parameter.EMPTY_ARRAY; } Statement code = this.visitBlockStatementsOpt(ctx.blockStatementsOpt()); ClosureExpression result = configureAST(new ClosureExpression(parameters, code), ctx); visitingClosureCnt--; return result; } @Override public Parameter[] visitFormalParameters(FormalParametersContext ctx) { if (!asBoolean(ctx)) { return Parameter.EMPTY_ARRAY; } return this.visitFormalParameterList(ctx.formalParameterList()); } @Override public Parameter[] visitFormalParameterList(FormalParameterListContext ctx) { if (!asBoolean(ctx)) { return Parameter.EMPTY_ARRAY; } List<Parameter> parameterList = new LinkedList<>(); if (asBoolean(ctx.thisFormalParameter())) { parameterList.add(this.visitThisFormalParameter(ctx.thisFormalParameter())); } List<? extends FormalParameterContext> formalParameterList = ctx.formalParameter(); if (asBoolean(formalParameterList)) { validateVarArgParameter(formalParameterList); parameterList.addAll( formalParameterList.stream().map(this::visitFormalParameter).collect(Collectors.toList())); } validateParameterList(parameterList); return parameterList.toArray(Parameter.EMPTY_ARRAY); } private void validateVarArgParameter(List<? extends FormalParameterContext> formalParameterList) { for (int i = 0, n = formalParameterList.size(); i < n - 1; i++) { FormalParameterContext formalParameterContext = formalParameterList.get(i); if (asBoolean(formalParameterContext.ELLIPSIS())) { throw createParsingFailedException("The var-arg parameter strs must be the last parameter", formalParameterContext); } } } private void validateParameterList(List<Parameter> parameterList) { for (int n = parameterList.size(), i = n - 1; i >= 0; i--) { Parameter parameter = parameterList.get(i); for (Parameter otherParameter : parameterList) { if (otherParameter == parameter) { continue; } if (otherParameter.getName().equals(parameter.getName())) { throw createParsingFailedException("Duplicated parameter '" + parameter.getName() + "' found.", parameter); } } } } @Override public Parameter visitFormalParameter(FormalParameterContext ctx) { return this.processFormalParameter(ctx, ctx.variableModifiersOpt(), ctx.type(), ctx.ELLIPSIS(), ctx.variableDeclaratorId(), ctx.expression()); } @Override public Parameter visitThisFormalParameter(ThisFormalParameterContext ctx) { return configureAST(new Parameter(this.visitType(ctx.type()), THIS_STR), ctx); } @Override public List<ModifierNode> visitClassOrInterfaceModifiersOpt(ClassOrInterfaceModifiersOptContext ctx) { if (asBoolean(ctx.classOrInterfaceModifiers())) { return this.visitClassOrInterfaceModifiers(ctx.classOrInterfaceModifiers()); } return Collections.emptyList(); } @Override public List<ModifierNode> visitClassOrInterfaceModifiers(ClassOrInterfaceModifiersContext ctx) { return ctx.classOrInterfaceModifier().stream().map(this::visitClassOrInterfaceModifier) .collect(Collectors.toList()); } @Override public ModifierNode visitClassOrInterfaceModifier(ClassOrInterfaceModifierContext ctx) { if (asBoolean(ctx.annotation())) { return configureAST(new ModifierNode(this.visitAnnotation(ctx.annotation()), ctx.getText()), ctx); } if (asBoolean(ctx.m)) { return configureAST(new ModifierNode(ctx.m.getType(), ctx.getText()), ctx); } throw createParsingFailedException("Unsupported class or interface modifier: " + ctx.getText(), ctx); } @Override public ModifierNode visitModifier(ModifierContext ctx) { if (asBoolean(ctx.classOrInterfaceModifier())) { return configureAST(this.visitClassOrInterfaceModifier(ctx.classOrInterfaceModifier()), ctx); } if (asBoolean(ctx.m)) { return configureAST(new ModifierNode(ctx.m.getType(), ctx.getText()), ctx); } throw createParsingFailedException("Unsupported modifier: " + ctx.getText(), ctx); } @Override public List<ModifierNode> visitModifiers(ModifiersContext ctx) { return ctx.modifier().stream().map(this::visitModifier).collect(Collectors.toList()); } @Override public List<ModifierNode> visitModifiersOpt(ModifiersOptContext ctx) { if (asBoolean(ctx.modifiers())) { return this.visitModifiers(ctx.modifiers()); } return Collections.emptyList(); } @Override public ModifierNode visitVariableModifier(VariableModifierContext ctx) { if (asBoolean(ctx.annotation())) { return configureAST(new ModifierNode(this.visitAnnotation(ctx.annotation()), ctx.getText()), ctx); } if (asBoolean(ctx.m)) { return configureAST(new ModifierNode(ctx.m.getType(), ctx.getText()), ctx); } throw createParsingFailedException("Unsupported variable modifier", ctx); } @Override public List<ModifierNode> visitVariableModifiersOpt(VariableModifiersOptContext ctx) { if (asBoolean(ctx.variableModifiers())) { return this.visitVariableModifiers(ctx.variableModifiers()); } return Collections.emptyList(); } @Override public List<ModifierNode> visitVariableModifiers(VariableModifiersContext ctx) { return ctx.variableModifier().stream().map(this::visitVariableModifier).collect(Collectors.toList()); } @Override public List<List<AnnotationNode>> visitDims(DimsContext ctx) { List<List<AnnotationNode>> dimList = ctx.annotationsOpt().stream().map(this::visitAnnotationsOpt) .collect(Collectors.toList()); Collections.reverse(dimList); return dimList; } @Override public List<List<AnnotationNode>> visitDimsOpt(DimsOptContext ctx) { if (!asBoolean(ctx.dims())) { return Collections.emptyList(); } return this.visitDims(ctx.dims()); } // type { -------------------------------------------------------------------- @Override public ClassNode visitType(TypeContext ctx) { if (!asBoolean(ctx)) { return ClassHelper.OBJECT_TYPE; } ClassNode classNode = null; if (asBoolean(ctx.classOrInterfaceType())) { ctx.classOrInterfaceType().putNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR, ctx.getNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR)); classNode = this.visitClassOrInterfaceType(ctx.classOrInterfaceType()); } else if (asBoolean(ctx.primitiveType())) { classNode = this.visitPrimitiveType(ctx.primitiveType()); } if (!asBoolean(classNode)) { if (VOID_STR.equals(ctx.getText())) { // TODO refine error message for `void` throw createParsingFailedException("void is not allowed here", ctx); } throw createParsingFailedException("Unsupported type: " + ctx.getText(), ctx); } classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); List<List<AnnotationNode>> dimList = this.visitDimsOpt(ctx.dimsOpt()); if (asBoolean(dimList)) { // clear array's generics type info. Groovy's bug? array's generics type will be ignored. e.g. List<String>[]... p classNode.setGenericsTypes(null); classNode.setUsingGenerics(false); classNode = this.createArrayType(classNode, dimList); } return configureAST(classNode, ctx); } @Override public ClassNode visitClassOrInterfaceType(ClassOrInterfaceTypeContext ctx) { ClassNode classNode; if (asBoolean(ctx.qualifiedClassName())) { ctx.qualifiedClassName().putNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR, ctx.getNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR)); classNode = this.visitQualifiedClassName(ctx.qualifiedClassName()); } else { ctx.qualifiedStandardClassName().putNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR, ctx.getNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR)); classNode = this.visitQualifiedStandardClassName(ctx.qualifiedStandardClassName()); } if (asBoolean(ctx.typeArguments())) { classNode.setGenericsTypes(this.visitTypeArguments(ctx.typeArguments())); } return configureAST(classNode, ctx); } @Override public GenericsType[] visitTypeArgumentsOrDiamond(TypeArgumentsOrDiamondContext ctx) { if (asBoolean(ctx.typeArguments())) { return this.visitTypeArguments(ctx.typeArguments()); } if (asBoolean(ctx.LT())) { // e.g. <> return GenericsType.EMPTY_ARRAY; } throw createParsingFailedException("Unsupported type arguments or diamond: " + ctx.getText(), ctx); } @Override public GenericsType[] visitTypeArguments(TypeArgumentsContext ctx) { return ctx.typeArgument().stream().map(this::visitTypeArgument).toArray(GenericsType[]::new); } @Override public GenericsType visitTypeArgument(TypeArgumentContext ctx) { if (asBoolean(ctx.QUESTION())) { ClassNode baseType = configureAST(ClassHelper.makeWithoutCaching(QUESTION_STR), ctx.QUESTION()); baseType.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); if (!asBoolean(ctx.type())) { GenericsType genericsType = new GenericsType(baseType); genericsType.setWildcard(true); genericsType.setName(QUESTION_STR); return configureAST(genericsType, ctx); } ClassNode[] upperBounds = null; ClassNode lowerBound = null; ClassNode classNode = this.visitType(ctx.type()); if (asBoolean(ctx.EXTENDS())) { upperBounds = new ClassNode[] { classNode }; } else if (asBoolean(ctx.SUPER())) { lowerBound = classNode; } GenericsType genericsType = new GenericsType(baseType, upperBounds, lowerBound); genericsType.setWildcard(true); return configureAST(genericsType, ctx); } else if (asBoolean(ctx.type())) { return configureAST(this.createGenericsType(this.visitType(ctx.type())), ctx); } throw createParsingFailedException("Unsupported type argument: " + ctx.getText(), ctx); } @Override public ClassNode visitPrimitiveType(PrimitiveTypeContext ctx) { return configureAST(ClassHelper.make(ctx.getText()), ctx); } // } type -------------------------------------------------------------------- @Override public VariableExpression visitVariableDeclaratorId(VariableDeclaratorIdContext ctx) { return configureAST(new VariableExpression(this.visitIdentifier(ctx.identifier())), ctx); } @Override public TupleExpression visitVariableNames(VariableNamesContext ctx) { return configureAST(new TupleExpression(ctx.variableDeclaratorId().stream() .map(this::visitVariableDeclaratorId).collect(Collectors.toList())), ctx); } @Override public ClosureExpression visitClosureOrLambdaExpression(ClosureOrLambdaExpressionContext ctx) { // GROOVY-8991: Difference in behaviour with closure and lambda if (asBoolean(ctx.closure())) { return configureAST(this.visitClosure(ctx.closure()), ctx); } else if (asBoolean(ctx.standardLambdaExpression())) { return configureAST(this.visitStandardLambdaExpression(ctx.standardLambdaExpression()), ctx); } // should never reach here throw createParsingFailedException("The node is not expected here" + ctx.getText(), ctx); } @Override public BlockStatement visitBlockStatementsOpt(BlockStatementsOptContext ctx) { if (asBoolean(ctx.blockStatements())) { return configureAST(this.visitBlockStatements(ctx.blockStatements()), ctx); } return configureAST(this.createBlockStatement(), ctx); } @Override public BlockStatement visitBlockStatements(BlockStatementsContext ctx) { return configureAST(this.createBlockStatement(ctx.blockStatement().stream().map(this::visitBlockStatement) .filter(DefaultGroovyMethods::asBoolean).collect(Collectors.toList())), ctx); } @Override public Statement visitBlockStatement(BlockStatementContext ctx) { if (asBoolean(ctx.localVariableDeclaration())) { return configureAST(this.visitLocalVariableDeclaration(ctx.localVariableDeclaration()), ctx); } if (asBoolean(ctx.statement())) { Object astNode = this.visit(ctx.statement()); //this.configureAST((Statement) this.visit(ctx.statement()), ctx); if (null == astNode) { return null; } if (astNode instanceof Statement) { return (Statement) astNode; } else if (astNode instanceof MethodNode) { throw createParsingFailedException("Method definition not expected here", ctx); } else if (astNode instanceof ImportNode) { throw createParsingFailedException("Import statement not expected here", ctx); } else { throw createParsingFailedException("The statement(" + astNode.getClass() + ") not expected here", ctx); } } throw createParsingFailedException("Unsupported block statement: " + ctx.getText(), ctx); } @Override public List<AnnotationNode> visitAnnotationsOpt(AnnotationsOptContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } return ctx.annotation().stream().map(this::visitAnnotation).collect(Collectors.toList()); } @Override public AnnotationNode visitAnnotation(AnnotationContext ctx) { String annotationName = this.visitAnnotationName(ctx.annotationName()); AnnotationNode annotationNode = new AnnotationNode(ClassHelper.make(annotationName)); List<Tuple2<String, Expression>> annotationElementValues = this.visitElementValues(ctx.elementValues()); annotationElementValues.forEach(e -> annotationNode.addMember(e.getV1(), e.getV2())); return configureAST(annotationNode, ctx); } @Override public List<Tuple2<String, Expression>> visitElementValues(ElementValuesContext ctx) { if (!asBoolean(ctx)) { return Collections.emptyList(); } List<Tuple2<String, Expression>> annotationElementValues = new LinkedList<>(); if (asBoolean(ctx.elementValuePairs())) { this.visitElementValuePairs(ctx.elementValuePairs()) .forEach((key, value) -> annotationElementValues.add(tuple(key, value))); } else if (asBoolean(ctx.elementValue())) { annotationElementValues.add(tuple(VALUE_STR, this.visitElementValue(ctx.elementValue()))); } return annotationElementValues; } @Override public String visitAnnotationName(AnnotationNameContext ctx) { return this.visitQualifiedClassName(ctx.qualifiedClassName()).getName(); } @Override public Map<String, Expression> visitElementValuePairs(ElementValuePairsContext ctx) { return ctx.elementValuePair().stream().map(this::visitElementValuePair) .collect(Collectors.toMap(Tuple2::getV1, Tuple2::getV2, (k, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", k)); }, LinkedHashMap::new)); } @Override public Tuple2<String, Expression> visitElementValuePair(ElementValuePairContext ctx) { return tuple(ctx.elementValuePairName().getText(), this.visitElementValue(ctx.elementValue())); } @Override public Expression visitElementValue(ElementValueContext ctx) { if (asBoolean(ctx.expression())) { return configureAST((Expression) this.visit(ctx.expression()), ctx); } if (asBoolean(ctx.annotation())) { return configureAST(new AnnotationConstantExpression(this.visitAnnotation(ctx.annotation())), ctx); } if (asBoolean(ctx.elementValueArrayInitializer())) { return configureAST(this.visitElementValueArrayInitializer(ctx.elementValueArrayInitializer()), ctx); } throw createParsingFailedException("Unsupported element value: " + ctx.getText(), ctx); } @Override public ListExpression visitElementValueArrayInitializer(ElementValueArrayInitializerContext ctx) { return configureAST( new ListExpression( ctx.elementValue().stream().map(this::visitElementValue).collect(Collectors.toList())), ctx); } @Override public String visitClassName(ClassNameContext ctx) { return ctx.getText(); } @Override public String visitIdentifier(IdentifierContext ctx) { return ctx.getText(); } @Override public String visitQualifiedName(QualifiedNameContext ctx) { return ctx.qualifiedNameElement().stream().map(ParseTree::getText).collect(Collectors.joining(DOT_STR)); } @Override public ClassNode visitAnnotatedQualifiedClassName(AnnotatedQualifiedClassNameContext ctx) { ClassNode classNode = this.visitQualifiedClassName(ctx.qualifiedClassName()); classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt())); return classNode; } @Override public ClassNode[] visitQualifiedClassNameList(QualifiedClassNameListContext ctx) { if (!asBoolean(ctx)) { return ClassNode.EMPTY_ARRAY; } return ctx.annotatedQualifiedClassName().stream().map(this::visitAnnotatedQualifiedClassName) .toArray(ClassNode[]::new); } @Override public ClassNode visitQualifiedClassName(QualifiedClassNameContext ctx) { return this.createClassNode(ctx); } @Override public ClassNode visitQualifiedStandardClassName(QualifiedStandardClassNameContext ctx) { return this.createClassNode(ctx); } private ClassNode createArrayType(ClassNode elementType, List<List<AnnotationNode>> dimsList) { ClassNode arrayType = elementType; for (int i = 0, n = dimsList.size(); i < n; i += 1) { arrayType = this.createArrayType(arrayType); arrayType.addAnnotations(dimsList.get(i)); } return arrayType; } private ClassNode createArrayType(ClassNode elementType) { if (ClassHelper.VOID_TYPE.equals(elementType)) { throw this.createParsingFailedException("void[] is an invalid type", elementType); } return elementType.makeArray(); } private ClassNode createClassNode(GroovyParserRuleContext ctx) { ClassNode result = ClassHelper.make(ctx.getText()); if (!isTrue(ctx, IS_INSIDE_INSTANCEOF_EXPR)) { // type in the "instanceof" expression should not have proxy to redirect to it result = this.proxyClassNode(result); } return configureAST(result, ctx); } private ClassNode proxyClassNode(ClassNode classNode) { if (!classNode.isUsingGenerics()) { return classNode; } ClassNode cn = ClassHelper.makeWithoutCaching(classNode.getName()); cn.setRedirect(classNode); return cn; } /** * Visit tree safely, no NPE occurred when the tree is null. * * @param tree an AST node * @return the visiting result */ @Override public Object visit(ParseTree tree) { if (!asBoolean(tree)) { return null; } return super.visit(tree); } // e.g. obj.a(1, 2) or obj.a 1, 2 private MethodCallExpression createMethodCallExpression(PropertyExpression propertyExpression, Expression arguments) { MethodCallExpression methodCallExpression = new MethodCallExpression( propertyExpression.getObjectExpression(), propertyExpression.getProperty(), arguments); methodCallExpression.setImplicitThis(false); methodCallExpression.setSafe(propertyExpression.isSafe()); methodCallExpression.setSpreadSafe(propertyExpression.isSpreadSafe()); // method call obj*.m(): "safe"(false) and "spreadSafe"(true) // property access obj*.p: "safe"(true) and "spreadSafe"(true) // so we have to reset safe here. if (propertyExpression.isSpreadSafe()) { methodCallExpression.setSafe(false); } // if the generics types meta data is not empty, it is a generic method call, e.g. obj.<Integer>a(1, 2) methodCallExpression .setGenericsTypes(propertyExpression.getNodeMetaData(PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES)); return methodCallExpression; } // e.g. m(1, 2) or m 1, 2 private MethodCallExpression createMethodCallExpression(Expression baseExpr, Expression arguments) { return new MethodCallExpression(VariableExpression.THIS_EXPRESSION, (baseExpr instanceof VariableExpression) ? this.createConstantExpression(baseExpr) : baseExpr, arguments); } private Parameter processFormalParameter(GroovyParserRuleContext ctx, VariableModifiersOptContext variableModifiersOptContext, TypeContext typeContext, TerminalNode ellipsis, VariableDeclaratorIdContext variableDeclaratorIdContext, ExpressionContext expressionContext) { ClassNode classNode = this.visitType(typeContext); if (asBoolean(ellipsis)) { classNode = this.createArrayType(classNode); if (!asBoolean(typeContext)) { configureAST(classNode, ellipsis); } else { configureAST(classNode, typeContext, configureAST(new ConstantExpression("..."), ellipsis)); } } Parameter parameter = new ModifierManager(this, this.visitVariableModifiersOpt(variableModifiersOptContext)) .processParameter(configureAST(new Parameter(classNode, this.visitVariableDeclaratorId(variableDeclaratorIdContext).getName()), ctx)); if (asBoolean(expressionContext)) { parameter.setInitialExpression((Expression) this.visit(expressionContext)); } return parameter; } private Expression createPathExpression(Expression primaryExpr, List<? extends PathElementContext> pathElementContextList) { return (Expression) pathElementContextList.stream().map(e -> (Object) e).reduce(primaryExpr, (r, e) -> { PathElementContext pathElementContext = (PathElementContext) e; pathElementContext.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR, r); Expression expression = this.visitPathElement(pathElementContext); boolean isSafeChain = isTrue((Expression) r, PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN); if (isSafeChain) { expression.putNodeMetaData(PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN, true); } return expression; }); } private GenericsType createGenericsType(ClassNode classNode) { return configureAST(new GenericsType(classNode), classNode); } private ConstantExpression createConstantExpression(Expression expression) { if (expression instanceof ConstantExpression) { return (ConstantExpression) expression; } return configureAST(new ConstantExpression(expression.getText()), expression); } private BinaryExpression createBinaryExpression(ExpressionContext left, Token op, ExpressionContext right) { return new BinaryExpression((Expression) this.visit(left), this.createGroovyToken(op), (Expression) this.visit(right)); } private BinaryExpression createBinaryExpression(ExpressionContext left, Token op, ExpressionContext right, ExpressionContext ctx) { BinaryExpression binaryExpression = this.createBinaryExpression(left, op, right); if (isTrue(ctx, IS_INSIDE_CONDITIONAL_EXPRESSION)) { return configureAST(binaryExpression, op); } return configureAST(binaryExpression, ctx); } private Statement unpackStatement(Statement statement) { if (statement instanceof DeclarationListStatement) { List<ExpressionStatement> expressionStatementList = ((DeclarationListStatement) statement) .getDeclarationStatements(); if (1 == expressionStatementList.size()) { return expressionStatementList.get(0); } return configureAST(this.createBlockStatement(statement), statement); // if DeclarationListStatement contains more than 1 declarations, maybe it's better to create a block to hold them } return statement; } BlockStatement createBlockStatement(Statement... statements) { return this.createBlockStatement(Arrays.asList(statements)); } private BlockStatement createBlockStatement(List<Statement> statementList) { return this.appendStatementsToBlockStatement(new BlockStatement(), statementList); } public BlockStatement appendStatementsToBlockStatement(BlockStatement bs, Statement... statements) { return this.appendStatementsToBlockStatement(bs, Arrays.asList(statements)); } private BlockStatement appendStatementsToBlockStatement(BlockStatement bs, List<Statement> statementList) { return (BlockStatement) statementList.stream().reduce(bs, (r, e) -> { BlockStatement blockStatement = (BlockStatement) r; if (e instanceof DeclarationListStatement) { ((DeclarationListStatement) e).getDeclarationStatements().forEach(blockStatement::addStatement); } else { blockStatement.addStatement(e); } return blockStatement; }); } private boolean isAnnotationDeclaration(ClassNode classNode) { return asBoolean(classNode) && classNode.isAnnotationDefinition(); } private boolean isSyntheticPublic(boolean isAnnotationDeclaration, boolean isAnonymousInnerEnumDeclaration, boolean hasReturnType, ModifierManager modifierManager) { if (modifierManager.containsVisibilityModifier()) { return false; } if (isAnnotationDeclaration) { return true; } if (hasReturnType && (modifierManager.containsAny(DEF, VAR))) { return true; } if (!hasReturnType || modifierManager.containsNonVisibilityModifier() || modifierManager.containsAnnotations()) { return true; } return isAnonymousInnerEnumDeclaration; } // the mixins of interface and annotation should be null private void hackMixins(ClassNode classNode) { classNode.setMixins(null); } private static final Map<ClassNode, Object> TYPE_DEFAULT_VALUE_MAP = Maps.of(ClassHelper.int_TYPE, 0, ClassHelper.long_TYPE, 0L, ClassHelper.double_TYPE, 0.0D, ClassHelper.float_TYPE, 0.0F, ClassHelper.short_TYPE, (short) 0, ClassHelper.byte_TYPE, (byte) 0, ClassHelper.char_TYPE, (char) 0, ClassHelper.boolean_TYPE, Boolean.FALSE); private Object findDefaultValueByType(ClassNode type) { return TYPE_DEFAULT_VALUE_MAP.get(type); } private boolean isPackageInfoDeclaration() { String name = this.sourceUnit.getName(); return null != name && name.endsWith(PACKAGE_INFO_FILE_NAME); } private boolean isBlankScript() { return moduleNode.getStatementBlock().isEmpty() && moduleNode.getMethods().isEmpty() && moduleNode.getClasses().isEmpty(); } private boolean isInsideParentheses(NodeMetaDataHandler nodeMetaDataHandler) { Integer insideParenLevel = nodeMetaDataHandler.getNodeMetaData(INSIDE_PARENTHESES_LEVEL); return null != insideParenLevel && insideParenLevel > 0; } private void addEmptyReturnStatement() { moduleNode.addStatement(ReturnStatement.RETURN_NULL_OR_VOID); } private void addPackageInfoClassNode() { List<ClassNode> classNodeList = moduleNode.getClasses(); ClassNode packageInfoClassNode = ClassHelper.make(moduleNode.getPackageName() + PACKAGE_INFO); if (!classNodeList.contains(packageInfoClassNode)) { moduleNode.addClass(packageInfoClassNode); } } private org.codehaus.groovy.syntax.Token createGroovyTokenByType(Token token, int type) { if (null == token) { throw new IllegalArgumentException("token should not be null"); } return new org.codehaus.groovy.syntax.Token(type, token.getText(), token.getLine(), token.getCharPositionInLine()); } private org.codehaus.groovy.syntax.Token createGroovyToken(Token token) { return this.createGroovyToken(token, 1); } private org.codehaus.groovy.syntax.Token createGroovyToken(Token token, int cardinality) { String text = StringGroovyMethods.multiply((CharSequence) token.getText(), cardinality); return new org.codehaus.groovy.syntax.Token( "..<".equals(token.getText()) || "..".equals(token.getText()) ? Types.RANGE_OPERATOR : Types.lookup(text, Types.ANY), text, token.getLine(), token.getCharPositionInLine() + 1); } /** * set the script source position */ private void configureScriptClassNode() { ClassNode scriptClassNode = moduleNode.getScriptClassDummy(); if (!asBoolean(scriptClassNode)) { return; } List<Statement> statements = moduleNode.getStatementBlock().getStatements(); if (!statements.isEmpty()) { Statement firstStatement = statements.get(0); Statement lastStatement = statements.get(statements.size() - 1); scriptClassNode.setSourcePosition(firstStatement); scriptClassNode.setLastColumnNumber(lastStatement.getLastColumnNumber()); scriptClassNode.setLastLineNumber(lastStatement.getLastLineNumber()); } } private String getOriginalText(ParserRuleContext context) { CharStream charStream = lexer.getInputStream(); return charStream .getText(Interval.of(context.getStart().getStartIndex(), context.getStop().getStopIndex())); } private boolean isTrue(NodeMetaDataHandler nodeMetaDataHandler, String key) { Object nmd = nodeMetaDataHandler.getNodeMetaData(key); if (null == nmd) { return false; } if (!(nmd instanceof Boolean)) { throw new GroovyBugError( nodeMetaDataHandler + " node meta data[" + key + "] is not an instance of Boolean"); } return (Boolean) nmd; } private CompilationFailedException createParsingFailedException(String msg, GroovyParserRuleContext ctx) { return createParsingFailedException( new SyntaxException(msg, ctx.start.getLine(), ctx.start.getCharPositionInLine() + 1, ctx.stop.getLine(), ctx.stop.getCharPositionInLine() + 1 + ctx.stop.getText().length())); } CompilationFailedException createParsingFailedException(String msg, ASTNode node) { Objects.requireNonNull(node, "node passed into createParsingFailedException should not be null"); return createParsingFailedException(new SyntaxException(msg, node.getLineNumber(), node.getColumnNumber(), node.getLastLineNumber(), node.getLastColumnNumber())); } private CompilationFailedException createParsingFailedException(String msg, TerminalNode node) { return createParsingFailedException(msg, node.getSymbol()); } private CompilationFailedException createParsingFailedException(String msg, Token token) { return createParsingFailedException( new SyntaxException(msg, token.getLine(), token.getCharPositionInLine() + 1, token.getLine(), token.getCharPositionInLine() + 1 + token.getText().length())); } private CompilationFailedException createParsingFailedException(Throwable t) { if (t instanceof SyntaxException) { this.collectSyntaxError((SyntaxException) t); } else if (t instanceof GroovySyntaxError) { GroovySyntaxError groovySyntaxError = (GroovySyntaxError) t; this.collectSyntaxError(new SyntaxException(groovySyntaxError.getMessage(), groovySyntaxError, groovySyntaxError.getLine(), groovySyntaxError.getColumn())); } else if (t instanceof Exception) { this.collectException((Exception) t); } return new CompilationFailedException(CompilePhase.PARSING.getPhaseNumber(), this.sourceUnit, t); } private void collectSyntaxError(SyntaxException e) { sourceUnit.getErrorCollector().addFatalError(new SyntaxErrorMessage(e, sourceUnit)); } private void collectException(Exception e) { sourceUnit.getErrorCollector().addException(e, this.sourceUnit); } private ANTLRErrorListener createANTLRErrorListener() { return new ANTLRErrorListener() { @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { collectSyntaxError(new SyntaxException(msg, line, charPositionInLine + 1)); } }; } private void removeErrorListeners() { lexer.removeErrorListeners(); parser.removeErrorListeners(); } private void addErrorListeners() { lexer.removeErrorListeners(); lexer.addErrorListener(this.createANTLRErrorListener()); parser.removeErrorListeners(); parser.addErrorListener(this.createANTLRErrorListener()); } private static class DeclarationListStatement extends Statement { private final List<ExpressionStatement> declarationStatements; public DeclarationListStatement(DeclarationExpression... declarations) { this(Arrays.asList(declarations)); } public DeclarationListStatement(List<DeclarationExpression> declarations) { this.declarationStatements = declarations.stream().map(e -> configureAST(new ExpressionStatement(e), e)) .collect(Collectors.toList()); } public List<ExpressionStatement> getDeclarationStatements() { List<String> declarationListStatementLabels = this.getStatementLabels(); this.declarationStatements.forEach(e -> { if (null != declarationListStatementLabels) { // clear existing statement labels before setting labels if (null != e.getStatementLabels()) { e.getStatementLabels().clear(); } declarationListStatementLabels.forEach(e::addStatementLabel); } }); return this.declarationStatements; } public List<DeclarationExpression> getDeclarationExpressions() { return this.declarationStatements.stream().map(e -> (DeclarationExpression) e.getExpression()) .collect(Collectors.toList()); } } private final ModuleNode moduleNode; private final SourceUnit sourceUnit; private final GroovyLangLexer lexer; private final GroovyLangParser parser; private final TryWithResourcesASTTransformation tryWithResourcesASTTransformation; private final GroovydocManager groovydocManager; private final List<ClassNode> classNodeList = new LinkedList<>(); private final Deque<ClassNode> classNodeStack = new ArrayDeque<>(); private final Deque<List<InnerClassNode>> anonymousInnerClassesDefinedInMethodStack = new ArrayDeque<>(); private Tuple2<GroovyParserRuleContext, Exception> numberFormatError; private int visitingLoopStatementCnt; private int visitingSwitchStatementCnt; private int visitingAssertStatementCnt; private int visitingClosureCnt; private static final String QUESTION_STR = "?"; private static final String DOT_STR = "."; private static final String SUB_STR = "-"; private static final String ASSIGN_STR = "="; private static final String VALUE_STR = "value"; private static final String DOLLAR_STR = "$"; private static final String CALL_STR = "call"; private static final String THIS_STR = "this"; private static final String SUPER_STR = "super"; private static final String VOID_STR = "void"; private static final String SLASH_STR = "/"; private static final String SLASH_DOLLAR_STR = "/$"; private static final String TDQ_STR = "\"\"\""; private static final String TSQ_STR = "'''"; private static final String SQ_STR = "'"; private static final String DQ_STR = "\""; private static final String DOLLAR_SLASH_STR = "$/"; private static final String VAR_STR = "var"; private static final Map<String, String> QUOTATION_MAP = Maps.of(DQ_STR, DQ_STR, SQ_STR, SQ_STR, TDQ_STR, TDQ_STR, TSQ_STR, TSQ_STR, SLASH_STR, SLASH_STR, DOLLAR_SLASH_STR, SLASH_DOLLAR_STR); private static final String PACKAGE_INFO = "package-info"; private static final String PACKAGE_INFO_FILE_NAME = PACKAGE_INFO + ".groovy"; private static final String CLASS_NAME = "_CLASS_NAME"; private static final String INSIDE_PARENTHESES_LEVEL = "_INSIDE_PARENTHESES_LEVEL"; private static final String IS_INSIDE_INSTANCEOF_EXPR = "_IS_INSIDE_INSTANCEOF_EXPR"; private static final String IS_SWITCH_DEFAULT = "_IS_SWITCH_DEFAULT"; private static final String IS_NUMERIC = "_IS_NUMERIC"; private static final String IS_STRING = "_IS_STRING"; private static final String IS_INTERFACE_WITH_DEFAULT_METHODS = "_IS_INTERFACE_WITH_DEFAULT_METHODS"; private static final String IS_INSIDE_CONDITIONAL_EXPRESSION = "_IS_INSIDE_CONDITIONAL_EXPRESSION"; private static final String IS_COMMAND_EXPRESSION = "_IS_COMMAND_EXPRESSION"; private static final String PATH_EXPRESSION_BASE_EXPR = "_PATH_EXPRESSION_BASE_EXPR"; private static final String PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES = "_PATH_EXPRESSION_BASE_EXPR_GENERICS_TYPES"; private static final String PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN = "_PATH_EXPRESSION_BASE_EXPR_SAFE_CHAIN"; private static final String CMD_EXPRESSION_BASE_EXPR = "_CMD_EXPRESSION_BASE_EXPR"; private static final String TYPE_DECLARATION_MODIFIERS = "_TYPE_DECLARATION_MODIFIERS"; private static final String CLASS_DECLARATION_CLASS_NODE = "_CLASS_DECLARATION_CLASS_NODE"; private static final String VARIABLE_DECLARATION_VARIABLE_TYPE = "_VARIABLE_DECLARATION_VARIABLE_TYPE"; private static final String ANONYMOUS_INNER_CLASS_SUPER_CLASS = "_ANONYMOUS_INNER_CLASS_SUPER_CLASS"; private static final String INTEGER_LITERAL_TEXT = "_INTEGER_LITERAL_TEXT"; private static final String FLOATING_POINT_LITERAL_TEXT = "_FLOATING_POINT_LITERAL_TEXT"; private static final String ENCLOSING_INSTANCE_EXPRESSION = "_ENCLOSING_INSTANCE_EXPRESSION"; }