public Expression visitPathElement()

in src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java [2436:2666]


    public Expression visitPathElement(final 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 this.createDotExpression(ctx, baseExpr, namePartExpr, genericsTypes, isSafeChain);
            } else if (asBoolean(ctx.SAFE_DOT())) {
                return this.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, Boolean.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().SAFE_INDEX())),
                    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;
            }

            NamedPropertyArgsContext namedPropertyArgsContext = ctx.namedPropertyArgs();
            Token token = (namedPropertyArgsContext.LBRACK() == null
                            ? namedPropertyArgsContext.SAFE_INDEX()
                            : namedPropertyArgsContext.LBRACK()).getSymbol();
            return configureAST(
                    new BinaryExpression(baseExpr, createGroovyToken(token), 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(this.createCallMethodCallExpression(this.createConstantExpression(baseExpr), argumentsExpr), ctx);
                } else if (isPrimitiveType(baseExprText)) { // e.g. int(), long(), float(), etc.
                    throw this.createParsingFailedException("Primitive type literal: " + baseExprText + " cannot be used as a method name", ctx);
                }
            }

            if (baseExpr instanceof VariableExpression // e.g. m()
                    || baseExpr instanceof GStringExpression // e.g. "$m"()
                    || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING))) { // e.g. "m"()
                String baseExprText = baseExpr.getText();
                if (THIS_STR.equals(baseExprText) || SUPER_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/groovy/org/codehaus/groovy/transform/MapConstructorTransformTest.groovy:
                    // @MapConstructor(pre={ super(args?.first, args?.last); args = args ?: [:] }, post = { first = first?.toUpperCase() })
                    if (visitingClosureCount > 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(this.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(
                                                configureAST(
                                                        new MapExpression(namedArgumentListExpression.getMapEntryExpressions()),
                                                        namedArgumentListExpression
                                                ),
                                                closureExpression
                                        ),
                                        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);
                }
            }

            if (baseExpr instanceof PropertyExpression) { // e.g. obj.m { }
                MethodCallExpression methodCallExpression =
                        this.createMethodCallExpression(
                                (PropertyExpression) baseExpr,
                                configureAST(
                                        new ArgumentListExpression(closureExpression),
                                        closureExpression
                                )
                        );

                return configureAST(methodCallExpression, ctx);
            }

            if (baseExpr instanceof VariableExpression // e.g. m { }
                    || baseExpr instanceof GStringExpression // e.g. "$m" { }
                    || (baseExpr instanceof ConstantExpression && isTrue(baseExpr, IS_STRING))) { // e.g. "m" { }
                MethodCallExpression methodCallExpression =
                        this.createMethodCallExpression(
                                baseExpr,
                                configureAST(
                                        new ArgumentListExpression(closureExpression),
                                        closureExpression
                                )
                        );

                return configureAST(methodCallExpression, ctx);
            }

            // e.g. 1 { }, 1.1 { }, (1 / 2) { }, m() { }, { -> ... } { }
            MethodCallExpression methodCallExpression =
                    this.createCallMethodCallExpression(
                        baseExpr,
                        configureAST(
                                new ArgumentListExpression(closureExpression),
                                closureExpression)
                    );

            return configureAST(methodCallExpression, ctx);
        }

        throw createParsingFailedException("Unsupported path element: " + ctx.getText(), ctx);
    }