fn rewrite_expr()

in hphp/hack/src/parser/lowerer/desugar_expression_tree.rs [647:1441]


fn rewrite_expr(
    temps: &mut Temporaries,
    e: Expr,
    visitor_name: &str,
    errors: &mut Vec<(Pos, String)>,
) -> RewriteResult {
    use aast::Expr_::*;

    // If we can't rewrite the expression (e.g. due to unsupported syntax), return the
    // original syntax unmodified. This is particularly useful during code completion,
    // where an unfinished code fragment might accidentally use unsupported syntax.
    let unchanged_result = RewriteResult {
        virtual_expr: e.clone(),
        desugar_expr: e.clone(),
    };

    let Expr(_, pos, expr_) = e;
    let pos_expr = exprpos(&pos);
    match expr_ {
        // Source: MyDsl`1`
        // Virtualized: MyDsl::intType()
        // Desugared: $0v->visitInt(new ExprPos(...), 1)
        Int(_) => {
            let virtual_expr = static_meth_call(visitor_name, et::INT_TYPE, vec![], &pos);
            let desugar_expr = v_meth_call(
                et::VISIT_INT,
                vec![pos_expr, Expr((), pos.clone(), expr_)],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`1.0`
        // Virtualized: MyDsl::floatType()
        // Desugared: $0v->visitFloat(new ExprPos(...), 1.0)
        Float(_) => {
            let virtual_expr = static_meth_call(visitor_name, et::FLOAT_TYPE, vec![], &pos);
            let desugar_expr = v_meth_call(
                et::VISIT_FLOAT,
                vec![pos_expr, Expr((), pos.clone(), expr_)],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`'foo'`
        // Virtualized: MyDsl::stringType()
        // Desugared: $0v->visitString(new ExprPos(...), 'foo')
        String(_) => {
            let virtual_expr = static_meth_call(visitor_name, et::STRING_TYPE, vec![], &pos);
            let desugar_expr = v_meth_call(
                et::VISIT_STRING,
                vec![pos_expr, Expr((), pos.clone(), expr_)],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`true`
        // Virtualized: MyDsl::boolType()
        // Desugared: $0v->visitBool(new ExprPos(...), true)
        True | False => {
            let virtual_expr = static_meth_call(visitor_name, et::BOOL_TYPE, vec![], &pos);
            let desugar_expr = v_meth_call(
                et::VISIT_BOOL,
                vec![pos_expr, Expr((), pos.clone(), expr_)],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`null`
        // Virtualized: MyDsl::nullType()
        // Desugared: $0v->visitNull(new ExprPos(...))
        Null => {
            let virtual_expr = static_meth_call(visitor_name, et::NULL_TYPE, vec![], &pos);
            let desugar_expr = v_meth_call(et::VISIT_NULL, vec![pos_expr], &pos);
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`$x`
        // Virtualized: $x
        // Desugared: $0v->visitLocal(new ExprPos(...), '$x')
        Lvar(lid) => {
            let desugar_expr = v_meth_call(
                et::VISIT_LOCAL,
                vec![pos_expr, string_literal(lid.0.clone(), &((lid.1).1))],
                &pos,
            );
            let virtual_expr = Expr((), pos, Lvar(lid));
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        Binop(bop) => {
            let (op, lhs, rhs) = *bop;
            let rewritten_lhs = rewrite_expr(temps, lhs, visitor_name, errors);
            let rewritten_rhs = rewrite_expr(temps, rhs, visitor_name, errors);

            if op == Bop::Eq(None) {
                // Source: MyDsl`$x = ...`
                // Virtualized: $x = ...
                // Desugared: $0v->visitAssign(new ExprPos(...), $0v->visitLocal(...), ...)
                let desugar_expr = v_meth_call(
                    et::VISIT_ASSIGN,
                    vec![
                        pos_expr,
                        rewritten_lhs.desugar_expr,
                        rewritten_rhs.desugar_expr,
                    ],
                    &pos,
                );
                let virtual_expr = Expr(
                    (),
                    pos,
                    Binop(Box::new((
                        op,
                        rewritten_lhs.virtual_expr,
                        rewritten_rhs.virtual_expr,
                    ))),
                );
                RewriteResult {
                    virtual_expr,
                    desugar_expr,
                }
            } else {
                // Source: MyDsl`... + ...`
                // Virtualized: ...->__plus(...)
                // Desugared: $0v->visitBinop(new ExprPos(...), ..., '__plus', ...)
                let binop_str = match op {
                    Bop::Plus => "__plus",
                    Bop::Minus => "__minus",
                    Bop::Star => "__star",
                    Bop::Slash => "__slash",
                    Bop::Percent => "__percent",
                    // Convert boolean &&, ||
                    Bop::Ampamp => "__ampamp",
                    Bop::Barbar => "__barbar",
                    // Convert comparison operators, <, <=, >, >=, ===, !==
                    Bop::Lt => "__lessThan",
                    Bop::Lte => "__lessThanEqual",
                    Bop::Gt => "__greaterThan",
                    Bop::Gte => "__greaterThanEqual",
                    Bop::Eqeqeq => "__tripleEquals",
                    Bop::Diff2 => "__notTripleEquals",
                    // Convert string concatenation
                    Bop::Dot => "__dot",
                    // Convert bitwise operators, &, |, ^, <<, >>
                    Bop::Amp => "__amp",
                    Bop::Bar => "__bar",
                    Bop::Xor => "__caret",
                    Bop::Ltlt => "__lessThanLessThan",
                    Bop::Gtgt => "__greaterThanGreaterThan",
                    // Explicit list of unsupported operators and error messages
                    Bop::Starstar => {
                        errors.push((
                            pos.clone(),
                            "Expression trees do not support the exponent operator `**`.".into(),
                        ));
                        "__unsupported"
                    }
                    Bop::Eqeq | Bop::Diff => {
                        errors.push((
                            pos.clone(),
                            "Expression trees only support strict equality operators `===` and `!==`".into(),
                        ));
                        "__unsupported"
                    }
                    Bop::Cmp => {
                        errors.push((
                            pos.clone(),
                            "Expression trees do not support the spaceship operator `<=>`. Try comparison operators like `<` and `>=`".into(),
                        ));
                        "__unsupported"
                    }
                    Bop::QuestionQuestion => {
                        errors.push((
                            pos.clone(),
                            "Expression trees do not support the null coalesce operator `??`."
                                .into(),
                        ));
                        "__unsupported"
                    }
                    Bop::Eq(_) => {
                        errors.push((
                            pos.clone(),
                            "Expression trees do not support compound assignments. Try the long form style `$foo = $foo + $bar` instead.".into(),
                        ));
                        "__unsupported"
                    }
                };
                let virtual_expr = meth_call(
                    rewritten_lhs.virtual_expr,
                    binop_str,
                    vec![rewritten_rhs.virtual_expr],
                    &pos,
                );
                let desugar_expr = v_meth_call(
                    et::VISIT_BINOP,
                    vec![
                        pos_expr,
                        rewritten_lhs.desugar_expr,
                        string_literal(pos.clone(), binop_str),
                        rewritten_rhs.desugar_expr,
                    ],
                    &pos,
                );
                RewriteResult {
                    virtual_expr,
                    desugar_expr,
                }
            }
        }
        // Source: MyDsl`!...`
        // Virtualized: ...->__exclamationMark(...)
        // Desugared: $0v->visitUnop(new ExprPos(...), ..., '__exclamationMark')
        Unop(unop) => {
            let (op, operand) = *unop;
            let rewritten_operand = rewrite_expr(temps, operand, visitor_name, errors);

            let op_str = match op {
                // Allow boolean not operator !$x
                Uop::Unot => "__exclamationMark",
                // Allow negation -$x (required for supporting negative literals -123)
                Uop::Uminus => "__negate",
                // Allow bitwise complement
                Uop::Utild => "__tilde",
                // Currently not allowed operators
                Uop::Uplus => {
                    errors.push((
                        pos.clone(),
                        "Expression trees do not support the unary plus operator.".into(),
                    ));
                    "__unsupported"
                }
                // Postfix ++
                Uop::Upincr => "__postfixPlusPlus",
                // Prefix ++
                Uop::Uincr => {
                    errors.push((
                        pos.clone(),
                        "Expression trees only support postfix increment operator `$x++`.".into(),
                    ));
                    "__unsupported"
                }
                // Postfix --
                Uop::Updecr => "__postfixMinusMinus",
                // Prefix --
                Uop::Udecr => {
                    errors.push((
                        pos.clone(),
                        "Expression trees only support postfix decrement operator `$x--`.".into(),
                    ));
                    "__unsupported"
                }
                Uop::Usilence => {
                    errors.push((
                        pos.clone(),
                        "Expression trees do not support the error suppression operator `@`."
                            .into(),
                    ));
                    "__unsupported"
                }
            };
            let virtual_expr = meth_call(rewritten_operand.virtual_expr, op_str, vec![], &pos);
            let desugar_expr = v_meth_call(
                et::VISIT_UNOP,
                vec![
                    pos_expr,
                    rewritten_operand.desugar_expr,
                    string_literal(pos.clone(), op_str),
                ],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`... ? ... : ...`
        // Virtualized: ...->__bool() ? ... : ...
        // Desugared: $0v->visitTernary(new ExprPos(...), ..., ..., ...)
        Eif(eif) => {
            let (e1, e2o, e3) = *eif;

            let rewritten_e1 = rewrite_expr(temps, e1, visitor_name, errors);
            let rewritten_e2 = if let Some(e2) = e2o {
                rewrite_expr(temps, e2, visitor_name, errors)
            } else {
                errors.push((
                    pos.clone(),
                    "Unsupported expression tree syntax: Elvis operator".into(),
                ));
                unchanged_result
            };
            let rewritten_e3 = rewrite_expr(temps, e3, visitor_name, errors);

            let desugar_expr = v_meth_call(
                et::VISIT_TERNARY,
                vec![
                    pos_expr,
                    rewritten_e1.desugar_expr,
                    rewritten_e2.desugar_expr,
                    rewritten_e3.desugar_expr,
                ],
                &pos,
            );
            let virtual_expr = Expr(
                (),
                pos,
                Eif(Box::new((
                    boolify(rewritten_e1.virtual_expr),
                    Some(rewritten_e2.virtual_expr),
                    rewritten_e3.virtual_expr,
                ))),
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`...()`
        // Virtualized: ...()
        // Desugared: $0v->visitCall(new ExprPos(...), ..., vec[])
        Call(call) => {
            let (recv, targs, args, variadic) = *call;

            if variadic.is_some() {
                errors.push((
                    pos.clone(),
                    "Expression trees do not support variadic calls.".into(),
                ));
            }
            if !targs.is_empty() {
                errors.push((
                    pos.clone(),
                    "Expression trees do not support function calls with generics.".into(),
                ));
            }
            match &recv.2 {
                // Don't transform calls to `hh_show`.
                Id(sid) if is_typechecker_fun_name(&sid.1) => {
                    let call_e = Expr::new((), pos, Call(Box::new((recv, targs, args, variadic))));
                    return RewriteResult {
                        desugar_expr: call_e.clone(),
                        virtual_expr: call_e,
                    };
                }
                _ => {}
            }

            let mut args_without_inout = vec![];
            for arg in args {
                match arg {
                    (ParamKind::Pnormal, e) => args_without_inout.push(e),
                    (ParamKind::Pinout(_), Expr(_, p, _)) => errors.push((
                        p,
                        "Expression trees do not support `inout` function calls.".into(),
                    )),
                }
            }

            let (virtual_args, desugar_args) =
                rewrite_exprs(temps, args_without_inout, visitor_name, errors);

            match recv.2 {
                // Source: MyDsl`foo()`
                // Virtualized: MyDsl::symbolType($0fpXX)()
                // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitGlobalFunction(new ExprPos(...), $0fpXX), vec[])
                Id(sid) => {
                    let len = temps.global_function_pointers.len();
                    temps.global_function_pointers.push(global_func_ptr(&sid));
                    let temp_variable = temp_function_pointer_lvar(&recv.1, len);

                    let desugar_expr = v_meth_call(
                        et::VISIT_CALL,
                        vec![
                            pos_expr.clone(),
                            v_meth_call(
                                et::VISIT_GLOBAL_FUNCTION,
                                vec![pos_expr, temp_variable.clone()],
                                &pos,
                            ),
                            vec_literal(desugar_args),
                        ],
                        &pos,
                    );
                    let virtual_expr = Expr(
                        (),
                        pos.clone(),
                        Call(Box::new((
                            static_meth_call(
                                visitor_name,
                                et::SYMBOL_TYPE,
                                vec![temp_variable],
                                &pos,
                            ),
                            vec![],
                            build_args(virtual_args),
                            None,
                        ))),
                    );
                    RewriteResult {
                        virtual_expr,
                        desugar_expr,
                    }
                }
                // Source: MyDsl`Foo::bar()`
                // Virtualized: MyDsl::symbolType($0smXX)()
                // Desugared: $0v->visitCall(new ExprPos(...), $0v->visitStaticMethod(new ExprPos(...), $0smXX, vec[])
                ClassConst(cc) => {
                    let (cid, s) = *cc;
                    if let ClassId_::CIexpr(Expr(_, _, Id(sid))) = &cid.2 {
                        if sid.1 == classes::PARENT
                            || sid.1 == classes::SELF
                            || sid.1 == classes::STATIC
                        {
                            errors.push((
                                pos,
                                "Static method calls in expression trees require explicit class names.".into(),
                            ));
                            return unchanged_result;
                        }
                    } else {
                        errors.push((
                            pos,
                            "Expression trees only support function calls and static method calls on named classes.".into(),
                        ));
                        return unchanged_result;
                    };

                    let len = temps.static_method_pointers.len();
                    temps
                        .static_method_pointers
                        .push(static_meth_ptr(&recv.1, &cid, &s));
                    let temp_variable = temp_static_method_lvar(&recv.1, len);

                    let desugar_expr = v_meth_call(
                        et::VISIT_CALL,
                        vec![
                            pos_expr.clone(),
                            v_meth_call(
                                et::VISIT_STATIC_METHOD,
                                vec![pos_expr, temp_variable.clone()],
                                &pos,
                            ),
                            vec_literal(desugar_args),
                        ],
                        &pos,
                    );
                    let virtual_expr = Expr(
                        (),
                        pos.clone(),
                        Call(Box::new((
                            static_meth_call(
                                visitor_name,
                                et::SYMBOL_TYPE,
                                vec![temp_variable],
                                &pos,
                            ),
                            vec![],
                            build_args(virtual_args),
                            None,
                        ))),
                    );
                    RewriteResult {
                        virtual_expr,
                        desugar_expr,
                    }
                }
                // Source: MyDsl`$x->bar()`
                // Virtualized: $x->bar()
                // Desugared: $0v->visitCall($0v->visitMethodCall(new ExprPos(...), $0v->visitLocal(new ExprPos(...), '$x'), 'bar'), vec[])
                ObjGet(og) if og.3 == ast::PropOrMethod::IsMethod => {
                    let (e1, e2, null_flavor, is_prop_call) = *og;
                    if null_flavor == OgNullFlavor::OGNullsafe {
                        errors.push((
                            pos.clone(),
                            "Expression Trees do not support nullsafe method calls".into(),
                        ));
                    }
                    let rewritten_e1 = rewrite_expr(temps, e1, visitor_name, errors);
                    let id = if let Id(id) = &e2.2 {
                        string_literal(id.0.clone(), &id.1)
                    } else {
                        errors.push((
                            pos.clone(),
                            "Expression trees only support named method calls.".into(),
                        ));
                        e2.clone()
                    };
                    let desugar_expr = v_meth_call(
                        et::VISIT_CALL,
                        vec![
                            pos_expr.clone(),
                            v_meth_call(
                                et::VISIT_INSTANCE_METHOD,
                                vec![pos_expr, rewritten_e1.desugar_expr, id],
                                &pos,
                            ),
                            vec_literal(desugar_args),
                        ],
                        &pos,
                    );
                    let virtual_expr = Expr(
                        (),
                        pos.clone(),
                        Call(Box::new((
                            Expr(
                                (),
                                pos,
                                ObjGet(Box::new((
                                    rewritten_e1.virtual_expr,
                                    e2,
                                    null_flavor,
                                    is_prop_call,
                                ))),
                            ),
                            vec![],
                            build_args(virtual_args),
                            None,
                        ))),
                    );
                    RewriteResult {
                        virtual_expr,
                        desugar_expr,
                    }
                }
                _ => {
                    let rewritten_recv =
                        rewrite_expr(temps, Expr((), recv.1, recv.2), visitor_name, errors);

                    let desugar_expr = v_meth_call(
                        et::VISIT_CALL,
                        vec![
                            pos_expr,
                            rewritten_recv.desugar_expr,
                            vec_literal(desugar_args),
                        ],
                        &pos,
                    );
                    let virtual_expr = Expr(
                        (),
                        pos,
                        Call(Box::new((
                            rewritten_recv.virtual_expr,
                            vec![],
                            build_args(virtual_args),
                            None,
                        ))),
                    );
                    RewriteResult {
                        virtual_expr,
                        desugar_expr,
                    }
                }
            }
        }
        // Source: MyDsl`($x) ==> { ... }`
        // Virtualized: ($x) ==> { ...; return MyDsl::voidType(); }
        //   if no `return expr;` statements.
        // Desugared: $0v->visitLambda(new ExprPos(...), vec['$x'], vec[...]).
        Lfun(lf) => {
            let mut fun_ = lf.0;

            let mut param_names = Vec::with_capacity(fun_.params.len());
            for param in &fun_.params {
                if param.expr.is_some() {
                    errors.push((
                        param.pos.clone(),
                        "Expression trees do not support parameters with default values.".into(),
                    ));
                }
                param_names.push(string_literal(param.pos.clone(), &param.name));
            }

            let body = std::mem::take(&mut fun_.body.fb_ast);

            let should_append_return = only_void_return(&body);

            let (mut virtual_body_stmts, desugar_body) =
                rewrite_stmts(temps, body, visitor_name, errors);
            if should_append_return {
                virtual_body_stmts.push(Stmt(
                    pos.clone(),
                    aast::Stmt_::Return(Box::new(Some(static_meth_call(
                        visitor_name,
                        et::VOID_TYPE,
                        vec![],
                        &pos,
                    )))),
                ));
            }

            let desugar_expr = v_meth_call(
                et::VISIT_LAMBDA,
                vec![
                    pos_expr,
                    vec_literal(param_names),
                    vec_literal(desugar_body),
                ],
                &pos,
            );
            fun_.body.fb_ast = virtual_body_stmts;
            let virtual_expr = Expr((), pos, Lfun(Box::new((fun_, vec![]))));
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`${ ... }`
        // Virtualized to `${ ... }`
        // Desugared to `$0v->splice(new ExprPos(...), '$var_name', ...)`
        ETSplice(e) => {
            if let Err(err) = check_nested_splice(&e) {
                errors.push(err);
            };

            let len = temps.splices.len();
            let expr_pos = e.1.clone();
            temps.splices.push(*e);
            let temp_variable = temp_splice_lvar(&expr_pos, len);
            let temp_variable_string = string_literal(expr_pos, &temp_splice_lvar_string(len));
            let desugar_expr = v_meth_call(
                et::SPLICE,
                vec![pos_expr, temp_variable_string, temp_variable.clone()],
                &pos,
            );
            let virtual_expr = Expr((), pos, ETSplice(Box::new(temp_variable)));
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`(...)->foo`
        // Virtualized to: `(...)->foo`
        // Desugared to `$0v->visitPropertyAccess(new ExprPos(...), ...), 'foo')`
        ObjGet(og) => {
            let (e1, e2, null_flavor, is_prop_call) = *og;

            if null_flavor == OgNullFlavor::OGNullsafe {
                errors.push((
                    pos.clone(),
                    "Expression Trees do not support nullsafe property access".into(),
                ));
            }
            let rewritten_e1 = rewrite_expr(temps, e1, visitor_name, errors);

            let id = if let Id(id) = &e2.2 {
                string_literal(id.0.clone(), &id.1)
            } else {
                errors.push((
                    pos.clone(),
                    "Expression trees only support named property access.".into(),
                ));
                e2.clone()
            };
            let desugar_expr = v_meth_call(
                et::VISIT_PROPERTY_ACCESS,
                vec![pos_expr, rewritten_e1.desugar_expr, id],
                &pos,
            );

            let virtual_expr = Expr(
                (),
                pos,
                ObjGet(Box::new((
                    rewritten_e1.virtual_expr,
                    e2,
                    null_flavor,
                    is_prop_call,
                ))),
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        // Source: MyDsl`<foo my-attr="stuff">text <foo-child/> </foo>`
        // Virtualized: <foo my-attr={MyDsl::stringType()}>{MyDsl::stringType()} <foo-child/> </foo>
        // Desugared:
        //   $0v->visitXhp(
        //     new ExprPos(...),
        //     :foo::class,
        //     dict["my-attr" => $0v->visitString(...)],
        //     vec[
        //       $0v->visitString(..., "text ")],
        //       $0v->visitXhp(..., :foo-child::class, ...),
        //     ],
        //   )
        Xml(xml) => {
            let (hint, attrs, children) = *xml;

            let mut virtual_attrs = vec![];
            let mut desugar_attrs = vec![];
            for attr in attrs {
                match attr {
                    aast::XhpAttribute::XhpSimple(xs) => {
                        let (attr_name_pos, attr_name) = xs.name.clone();
                        let dict_key =
                            Expr::new((), attr_name_pos, Expr_::String(BString::from(attr_name)));

                        let rewritten_attr_expr =
                            rewrite_expr(temps, xs.expr, visitor_name, errors);
                        desugar_attrs.push((dict_key, rewritten_attr_expr.desugar_expr));
                        virtual_attrs.push(aast::XhpAttribute::XhpSimple(aast::XhpSimple {
                            expr: rewritten_attr_expr.virtual_expr,
                            ..xs
                        }))
                    }
                    aast::XhpAttribute::XhpSpread(e) => {
                        errors.push((
                            e.1,
                            "Expression trees do not support attribute spread syntax.".into(),
                        ));
                    }
                }
            }

            let (virtual_children, desugar_children) =
                rewrite_exprs(temps, children, visitor_name, errors);

            // Construct :foo::class.
            let hint_pos = hint.0.clone();
            let hint_class = Expr_::ClassConst(Box::new((
                ClassId(
                    (),
                    hint_pos.clone(),
                    ClassId_::CIexpr(Expr::new(
                        (),
                        hint_pos.clone(),
                        Expr_::Id(Box::new(ast_defs::Id(hint_pos.clone(), hint.1.clone()))),
                    )),
                ),
                (hint_pos, "class".to_string()),
            )));

            let virtual_expr = Expr(
                (),
                pos.clone(),
                Xml(Box::new((hint, virtual_attrs, virtual_children))),
            );
            let desugar_expr = v_meth_call(
                et::VISIT_XHP,
                vec![
                    pos_expr,
                    Expr((), pos.clone(), hint_class),
                    dict_literal(&pos, desugar_attrs),
                    vec_literal(desugar_children),
                ],
                &pos,
            );
            RewriteResult {
                virtual_expr,
                desugar_expr,
            }
        }
        ClassConst(_) => {
            errors.push((
                pos,
                "Expression trees do not support directly referencing class consts. Consider splicing values defined outside the scope of an Expression Tree using ${...}.".into(),
            ));
            unchanged_result
        }
        Efun(_) => {
            errors.push((
                pos,
                "Expression trees do not support PHP lambdas. Consider using Hack lambdas `() ==> {}` instead.".into(),
            ));
            unchanged_result
        }
        ExpressionTree(_) => {
            errors.push((
                pos,
                "Expression trees may not be nested. Consider splicing Expression trees together using `${}`.".into()
            ));
            unchanged_result
        }
        _ => {
            errors.push((pos, "Unsupported expression tree syntax.".into()));
            unchanged_result
        }
    }
}