Skip to main content

luaur_ast/methods/
parser_parse_compound_assignment.rs

1use crate::functions::is_expr_l_value::is_expr_l_value;
2use crate::records::ast_expr::AstExpr;
3use crate::records::ast_expr_binary::AstExprBinaryOp;
4use crate::records::ast_stat::AstStat;
5use crate::records::ast_stat_compound_assign::AstStatCompoundAssign;
6use crate::records::cst_stat_compound_assign::CstStatCompoundAssign;
7use crate::records::location::Location;
8use crate::records::parser::Parser;
9
10impl Parser {
11    pub fn parse_compound_assignment(
12        &mut self,
13        mut initial: *mut AstExpr,
14        op: AstExprBinaryOp,
15    ) -> *mut AstStat {
16        // C++ REASSIGNS `initial` to an error node and FALLS THROUGH to
17        // nextLexeme()+parseExpr() — it does NOT return here. The port returned
18        // early, so when the LHS is not an lvalue the operator (`+=` etc.) was
19        // never consumed; parseBlockNoScope's statement loop then re-parsed the
20        // same token forever, allocating until the process OOMs (machine crash).
21        if !is_expr_l_value(initial) {
22            initial = if luaur_common::FFlag::LuauConst2.get() {
23                self.report_l_value_error(initial)
24            } else {
25                let expressions = self.copy_initializer_list_t(&[initial]);
26                self.report_expr_error(
27                    unsafe { (*initial).base.location },
28                    expressions,
29                    format_args!("Assigned expression must be a variable or a field"),
30                )
31            } as *mut AstExpr;
32        }
33
34        let op_position = self.lexer.current().location.begin;
35        self.next_lexeme();
36
37        let value = self.parse_expr_i32(0);
38
39        let node = unsafe {
40            (*self.allocator).alloc(AstStatCompoundAssign::new(
41                Location::new(unsafe { (*initial).base.location.begin }, unsafe {
42                    (*value).base.location.end
43                }),
44                op,
45                initial,
46                value,
47            ))
48        };
49
50        if self.options.store_cst_data {
51            let cst_node =
52                unsafe { (*self.allocator).alloc(CstStatCompoundAssign::new(op_position)) };
53            self.cst_node_map.try_insert(
54                node as *mut crate::records::ast_node::AstNode,
55                cst_node as *mut crate::records::cst_node::CstNode,
56            );
57        }
58
59        node as *mut AstStat
60    }
61}