Skip to main content

zuzu_rust/
optimizer.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::ast::{
4    BlockStatement, CallArgument, CatchClause, ClassMember, DictEntry, DictKey, Expression,
5    ExpressionStatement, IfStatement, Program, Statement, SwitchIndexEntry, SwitchStatement,
6    TemplatePart, VariableDeclaration, WhileStatement,
7};
8use crate::error::{Result, ZuzuRustError};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum OptimizationLevel {
12    O0,
13    O1,
14    O2,
15    O3,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum OptimizationPass {
20    BlockScopeElision,
21    ConstantFolding,
22    ConstantConditionPruning,
23    UnreachablePruning,
24    RegexCache,
25    IdentifierResolution,
26    TypecheckSkip,
27    OperatorEnum,
28    ChainInline,
29    CollectionPresize,
30    SwitchIndexing,
31    RangeArrayLoopLowering,
32}
33
34const ALL_OPTIMIZATION_PASSES: &[OptimizationPass] = &[
35    OptimizationPass::BlockScopeElision,
36    OptimizationPass::ConstantFolding,
37    OptimizationPass::ConstantConditionPruning,
38    OptimizationPass::UnreachablePruning,
39    OptimizationPass::RegexCache,
40    OptimizationPass::IdentifierResolution,
41    OptimizationPass::TypecheckSkip,
42    OptimizationPass::OperatorEnum,
43    OptimizationPass::ChainInline,
44    OptimizationPass::CollectionPresize,
45    OptimizationPass::SwitchIndexing,
46    OptimizationPass::RangeArrayLoopLowering,
47];
48
49impl OptimizationPass {
50    pub fn name(self) -> &'static str {
51        match self {
52            OptimizationPass::BlockScopeElision => "block-scope-elision",
53            OptimizationPass::ConstantFolding => "constant-folding",
54            OptimizationPass::ConstantConditionPruning => "constant-condition-pruning",
55            OptimizationPass::UnreachablePruning => "unreachable-pruning",
56            OptimizationPass::RegexCache => "regex-cache",
57            OptimizationPass::IdentifierResolution => "identifier-resolution",
58            OptimizationPass::TypecheckSkip => "typecheck-skip",
59            OptimizationPass::OperatorEnum => "operator-enum",
60            OptimizationPass::ChainInline => "chain-inline",
61            OptimizationPass::CollectionPresize => "collection-presize",
62            OptimizationPass::SwitchIndexing => "switch-indexing",
63            OptimizationPass::RangeArrayLoopLowering => "range-array-loop-lowering",
64        }
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct OptimizationOptions {
70    level: OptimizationLevel,
71    enabled: HashSet<OptimizationPass>,
72}
73
74impl Default for OptimizationOptions {
75    fn default() -> Self {
76        Self::for_level(OptimizationLevel::O2)
77    }
78}
79
80impl OptimizationOptions {
81    pub fn for_level(level: OptimizationLevel) -> Self {
82        let mut enabled = HashSet::new();
83        match level {
84            OptimizationLevel::O0 => {}
85            OptimizationLevel::O1 | OptimizationLevel::O2 | OptimizationLevel::O3 => {
86                enabled.insert(OptimizationPass::BlockScopeElision);
87                enabled.insert(OptimizationPass::ConstantFolding);
88                enabled.insert(OptimizationPass::UnreachablePruning);
89                enabled.insert(OptimizationPass::RegexCache);
90                enabled.insert(OptimizationPass::TypecheckSkip);
91                enabled.insert(OptimizationPass::CollectionPresize);
92            }
93        }
94        match level {
95            OptimizationLevel::O2 | OptimizationLevel::O3 => {
96                enabled.insert(OptimizationPass::IdentifierResolution);
97                enabled.insert(OptimizationPass::OperatorEnum);
98                enabled.insert(OptimizationPass::ChainInline);
99            }
100            _ => {}
101        }
102        if matches!(level, OptimizationLevel::O3) {
103            enabled.insert(OptimizationPass::SwitchIndexing);
104            enabled.insert(OptimizationPass::RangeArrayLoopLowering);
105        }
106        Self { level, enabled }
107    }
108
109    pub fn o1() -> Self {
110        Self::for_level(OptimizationLevel::O1)
111    }
112
113    pub fn disabled() -> Self {
114        Self::for_level(OptimizationLevel::O0)
115    }
116
117    pub fn level(&self) -> OptimizationLevel {
118        self.level
119    }
120
121    pub fn enables(&self, pass: OptimizationPass) -> bool {
122        self.enabled.contains(&pass)
123    }
124
125    pub fn is_empty(&self) -> bool {
126        self.enabled.is_empty()
127    }
128
129    pub fn enable(&mut self, name: &str) -> Result<()> {
130        let pass = parse_pass_name(name)?;
131        self.enabled.insert(pass);
132        Ok(())
133    }
134
135    pub fn disable(&mut self, name: &str) -> Result<()> {
136        let pass = parse_pass_name(name)?;
137        self.enabled.remove(&pass);
138        Ok(())
139    }
140}
141
142pub fn parse_level(text: &str) -> Option<OptimizationLevel> {
143    match text {
144        "-o0" | "0" => Some(OptimizationLevel::O0),
145        "-o1" | "1" => Some(OptimizationLevel::O1),
146        "-o2" | "2" => Some(OptimizationLevel::O2),
147        "-o3" | "3" => Some(OptimizationLevel::O3),
148        _ => None,
149    }
150}
151
152pub fn parse_pass_name(name: &str) -> Result<OptimizationPass> {
153    for pass in ALL_OPTIMIZATION_PASSES {
154        if pass.name() == name {
155            return Ok(*pass);
156        }
157    }
158    Err(ZuzuRustError::cli(format!(
159        "unknown optimization pass: {name}"
160    )))
161}
162
163pub fn all_passes() -> &'static [OptimizationPass] {
164    ALL_OPTIMIZATION_PASSES
165}
166
167pub fn optimize_program(program: &mut Program, options: &OptimizationOptions) {
168    if options.is_empty() {
169        return;
170    }
171    let mut optimizer = Optimizer { options };
172    optimizer.optimize_statements(&mut program.statements);
173    if options.enables(OptimizationPass::ConstantFolding) {
174        propagate_constants(&mut program.statements);
175        let mut post_propagation_options = options.clone();
176        post_propagation_options
177            .enabled
178            .remove(&OptimizationPass::ConstantConditionPruning);
179        post_propagation_options
180            .enabled
181            .remove(&OptimizationPass::UnreachablePruning);
182        let mut post_propagation_optimizer = Optimizer {
183            options: &post_propagation_options,
184        };
185        post_propagation_optimizer.optimize_statements(&mut program.statements);
186    }
187    if options.enables(OptimizationPass::IdentifierResolution) {
188        annotate_identifier_depths(program);
189    }
190}
191
192struct Optimizer<'a> {
193    options: &'a OptimizationOptions,
194}
195
196impl Optimizer<'_> {
197    fn optimize_statements(&mut self, statements: &mut Vec<Statement>) {
198        let mut optimized = Vec::with_capacity(statements.len());
199        for mut statement in std::mem::take(statements) {
200            self.optimize_statement(&mut statement);
201            if self
202                .options
203                .enables(OptimizationPass::ConstantConditionPruning)
204            {
205                if let Some(replacement) = self.prune_statement(statement) {
206                    for mut replacement in replacement {
207                        self.optimize_statement(&mut replacement);
208                        optimized.push(replacement);
209                    }
210                }
211            } else {
212                optimized.push(statement);
213            }
214            if self.options.enables(OptimizationPass::UnreachablePruning)
215                && optimized.last().map(is_terminal_statement).unwrap_or(false)
216            {
217                break;
218            }
219        }
220        *statements = optimized;
221    }
222
223    fn optimize_statement(&mut self, statement: &mut Statement) {
224        match statement {
225            Statement::Block(block) => self.optimize_block(block),
226            Statement::VariableDeclaration(node) => {
227                if let Some(init) = &mut node.init {
228                    self.optimize_expression(init);
229                }
230                if self.options.enables(OptimizationPass::TypecheckSkip)
231                    && node.runtime_typecheck_required == Some(false)
232                {
233                    node.runtime_typecheck_required = Some(false);
234                }
235            }
236            Statement::VariableUnpackDeclaration(node) => {
237                self.optimize_expression(&mut node.init);
238                for entry in node.pattern.entries_mut() {
239                    self.optimize_dict_key(&mut entry.key);
240                    if let Some(default_value) = &mut entry.default_value {
241                        self.optimize_expression(default_value);
242                    }
243                    if self.options.enables(OptimizationPass::TypecheckSkip)
244                        && entry.runtime_typecheck_required == Some(false)
245                    {
246                        entry.runtime_typecheck_required = Some(false);
247                    }
248                }
249            }
250            Statement::FunctionDeclaration(node) => self.optimize_block(&mut node.body),
251            Statement::ClassDeclaration(node) => {
252                for member in &mut node.body {
253                    self.optimize_class_member(member);
254                }
255            }
256            Statement::TraitDeclaration(node) => {
257                for member in &mut node.body {
258                    self.optimize_class_member(member);
259                }
260            }
261            Statement::ImportDeclaration(node) => {
262                if let Some(condition) = &mut node.condition {
263                    self.optimize_expression(&mut condition.test);
264                }
265            }
266            Statement::IfStatement(node) => {
267                self.optimize_expression(&mut node.test);
268                self.optimize_block(&mut node.consequent);
269                if let Some(alternate) = &mut node.alternate {
270                    self.optimize_statement(alternate);
271                }
272            }
273            Statement::WhileStatement(node) => {
274                self.optimize_expression(&mut node.test);
275                self.optimize_block(&mut node.body);
276            }
277            Statement::ForStatement(node) => {
278                self.optimize_expression(&mut node.iterable);
279                self.optimize_block(&mut node.body);
280                if let Some(else_block) = &mut node.else_block {
281                    self.optimize_block(else_block);
282                }
283            }
284            Statement::SwitchStatement(node) => self.optimize_switch(node),
285            Statement::TryStatement(node) => {
286                self.optimize_block(&mut node.body);
287                for handler in &mut node.handlers {
288                    self.optimize_block(&mut handler.body);
289                }
290            }
291            Statement::ReturnStatement(node) => {
292                if let Some(argument) = &mut node.argument {
293                    self.optimize_expression(argument);
294                }
295            }
296            Statement::ThrowStatement(node) => self.optimize_expression(&mut node.argument),
297            Statement::DieStatement(node) => self.optimize_expression(&mut node.argument),
298            Statement::PostfixConditionalStatement(node) => {
299                self.optimize_statement(&mut node.statement);
300                self.optimize_expression(&mut node.test);
301            }
302            Statement::KeywordStatement(node) => {
303                for argument in &mut node.arguments {
304                    self.optimize_expression(argument);
305                }
306            }
307            Statement::ExpressionStatement(node) => self.optimize_expression(&mut node.expression),
308            Statement::LoopControlStatement(_) => {}
309        }
310        if self
311            .options
312            .enables(OptimizationPass::RangeArrayLoopLowering)
313        {
314            if let Some(mut lowered) = lower_range_array_loop(statement) {
315                self.optimize_statement(&mut lowered);
316                *statement = lowered;
317            }
318        }
319    }
320
321    fn optimize_class_member(&mut self, member: &mut ClassMember) {
322        match member {
323            ClassMember::Field(field) => {
324                if let Some(default_value) = &mut field.default_value {
325                    self.optimize_expression(default_value);
326                }
327            }
328            ClassMember::Method(method) => self.optimize_block(&mut method.body),
329            ClassMember::Class(class) => {
330                for member in &mut class.body {
331                    self.optimize_class_member(member);
332                }
333            }
334            ClassMember::Trait(trait_decl) => {
335                for member in &mut trait_decl.body {
336                    self.optimize_class_member(member);
337                }
338            }
339        }
340    }
341
342    fn optimize_block(&mut self, block: &mut BlockStatement) {
343        self.optimize_statements(&mut block.statements);
344        if self.options.enables(OptimizationPass::BlockScopeElision) {
345            block.needs_lexical_scope = block_needs_lexical_scope(block);
346        }
347    }
348
349    fn optimize_switch(&mut self, node: &mut SwitchStatement) {
350        self.optimize_expression(&mut node.discriminant);
351        if let Some(comparator) = &mut node.comparator {
352            *comparator = preferred_operator(comparator).to_owned();
353        }
354        for case in &mut node.cases {
355            for operator in &mut case.operators {
356                if let Some(operator) = operator {
357                    *operator = preferred_operator(operator).to_owned();
358                }
359            }
360            for value in &mut case.values {
361                self.optimize_expression(value);
362            }
363            self.optimize_statements(&mut case.consequent);
364        }
365        if let Some(default) = &mut node.default {
366            self.optimize_statements(default);
367        }
368        if self.options.enables(OptimizationPass::SwitchIndexing) {
369            node.index = build_switch_index(node);
370        }
371    }
372
373    fn optimize_expression(&mut self, expr: &mut Expression) {
374        match expr {
375            Expression::Unary {
376                operator, argument, ..
377            } => {
378                self.optimize_expression(argument);
379                if self.options.enables(OptimizationPass::OperatorEnum) && operator != "\\" {
380                    *operator = preferred_operator(operator).to_owned();
381                }
382            }
383            Expression::Binary {
384                operator,
385                left,
386                right,
387                ..
388            } => {
389                self.optimize_expression(left);
390                self.optimize_expression(right);
391                if self.options.enables(OptimizationPass::OperatorEnum) {
392                    *operator = preferred_operator(operator).to_owned();
393                }
394            }
395            Expression::Ternary {
396                test,
397                consequent,
398                alternate,
399                ..
400            } => {
401                self.optimize_expression(test);
402                self.optimize_expression(consequent);
403                self.optimize_expression(alternate);
404            }
405            Expression::DefinedOr { left, right, .. } => {
406                self.optimize_expression(left);
407                self.optimize_expression(right);
408            }
409            Expression::Assignment {
410                operator,
411                left,
412                right,
413                ..
414            } => {
415                self.optimize_expression(left);
416                self.optimize_expression(right);
417                if self.options.enables(OptimizationPass::OperatorEnum) {
418                    *operator = preferred_operator(operator).to_owned();
419                }
420            }
421            Expression::Call {
422                callee, arguments, ..
423            } => {
424                self.optimize_expression(callee);
425                for argument in arguments {
426                    self.optimize_call_argument(argument);
427                }
428            }
429            Expression::MemberAccess { object, .. } => self.optimize_expression(object),
430            Expression::DynamicMemberCall {
431                object,
432                member,
433                arguments,
434                ..
435            } => {
436                self.optimize_expression(object);
437                self.optimize_expression(member);
438                for argument in arguments {
439                    self.optimize_call_argument(argument);
440                }
441            }
442            Expression::Index { object, index, .. } => {
443                self.optimize_expression(object);
444                self.optimize_expression(index);
445            }
446            Expression::Slice {
447                object, start, end, ..
448            } => {
449                self.optimize_expression(object);
450                if let Some(start) = start {
451                    self.optimize_expression(start);
452                }
453                if let Some(end) = end {
454                    self.optimize_expression(end);
455                }
456            }
457            Expression::DictAccess { object, key, .. } => {
458                self.optimize_expression(object);
459                self.optimize_dict_key(key);
460            }
461            Expression::PostfixUpdate {
462                operator, argument, ..
463            } => {
464                self.optimize_expression(argument);
465                if self.options.enables(OptimizationPass::OperatorEnum) {
466                    *operator = preferred_operator(operator).to_owned();
467                }
468            }
469            Expression::Lambda { body, .. } => self.optimize_expression(body),
470            Expression::FunctionExpression { body, .. } => self.optimize_block(body),
471            Expression::LetExpression { init, .. } => {
472                if let Some(init) = init {
473                    self.optimize_expression(init);
474                }
475            }
476            Expression::TryExpression { body, handlers, .. } => {
477                self.optimize_block(body);
478                for CatchClause { body, .. } in handlers {
479                    self.optimize_block(body);
480                }
481            }
482            Expression::DoExpression { body, .. }
483            | Expression::AwaitExpression { body, .. }
484            | Expression::SpawnExpression { body, .. } => self.optimize_block(body),
485            Expression::ArrayLiteral {
486                elements,
487                capacity_hint,
488                ..
489            } => {
490                for element in &mut *elements {
491                    self.optimize_expression(element);
492                }
493                if self.options.enables(OptimizationPass::CollectionPresize) {
494                    *capacity_hint = Some(collection_element_capacity_hint(elements));
495                }
496            }
497            Expression::SetLiteral {
498                elements,
499                capacity_hint,
500                ..
501            }
502            | Expression::BagLiteral {
503                elements,
504                capacity_hint,
505                ..
506            } => {
507                for element in &mut *elements {
508                    self.optimize_expression(element);
509                }
510                if self.options.enables(OptimizationPass::CollectionPresize) {
511                    *capacity_hint = Some(collection_element_capacity_hint(elements));
512                }
513            }
514            Expression::DictLiteral {
515                entries,
516                capacity_hint,
517                ..
518            }
519            | Expression::PairListLiteral {
520                entries,
521                capacity_hint,
522                ..
523            } => {
524                for entry in &mut *entries {
525                    self.optimize_dict_entry(entry);
526                }
527                if self.options.enables(OptimizationPass::CollectionPresize) {
528                    *capacity_hint = Some(entries.len());
529                }
530            }
531            Expression::TemplateLiteral { parts, .. } => {
532                for part in parts {
533                    if let TemplatePart::Expression { expression, .. } = part {
534                        self.optimize_expression(expression);
535                    }
536                }
537            }
538            Expression::SuperCall { arguments, .. } => {
539                for argument in arguments {
540                    self.optimize_call_argument(argument);
541                }
542            }
543            Expression::RegexLiteral {
544                pattern,
545                parts,
546                flags,
547                cache_key,
548                ..
549            } => {
550                for part in parts.iter_mut() {
551                    if let TemplatePart::Expression { expression, .. } = part {
552                        self.optimize_expression(expression);
553                    }
554                }
555                if parts.is_empty() && self.options.enables(OptimizationPass::RegexCache) {
556                    *cache_key = Some(regex_cache_key(pattern, flags));
557                }
558            }
559            Expression::Identifier { .. }
560            | Expression::NumberLiteral { .. }
561            | Expression::StringLiteral { .. }
562            | Expression::BinaryStringLiteral { .. }
563            | Expression::BooleanLiteral { .. }
564            | Expression::NullLiteral { .. } => {}
565        }
566        if self.options.enables(OptimizationPass::ChainInline) {
567            if let Some(inlined) = inline_simple_chain(expr) {
568                *expr = inlined;
569            }
570        }
571        if self.options.enables(OptimizationPass::ConstantFolding) {
572            if let Some(folded) = fold_expression(expr) {
573                *expr = folded;
574            }
575        }
576    }
577
578    fn optimize_dict_entry(&mut self, entry: &mut DictEntry) {
579        self.optimize_dict_key(&mut entry.key);
580        self.optimize_expression(&mut entry.value);
581    }
582
583    fn optimize_dict_key(&mut self, key: &mut DictKey) {
584        if let DictKey::Expression { expression, .. } = key {
585            self.optimize_expression(expression);
586        }
587    }
588
589    fn optimize_call_argument(&mut self, argument: &mut CallArgument) {
590        match argument {
591            CallArgument::Positional { value, .. } => self.optimize_expression(value),
592            CallArgument::Spread { value, .. } => self.optimize_expression(value),
593            CallArgument::Named { name, value, .. } => {
594                self.optimize_dict_key(name);
595                self.optimize_expression(value);
596            }
597        }
598    }
599
600    fn prune_statement(&self, statement: Statement) -> Option<Vec<Statement>> {
601        match statement {
602            Statement::IfStatement(node) => match literal_boolean(&node.test) {
603                Some(true) => Some(node.consequent.statements),
604                Some(false) => match node.alternate {
605                    Some(alternate) => Some(vec![*alternate]),
606                    None => Some(Vec::new()),
607                },
608                None => Some(vec![Statement::IfStatement(node)]),
609            },
610            Statement::WhileStatement(node) if literal_boolean(&node.test) == Some(false) => {
611                Some(Vec::new())
612            }
613            Statement::PostfixConditionalStatement(node) => {
614                let truth = literal_boolean(&node.test)?;
615                let should_run = if node.keyword == "if" { truth } else { !truth };
616                if should_run {
617                    Some(vec![*node.statement])
618                } else {
619                    Some(Vec::new())
620                }
621            }
622            other => Some(vec![other]),
623        }
624    }
625}
626
627#[derive(Default)]
628struct HereUsage {
629    free: usize,
630    captured: usize,
631    unsupported: bool,
632}
633
634fn inline_simple_chain(expr: &Expression) -> Option<Expression> {
635    let direction = optimized_expression_chain_direction(expr)?;
636    let mut base = None;
637    let mut stages = Vec::new();
638    collect_chain_parts(expr, direction, &mut base, &mut stages)?;
639    let mut current = base?;
640    for stage in stages {
641        let usage = analyse_here_usage(&stage, false);
642        if usage.free != 1 || usage.captured != 0 || usage.unsupported {
643            return None;
644        }
645        current = substitute_here(&stage, &current);
646    }
647    Some(current)
648}
649
650fn collect_chain_parts(
651    expr: &Expression,
652    direction: &'static str,
653    base: &mut Option<Expression>,
654    stages: &mut Vec<Expression>,
655) -> Option<()> {
656    match expr {
657        Expression::Binary {
658            operator,
659            left,
660            right,
661            ..
662        } if optimized_chain_direction(operator) == Some(direction) => {
663            if direction == "right" {
664                collect_chain_parts(left, direction, base, stages)?;
665                stages.push((**right).clone());
666            } else {
667                collect_chain_parts(right, direction, base, stages)?;
668                stages.push((**left).clone());
669            }
670            Some(())
671        }
672        Expression::Binary { operator, .. } if optimized_chain_direction(operator).is_some() => {
673            None
674        }
675        other if base.is_none() => {
676            *base = Some(other.clone());
677            Some(())
678        }
679        _ => None,
680    }
681}
682
683fn optimized_chain_direction(operator: &str) -> Option<&'static str> {
684    match operator {
685        "▷" | "|>" => Some("right"),
686        "◁" | "<|" => Some("left"),
687        _ => None,
688    }
689}
690
691fn optimized_expression_chain_direction(expression: &Expression) -> Option<&'static str> {
692    match expression {
693        Expression::Binary { operator, .. } => optimized_chain_direction(operator),
694        _ => None,
695    }
696}
697
698fn analyse_here_usage(expr: &Expression, in_closure: bool) -> HereUsage {
699    let mut usage = HereUsage::default();
700    collect_here_usage(expr, in_closure, &mut usage);
701    usage
702}
703
704fn collect_here_usage(expr: &Expression, in_closure: bool, usage: &mut HereUsage) {
705    match expr {
706        Expression::Identifier { name, .. } if name == "^^" => {
707            if in_closure {
708                usage.captured += 1;
709            } else {
710                usage.free += 1;
711            }
712        }
713        Expression::Unary { argument, .. } => {
714            collect_here_usage(argument, in_closure, usage);
715        }
716        Expression::Binary {
717            operator,
718            left,
719            right,
720            ..
721        } => {
722            if optimized_chain_direction(operator).is_some() {
723                usage.unsupported = true;
724                return;
725            }
726            collect_here_usage(left, in_closure, usage);
727            collect_here_usage(right, in_closure, usage);
728        }
729        Expression::Ternary {
730            test,
731            consequent,
732            alternate,
733            ..
734        } => {
735            collect_here_usage(test, in_closure, usage);
736            collect_here_usage(consequent, in_closure, usage);
737            collect_here_usage(alternate, in_closure, usage);
738        }
739        Expression::DefinedOr { left, right, .. } => {
740            collect_here_usage(left, in_closure, usage);
741            collect_here_usage(right, in_closure, usage);
742        }
743        Expression::Call {
744            callee, arguments, ..
745        } => {
746            collect_here_usage(callee, in_closure, usage);
747            for argument in arguments {
748                collect_call_argument_here_usage(argument, in_closure, usage);
749            }
750        }
751        Expression::MemberAccess { object, .. } => collect_here_usage(object, in_closure, usage),
752        Expression::Index { object, index, .. } => {
753            collect_here_usage(object, in_closure, usage);
754            collect_here_usage(index, in_closure, usage);
755        }
756        Expression::ArrayLiteral { elements, .. } => {
757            for element in elements {
758                collect_here_usage(element, in_closure, usage);
759            }
760        }
761        Expression::Lambda { body, .. } => collect_here_usage(body, true, usage),
762        Expression::FunctionExpression { body, .. } => {
763            if block_contains_here(body) {
764                usage.captured += 1;
765            }
766        }
767        Expression::Identifier { .. }
768        | Expression::NumberLiteral { .. }
769        | Expression::StringLiteral { .. }
770        | Expression::RegexLiteral { .. }
771        | Expression::BooleanLiteral { .. }
772        | Expression::NullLiteral { .. } => {}
773        _ => usage.unsupported = true,
774    }
775}
776
777fn collect_call_argument_here_usage(
778    argument: &CallArgument,
779    in_closure: bool,
780    usage: &mut HereUsage,
781) {
782    match argument {
783        CallArgument::Positional { value, .. } | CallArgument::Spread { value, .. } => {
784            collect_here_usage(value, in_closure, usage);
785        }
786        CallArgument::Named { value, .. } => collect_here_usage(value, in_closure, usage),
787    }
788}
789
790fn block_contains_here(block: &BlockStatement) -> bool {
791    block.statements.iter().any(statement_contains_here)
792}
793
794fn statement_contains_here(statement: &Statement) -> bool {
795    match statement {
796        Statement::Block(block) => block_contains_here(block),
797        Statement::VariableDeclaration(node) => node
798            .init
799            .as_ref()
800            .map(expression_contains_here)
801            .unwrap_or(false),
802        Statement::VariableUnpackDeclaration(node) => expression_contains_here(&node.init),
803        Statement::IfStatement(node) => {
804            expression_contains_here(&node.test)
805                || block_contains_here(&node.consequent)
806                || node
807                    .alternate
808                    .as_ref()
809                    .map(|alternate| statement_contains_here(alternate))
810                    .unwrap_or(false)
811        }
812        Statement::WhileStatement(node) => {
813            expression_contains_here(&node.test) || block_contains_here(&node.body)
814        }
815        Statement::ForStatement(node) => {
816            expression_contains_here(&node.iterable) || block_contains_here(&node.body)
817        }
818        Statement::ReturnStatement(node) => node
819            .argument
820            .as_ref()
821            .map(expression_contains_here)
822            .unwrap_or(false),
823        Statement::ThrowStatement(node) => expression_contains_here(&node.argument),
824        Statement::DieStatement(node) => expression_contains_here(&node.argument),
825        Statement::PostfixConditionalStatement(node) => {
826            statement_contains_here(&node.statement) || expression_contains_here(&node.test)
827        }
828        Statement::KeywordStatement(node) => node.arguments.iter().any(expression_contains_here),
829        Statement::ExpressionStatement(node) => expression_contains_here(&node.expression),
830        _ => false,
831    }
832}
833
834fn expression_contains_here(expr: &Expression) -> bool {
835    let usage = analyse_here_usage(expr, false);
836    usage.free > 0 || usage.captured > 0
837}
838
839fn substitute_here(expr: &Expression, replacement: &Expression) -> Expression {
840    match expr {
841        Expression::Identifier { name, .. } if name == "^^" => replacement.clone(),
842        Expression::Unary {
843            line,
844            source_file,
845            operator,
846            argument,
847            traits,
848            inferred_type,
849        } => Expression::Unary {
850            line: *line,
851            source_file: source_file.clone(),
852            operator: operator.clone(),
853            argument: Box::new(substitute_here(argument, replacement)),
854            traits: traits.clone(),
855            inferred_type: inferred_type.clone(),
856        },
857        Expression::Binary {
858            line,
859            source_file,
860            operator,
861            left,
862            right,
863            inferred_type,
864        } => Expression::Binary {
865            line: *line,
866            source_file: source_file.clone(),
867            operator: operator.clone(),
868            left: Box::new(substitute_here(left, replacement)),
869            right: Box::new(substitute_here(right, replacement)),
870            inferred_type: inferred_type.clone(),
871        },
872        Expression::Ternary {
873            line,
874            source_file,
875            test,
876            consequent,
877            alternate,
878            inferred_type,
879        } => Expression::Ternary {
880            line: *line,
881            source_file: source_file.clone(),
882            test: Box::new(substitute_here(test, replacement)),
883            consequent: Box::new(substitute_here(consequent, replacement)),
884            alternate: Box::new(substitute_here(alternate, replacement)),
885            inferred_type: inferred_type.clone(),
886        },
887        Expression::DefinedOr {
888            line,
889            source_file,
890            left,
891            right,
892            inferred_type,
893        } => Expression::DefinedOr {
894            line: *line,
895            source_file: source_file.clone(),
896            left: Box::new(substitute_here(left, replacement)),
897            right: Box::new(substitute_here(right, replacement)),
898            inferred_type: inferred_type.clone(),
899        },
900        Expression::Call {
901            line,
902            source_file,
903            callee,
904            arguments,
905            inferred_type,
906        } => Expression::Call {
907            line: *line,
908            source_file: source_file.clone(),
909            callee: Box::new(substitute_here(callee, replacement)),
910            arguments: arguments
911                .iter()
912                .map(|argument| substitute_call_argument(argument, replacement))
913                .collect(),
914            inferred_type: inferred_type.clone(),
915        },
916        Expression::MemberAccess {
917            line,
918            source_file,
919            object,
920            member,
921            inferred_type,
922        } => Expression::MemberAccess {
923            line: *line,
924            source_file: source_file.clone(),
925            object: Box::new(substitute_here(object, replacement)),
926            member: member.clone(),
927            inferred_type: inferred_type.clone(),
928        },
929        Expression::Index {
930            line,
931            source_file,
932            object,
933            index,
934            inferred_type,
935        } => Expression::Index {
936            line: *line,
937            source_file: source_file.clone(),
938            object: Box::new(substitute_here(object, replacement)),
939            index: Box::new(substitute_here(index, replacement)),
940            inferred_type: inferred_type.clone(),
941        },
942        Expression::ArrayLiteral {
943            line,
944            source_file,
945            elements,
946            capacity_hint,
947            inferred_type,
948        } => Expression::ArrayLiteral {
949            line: *line,
950            source_file: source_file.clone(),
951            elements: elements
952                .iter()
953                .map(|element| substitute_here(element, replacement))
954                .collect(),
955            capacity_hint: *capacity_hint,
956            inferred_type: inferred_type.clone(),
957        },
958        other => other.clone(),
959    }
960}
961
962fn substitute_call_argument(argument: &CallArgument, replacement: &Expression) -> CallArgument {
963    match argument {
964        CallArgument::Positional {
965            line,
966            source_file,
967            value,
968        } => CallArgument::Positional {
969            line: *line,
970            source_file: source_file.clone(),
971            value: substitute_here(value, replacement),
972        },
973        CallArgument::Spread {
974            line,
975            source_file,
976            value,
977        } => CallArgument::Spread {
978            line: *line,
979            source_file: source_file.clone(),
980            value: substitute_here(value, replacement),
981        },
982        CallArgument::Named {
983            line,
984            source_file,
985            name,
986            value,
987        } => CallArgument::Named {
988            line: *line,
989            source_file: source_file.clone(),
990            name: name.clone(),
991            value: substitute_here(value, replacement),
992        },
993    }
994}
995
996fn lower_range_array_loop(statement: &Statement) -> Option<Statement> {
997    let Statement::ForStatement(node) = statement else {
998        return None;
999    };
1000    let (start, end) = range_array_expression(&node.iterable)?;
1001    if !is_simple_range_endpoint(start) || !is_simple_range_endpoint(end) {
1002        return None;
1003    }
1004
1005    let prefix = format!("__zuzu_range_{}_{}", node.line, node.variable);
1006    let current_name = format!("{prefix}_current");
1007    let end_name = format!("{prefix}_end");
1008    let step_name = format!("{prefix}_step");
1009    let seen_name = format!("{prefix}_seen");
1010    let generated = [
1011        current_name.as_str(),
1012        end_name.as_str(),
1013        step_name.as_str(),
1014        seen_name.as_str(),
1015    ];
1016    let mut identifiers = HashSet::new();
1017    collect_statement_identifiers(statement, &mut identifiers);
1018    if generated.iter().any(|name| identifiers.contains(*name)) {
1019        return None;
1020    }
1021
1022    let line = node.line;
1023    let source_file = node.source_file.clone();
1024    let mut statements = vec![
1025        variable_statement(
1026            line,
1027            source_file.clone(),
1028            "let",
1029            &current_name,
1030            start.clone(),
1031        ),
1032        variable_statement(line, source_file.clone(), "let", &end_name, end.clone()),
1033        variable_statement(
1034            line,
1035            source_file.clone(),
1036            "let",
1037            &step_name,
1038            Expression::Ternary {
1039                line,
1040                source_file: source_file.clone(),
1041                test: Box::new(binary_expression(
1042                    line,
1043                    source_file.clone(),
1044                    "≤",
1045                    identifier_expression(line, source_file.clone(), &current_name),
1046                    identifier_expression(line, source_file.clone(), &end_name),
1047                )),
1048                consequent: Box::new(number_expression(line, source_file.clone(), "1")),
1049                alternate: Box::new(number_expression(line, source_file.clone(), "-1")),
1050                inferred_type: None,
1051            },
1052        ),
1053    ];
1054
1055    if node.else_block.is_some() {
1056        statements.push(variable_statement(
1057            line,
1058            source_file.clone(),
1059            "let",
1060            &seen_name,
1061            Expression::BooleanLiteral {
1062                line,
1063                source_file: source_file.clone(),
1064                value: false,
1065                inferred_type: None,
1066            },
1067        ));
1068    }
1069
1070    let mut body_statements = Vec::new();
1071    if node.else_block.is_some() {
1072        body_statements.push(expression_statement(
1073            line,
1074            source_file.clone(),
1075            assignment_expression(
1076                line,
1077                source_file.clone(),
1078                identifier_expression(line, source_file.clone(), &seen_name),
1079                Expression::BooleanLiteral {
1080                    line,
1081                    source_file: source_file.clone(),
1082                    value: true,
1083                    inferred_type: None,
1084                },
1085            ),
1086        ));
1087    }
1088    body_statements.push(variable_statement(
1089        line,
1090        source_file.clone(),
1091        node.binding_kind.as_deref().unwrap_or("let"),
1092        &node.variable,
1093        identifier_expression(line, source_file.clone(), &current_name),
1094    ));
1095    body_statements.push(expression_statement(
1096        line,
1097        source_file.clone(),
1098        Expression::Assignment {
1099            line,
1100            source_file: source_file.clone(),
1101            operator: "+=".to_owned(),
1102            left: Box::new(identifier_expression(
1103                line,
1104                source_file.clone(),
1105                &current_name,
1106            )),
1107            right: Box::new(identifier_expression(line, source_file.clone(), &step_name)),
1108            is_weak_write: false,
1109            inferred_type: None,
1110            runtime_typecheck_required: None,
1111        },
1112    ));
1113    body_statements.extend(node.body.statements.clone());
1114
1115    statements.push(Statement::WhileStatement(WhileStatement {
1116        line,
1117        source_file: source_file.clone(),
1118        test: range_loop_test(
1119            line,
1120            source_file.clone(),
1121            &current_name,
1122            &end_name,
1123            &step_name,
1124        ),
1125        body: BlockStatement {
1126            line,
1127            source_file: source_file.clone(),
1128            statements: body_statements,
1129            needs_lexical_scope: true,
1130        },
1131    }));
1132
1133    if let Some(else_block) = &node.else_block {
1134        statements.push(Statement::IfStatement(IfStatement {
1135            line,
1136            source_file: source_file.clone(),
1137            test: Expression::Unary {
1138                line,
1139                source_file: source_file.clone(),
1140                operator: "¬".to_owned(),
1141                argument: Box::new(identifier_expression(line, source_file.clone(), &seen_name)),
1142                traits: Vec::new(),
1143                inferred_type: None,
1144            },
1145            consequent: else_block.clone(),
1146            alternate: None,
1147        }));
1148    }
1149
1150    Some(Statement::Block(BlockStatement {
1151        line,
1152        source_file,
1153        statements,
1154        needs_lexical_scope: true,
1155    }))
1156}
1157
1158fn regex_cache_key(pattern: &str, flags: &str) -> String {
1159    format!("{}:{pattern}:{flags}", pattern.len())
1160}
1161
1162fn collection_element_capacity_hint(elements: &[Expression]) -> usize {
1163    elements
1164        .iter()
1165        .map(|element| range_literal_len(element).unwrap_or(1))
1166        .sum()
1167}
1168
1169fn range_literal_len(expression: &Expression) -> Option<usize> {
1170    let Expression::Binary {
1171        operator,
1172        left,
1173        right,
1174        ..
1175    } = expression
1176    else {
1177        return None;
1178    };
1179    if operator != "..." {
1180        return None;
1181    }
1182    let start = literal_number(left)? as i64;
1183    let end = literal_number(right)? as i64;
1184    Some(start.abs_diff(end) as usize + 1)
1185}
1186
1187fn range_array_expression(iterable: &Expression) -> Option<(&Expression, &Expression)> {
1188    let Expression::ArrayLiteral { elements, .. } = iterable else {
1189        return None;
1190    };
1191    let [Expression::Binary {
1192        operator,
1193        left,
1194        right,
1195        ..
1196    }] = elements.as_slice()
1197    else {
1198        return None;
1199    };
1200    if operator != "..." {
1201        return None;
1202    }
1203    Some((left, right))
1204}
1205
1206fn is_simple_range_endpoint(expression: &Expression) -> bool {
1207    matches!(
1208        expression,
1209        Expression::NumberLiteral { .. } | Expression::Identifier { .. }
1210    )
1211}
1212
1213fn range_loop_test(
1214    line: usize,
1215    source_file: Option<String>,
1216    current_name: &str,
1217    end_name: &str,
1218    step_name: &str,
1219) -> Expression {
1220    binary_expression(
1221        line,
1222        source_file.clone(),
1223        "⋁",
1224        binary_expression(
1225            line,
1226            source_file.clone(),
1227            "⋀",
1228            binary_expression(
1229                line,
1230                source_file.clone(),
1231                ">",
1232                identifier_expression(line, source_file.clone(), step_name),
1233                number_expression(line, source_file.clone(), "0"),
1234            ),
1235            binary_expression(
1236                line,
1237                source_file.clone(),
1238                "≤",
1239                identifier_expression(line, source_file.clone(), current_name),
1240                identifier_expression(line, source_file.clone(), end_name),
1241            ),
1242        ),
1243        binary_expression(
1244            line,
1245            source_file.clone(),
1246            "⋀",
1247            binary_expression(
1248                line,
1249                source_file.clone(),
1250                "<",
1251                identifier_expression(line, source_file.clone(), step_name),
1252                number_expression(line, source_file.clone(), "0"),
1253            ),
1254            binary_expression(
1255                line,
1256                source_file.clone(),
1257                "≥",
1258                identifier_expression(line, source_file.clone(), current_name),
1259                identifier_expression(line, source_file, end_name),
1260            ),
1261        ),
1262    )
1263}
1264
1265fn variable_statement(
1266    line: usize,
1267    source_file: Option<String>,
1268    kind: &str,
1269    name: &str,
1270    init: Expression,
1271) -> Statement {
1272    Statement::VariableDeclaration(VariableDeclaration {
1273        line,
1274        source_file,
1275        kind: kind.to_owned(),
1276        declared_type: None,
1277        name: name.to_owned(),
1278        init: Some(init),
1279        is_weak_storage: false,
1280        runtime_typecheck_required: None,
1281    })
1282}
1283
1284fn expression_statement(
1285    line: usize,
1286    source_file: Option<String>,
1287    expression: Expression,
1288) -> Statement {
1289    Statement::ExpressionStatement(ExpressionStatement {
1290        line,
1291        source_file,
1292        expression,
1293    })
1294}
1295
1296fn assignment_expression(
1297    line: usize,
1298    source_file: Option<String>,
1299    left: Expression,
1300    right: Expression,
1301) -> Expression {
1302    Expression::Assignment {
1303        line,
1304        source_file,
1305        operator: ":=".to_owned(),
1306        left: Box::new(left),
1307        right: Box::new(right),
1308        is_weak_write: false,
1309        inferred_type: None,
1310        runtime_typecheck_required: None,
1311    }
1312}
1313
1314fn binary_expression(
1315    line: usize,
1316    source_file: Option<String>,
1317    operator: &str,
1318    left: Expression,
1319    right: Expression,
1320) -> Expression {
1321    Expression::Binary {
1322        line,
1323        source_file,
1324        operator: operator.to_owned(),
1325        left: Box::new(left),
1326        right: Box::new(right),
1327        inferred_type: None,
1328    }
1329}
1330
1331fn identifier_expression(line: usize, source_file: Option<String>, name: &str) -> Expression {
1332    Expression::Identifier {
1333        line,
1334        source_file,
1335        name: name.to_owned(),
1336        inferred_type: None,
1337        binding_depth: None,
1338    }
1339}
1340
1341fn number_expression(line: usize, source_file: Option<String>, value: &str) -> Expression {
1342    Expression::NumberLiteral {
1343        line,
1344        source_file,
1345        value: value.to_owned(),
1346        inferred_type: None,
1347    }
1348}
1349
1350fn collect_statement_identifiers(statement: &Statement, names: &mut HashSet<String>) {
1351    match statement {
1352        Statement::Block(block) => collect_block_identifiers(block, names),
1353        Statement::VariableDeclaration(node) => {
1354            names.insert(node.name.clone());
1355            if let Some(init) = &node.init {
1356                collect_expression_identifiers(init, names);
1357            }
1358        }
1359        Statement::VariableUnpackDeclaration(node) => {
1360            collect_expression_identifiers(&node.init, names);
1361            for entry in node.pattern.entries() {
1362                names.insert(entry.name.clone());
1363                collect_dict_key_identifiers(&entry.key, names);
1364                if let Some(default_value) = &entry.default_value {
1365                    collect_expression_identifiers(default_value, names);
1366                }
1367            }
1368        }
1369        Statement::FunctionDeclaration(node) => {
1370            names.insert(node.name.clone());
1371            for param in &node.params {
1372                names.insert(param.name.clone());
1373                if let Some(default_value) = &param.default_value {
1374                    collect_expression_identifiers(default_value, names);
1375                }
1376            }
1377            collect_block_identifiers(&node.body, names);
1378        }
1379        Statement::ClassDeclaration(node) => {
1380            names.insert(node.name.clone());
1381            for member in &node.body {
1382                collect_class_member_identifiers(member, names);
1383            }
1384        }
1385        Statement::TraitDeclaration(node) => {
1386            names.insert(node.name.clone());
1387            for member in &node.body {
1388                collect_class_member_identifiers(member, names);
1389            }
1390        }
1391        Statement::ImportDeclaration(node) => {
1392            for specifier in &node.specifiers {
1393                names.insert(specifier.local.clone());
1394            }
1395            if let Some(condition) = &node.condition {
1396                collect_expression_identifiers(&condition.test, names);
1397            }
1398        }
1399        Statement::IfStatement(node) => {
1400            collect_expression_identifiers(&node.test, names);
1401            collect_block_identifiers(&node.consequent, names);
1402            if let Some(alternate) = &node.alternate {
1403                collect_statement_identifiers(alternate, names);
1404            }
1405        }
1406        Statement::WhileStatement(node) => {
1407            collect_expression_identifiers(&node.test, names);
1408            collect_block_identifiers(&node.body, names);
1409        }
1410        Statement::ForStatement(node) => {
1411            names.insert(node.variable.clone());
1412            collect_expression_identifiers(&node.iterable, names);
1413            collect_block_identifiers(&node.body, names);
1414            if let Some(else_block) = &node.else_block {
1415                collect_block_identifiers(else_block, names);
1416            }
1417        }
1418        Statement::SwitchStatement(node) => {
1419            collect_expression_identifiers(&node.discriminant, names);
1420            for case in &node.cases {
1421                for value in &case.values {
1422                    collect_expression_identifiers(value, names);
1423                }
1424                for statement in &case.consequent {
1425                    collect_statement_identifiers(statement, names);
1426                }
1427            }
1428            if let Some(default) = &node.default {
1429                for statement in default {
1430                    collect_statement_identifiers(statement, names);
1431                }
1432            }
1433        }
1434        Statement::TryStatement(node) => {
1435            collect_block_identifiers(&node.body, names);
1436            for handler in &node.handlers {
1437                if let Some(name) = handler
1438                    .binding
1439                    .as_ref()
1440                    .and_then(|binding| binding.name.as_ref())
1441                {
1442                    names.insert(name.clone());
1443                }
1444                collect_block_identifiers(&handler.body, names);
1445            }
1446        }
1447        Statement::ReturnStatement(node) => {
1448            if let Some(argument) = &node.argument {
1449                collect_expression_identifiers(argument, names);
1450            }
1451        }
1452        Statement::ThrowStatement(node) => collect_expression_identifiers(&node.argument, names),
1453        Statement::DieStatement(node) => collect_expression_identifiers(&node.argument, names),
1454        Statement::PostfixConditionalStatement(node) => {
1455            collect_statement_identifiers(&node.statement, names);
1456            collect_expression_identifiers(&node.test, names);
1457        }
1458        Statement::KeywordStatement(node) => {
1459            for argument in &node.arguments {
1460                collect_expression_identifiers(argument, names);
1461            }
1462        }
1463        Statement::ExpressionStatement(node) => {
1464            collect_expression_identifiers(&node.expression, names);
1465        }
1466        Statement::LoopControlStatement(_) => {}
1467    }
1468}
1469
1470fn collect_block_identifiers(block: &BlockStatement, names: &mut HashSet<String>) {
1471    for statement in &block.statements {
1472        collect_statement_identifiers(statement, names);
1473    }
1474}
1475
1476fn collect_class_member_identifiers(member: &ClassMember, names: &mut HashSet<String>) {
1477    match member {
1478        ClassMember::Field(field) => {
1479            names.insert(field.name.clone());
1480            if let Some(default_value) = &field.default_value {
1481                collect_expression_identifiers(default_value, names);
1482            }
1483        }
1484        ClassMember::Method(method) => {
1485            names.insert(method.name.clone());
1486            for param in &method.params {
1487                names.insert(param.name.clone());
1488                if let Some(default_value) = &param.default_value {
1489                    collect_expression_identifiers(default_value, names);
1490                }
1491            }
1492            collect_block_identifiers(&method.body, names);
1493        }
1494        ClassMember::Class(class) => {
1495            names.insert(class.name.clone());
1496            for member in &class.body {
1497                collect_class_member_identifiers(member, names);
1498            }
1499        }
1500        ClassMember::Trait(trait_decl) => {
1501            names.insert(trait_decl.name.clone());
1502            for member in &trait_decl.body {
1503                collect_class_member_identifiers(member, names);
1504            }
1505        }
1506    }
1507}
1508
1509fn collect_expression_identifiers(expression: &Expression, names: &mut HashSet<String>) {
1510    match expression {
1511        Expression::Identifier { name, .. } => {
1512            names.insert(name.clone());
1513        }
1514        Expression::Unary { argument, .. } => collect_expression_identifiers(argument, names),
1515        Expression::Binary { left, right, .. }
1516        | Expression::DefinedOr { left, right, .. }
1517        | Expression::Assignment { left, right, .. } => {
1518            collect_expression_identifiers(left, names);
1519            collect_expression_identifiers(right, names);
1520        }
1521        Expression::Ternary {
1522            test,
1523            consequent,
1524            alternate,
1525            ..
1526        } => {
1527            collect_expression_identifiers(test, names);
1528            collect_expression_identifiers(consequent, names);
1529            collect_expression_identifiers(alternate, names);
1530        }
1531        Expression::Call {
1532            callee, arguments, ..
1533        } => {
1534            collect_expression_identifiers(callee, names);
1535            for argument in arguments {
1536                collect_call_argument_identifiers(argument, names);
1537            }
1538        }
1539        Expression::MemberAccess { object, .. } => {
1540            collect_expression_identifiers(object, names);
1541        }
1542        Expression::DynamicMemberCall {
1543            object,
1544            member,
1545            arguments,
1546            ..
1547        } => {
1548            collect_expression_identifiers(object, names);
1549            collect_expression_identifiers(member, names);
1550            for argument in arguments {
1551                collect_call_argument_identifiers(argument, names);
1552            }
1553        }
1554        Expression::Index { object, index, .. } => {
1555            collect_expression_identifiers(object, names);
1556            collect_expression_identifiers(index, names);
1557        }
1558        Expression::Slice {
1559            object, start, end, ..
1560        } => {
1561            collect_expression_identifiers(object, names);
1562            if let Some(start) = start {
1563                collect_expression_identifiers(start, names);
1564            }
1565            if let Some(end) = end {
1566                collect_expression_identifiers(end, names);
1567            }
1568        }
1569        Expression::DictAccess { object, key, .. } => {
1570            collect_expression_identifiers(object, names);
1571            collect_dict_key_identifiers(key, names);
1572        }
1573        Expression::PostfixUpdate { argument, .. } => {
1574            collect_expression_identifiers(argument, names);
1575        }
1576        Expression::Lambda { params, body, .. } => {
1577            for param in params {
1578                names.insert(param.name.clone());
1579                if let Some(default_value) = &param.default_value {
1580                    collect_expression_identifiers(default_value, names);
1581                }
1582            }
1583            collect_expression_identifiers(body, names);
1584        }
1585        Expression::FunctionExpression { params, body, .. } => {
1586            for param in params {
1587                names.insert(param.name.clone());
1588                if let Some(default_value) = &param.default_value {
1589                    collect_expression_identifiers(default_value, names);
1590                }
1591            }
1592            collect_block_identifiers(body, names);
1593        }
1594        Expression::LetExpression { name, init, .. } => {
1595            names.insert(name.clone());
1596            if let Some(init) = init {
1597                collect_expression_identifiers(init, names);
1598            }
1599        }
1600        Expression::TryExpression { body, handlers, .. } => {
1601            collect_block_identifiers(body, names);
1602            for handler in handlers {
1603                if let Some(name) = handler
1604                    .binding
1605                    .as_ref()
1606                    .and_then(|binding| binding.name.as_ref())
1607                {
1608                    names.insert(name.clone());
1609                }
1610                collect_block_identifiers(&handler.body, names);
1611            }
1612        }
1613        Expression::DoExpression { body, .. }
1614        | Expression::AwaitExpression { body, .. }
1615        | Expression::SpawnExpression { body, .. } => collect_block_identifiers(body, names),
1616        Expression::ArrayLiteral { elements, .. }
1617        | Expression::SetLiteral { elements, .. }
1618        | Expression::BagLiteral { elements, .. } => {
1619            for element in elements {
1620                collect_expression_identifiers(element, names);
1621            }
1622        }
1623        Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
1624            for entry in entries {
1625                collect_dict_key_identifiers(&entry.key, names);
1626                collect_expression_identifiers(&entry.value, names);
1627            }
1628        }
1629        Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
1630            for part in parts {
1631                if let TemplatePart::Expression { expression, .. } = part {
1632                    collect_expression_identifiers(expression, names);
1633                }
1634            }
1635        }
1636        Expression::SuperCall { arguments, .. } => {
1637            for argument in arguments {
1638                collect_call_argument_identifiers(argument, names);
1639            }
1640        }
1641        Expression::NumberLiteral { .. }
1642        | Expression::StringLiteral { .. }
1643        | Expression::BinaryStringLiteral { .. }
1644        | Expression::BooleanLiteral { .. }
1645        | Expression::NullLiteral { .. } => {}
1646    }
1647}
1648
1649fn collect_call_argument_identifiers(argument: &CallArgument, names: &mut HashSet<String>) {
1650    match argument {
1651        CallArgument::Positional { value, .. } => collect_expression_identifiers(value, names),
1652        CallArgument::Spread { value, .. } => collect_expression_identifiers(value, names),
1653        CallArgument::Named { name, value, .. } => {
1654            collect_dict_key_identifiers(name, names);
1655            collect_expression_identifiers(value, names);
1656        }
1657    }
1658}
1659
1660fn collect_dict_key_identifiers(key: &DictKey, names: &mut HashSet<String>) {
1661    if let DictKey::Expression { expression, .. } = key {
1662        collect_expression_identifiers(expression, names);
1663    }
1664}
1665
1666fn block_needs_lexical_scope(block: &BlockStatement) -> bool {
1667    block.statements.iter().any(statement_needs_lexical_scope)
1668}
1669
1670fn statement_needs_lexical_scope(statement: &Statement) -> bool {
1671    match statement {
1672        Statement::VariableDeclaration(_)
1673        | Statement::VariableUnpackDeclaration(_)
1674        | Statement::FunctionDeclaration(_)
1675        | Statement::ClassDeclaration(_)
1676        | Statement::TraitDeclaration(_)
1677        | Statement::ImportDeclaration(_) => true,
1678        Statement::ExpressionStatement(node) => expression_declares_binding(&node.expression),
1679        Statement::KeywordStatement(node) => node.arguments.iter().any(expression_declares_binding),
1680        Statement::ReturnStatement(node) => node
1681            .argument
1682            .as_ref()
1683            .map(expression_declares_binding)
1684            .unwrap_or(false),
1685        Statement::ThrowStatement(node) => expression_declares_binding(&node.argument),
1686        Statement::DieStatement(node) => expression_declares_binding(&node.argument),
1687        _ => false,
1688    }
1689}
1690
1691fn expression_declares_binding(expr: &Expression) -> bool {
1692    match expr {
1693        Expression::LetExpression { .. } => true,
1694        Expression::Unary { argument, .. } => expression_declares_binding(argument),
1695        Expression::Binary { left, right, .. }
1696        | Expression::DefinedOr { left, right, .. }
1697        | Expression::Assignment { left, right, .. } => {
1698            expression_declares_binding(left) || expression_declares_binding(right)
1699        }
1700        Expression::Ternary {
1701            test,
1702            consequent,
1703            alternate,
1704            ..
1705        } => {
1706            expression_declares_binding(test)
1707                || expression_declares_binding(consequent)
1708                || expression_declares_binding(alternate)
1709        }
1710        Expression::Call {
1711            callee, arguments, ..
1712        } => {
1713            expression_declares_binding(callee)
1714                || arguments.iter().any(call_argument_declares_binding)
1715        }
1716        Expression::DynamicMemberCall {
1717            object,
1718            member,
1719            arguments,
1720            ..
1721        } => {
1722            expression_declares_binding(object)
1723                || expression_declares_binding(member)
1724                || arguments.iter().any(call_argument_declares_binding)
1725        }
1726        Expression::MemberAccess { object, .. } => expression_declares_binding(object),
1727        Expression::Index { object, index, .. } => {
1728            expression_declares_binding(object) || expression_declares_binding(index)
1729        }
1730        Expression::Slice {
1731            object, start, end, ..
1732        } => {
1733            expression_declares_binding(object)
1734                || start
1735                    .as_ref()
1736                    .map(|expr| expression_declares_binding(expr))
1737                    .unwrap_or(false)
1738                || end
1739                    .as_ref()
1740                    .map(|expr| expression_declares_binding(expr))
1741                    .unwrap_or(false)
1742        }
1743        Expression::DictAccess { object, key, .. } => {
1744            expression_declares_binding(object) || dict_key_declares_binding(key)
1745        }
1746        Expression::PostfixUpdate { argument, .. } => expression_declares_binding(argument),
1747        Expression::Lambda { body, .. } => expression_declares_binding(body),
1748        Expression::FunctionExpression { body, .. }
1749        | Expression::TryExpression { body, .. }
1750        | Expression::DoExpression { body, .. }
1751        | Expression::AwaitExpression { body, .. }
1752        | Expression::SpawnExpression { body, .. } => block_needs_lexical_scope(body),
1753        Expression::ArrayLiteral { elements, .. }
1754        | Expression::SetLiteral { elements, .. }
1755        | Expression::BagLiteral { elements, .. } => {
1756            elements.iter().any(expression_declares_binding)
1757        }
1758        Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
1759            entries.iter().any(|entry| {
1760                dict_key_declares_binding(&entry.key) || expression_declares_binding(&entry.value)
1761            })
1762        }
1763        Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
1764            parts.iter().any(|part| match part {
1765                TemplatePart::Expression { expression, .. } => {
1766                    expression_declares_binding(expression)
1767                }
1768                TemplatePart::Text { .. } => false,
1769            })
1770        }
1771        Expression::SuperCall { arguments, .. } => {
1772            arguments.iter().any(call_argument_declares_binding)
1773        }
1774        Expression::Identifier { .. }
1775        | Expression::NumberLiteral { .. }
1776        | Expression::StringLiteral { .. }
1777        | Expression::BinaryStringLiteral { .. }
1778        | Expression::BooleanLiteral { .. }
1779        | Expression::NullLiteral { .. } => false,
1780    }
1781}
1782
1783fn call_argument_declares_binding(argument: &CallArgument) -> bool {
1784    match argument {
1785        CallArgument::Positional { value, .. } => expression_declares_binding(value),
1786        CallArgument::Spread { value, .. } => expression_declares_binding(value),
1787        CallArgument::Named { name, value, .. } => {
1788            dict_key_declares_binding(name) || expression_declares_binding(value)
1789        }
1790    }
1791}
1792
1793fn dict_key_declares_binding(key: &DictKey) -> bool {
1794    match key {
1795        DictKey::Expression { expression, .. } => expression_declares_binding(expression),
1796        DictKey::Identifier { .. } | DictKey::StringLiteral { .. } => false,
1797    }
1798}
1799
1800fn is_terminal_statement(statement: &Statement) -> bool {
1801    matches!(
1802        statement,
1803        Statement::ReturnStatement(_)
1804            | Statement::ThrowStatement(_)
1805            | Statement::DieStatement(_)
1806            | Statement::LoopControlStatement(_)
1807    )
1808}
1809
1810fn fold_expression(expr: &Expression) -> Option<Expression> {
1811    match expr {
1812        Expression::Unary {
1813            line,
1814            source_file,
1815            operator,
1816            argument,
1817            traits: _,
1818            inferred_type,
1819        } => fold_unary(
1820            *line,
1821            source_file.clone(),
1822            operator,
1823            argument,
1824            inferred_type.clone(),
1825        ),
1826        Expression::Binary {
1827            line,
1828            source_file,
1829            operator,
1830            left,
1831            right,
1832            inferred_type,
1833        } => fold_binary(
1834            *line,
1835            source_file.clone(),
1836            operator,
1837            left,
1838            right,
1839            inferred_type.clone(),
1840        ),
1841        Expression::Ternary {
1842            test,
1843            consequent,
1844            alternate,
1845            ..
1846        } => match literal_truth(test)? {
1847            true => Some((**consequent).clone()),
1848            false => Some((**alternate).clone()),
1849        },
1850        Expression::TemplateLiteral {
1851            line,
1852            source_file,
1853            parts,
1854            inferred_type,
1855        } => {
1856            let mut text = String::new();
1857            for part in parts {
1858                match part {
1859                    TemplatePart::Text { value, .. } => text.push_str(value),
1860                    TemplatePart::Expression { expression, .. } => {
1861                        text.push_str(&literal_render(expression)?);
1862                    }
1863                }
1864            }
1865            Some(Expression::StringLiteral {
1866                line: *line,
1867                source_file: source_file.clone(),
1868                value: text,
1869                inferred_type: inferred_type.clone(),
1870            })
1871        }
1872        _ => None,
1873    }
1874}
1875
1876fn fold_unary(
1877    line: usize,
1878    source_file: Option<String>,
1879    operator: &str,
1880    argument: &Expression,
1881    inferred_type: Option<String>,
1882) -> Option<Expression> {
1883    match preferred_operator(operator) {
1884        "+" => Some(number_literal(
1885            line,
1886            source_file,
1887            literal_number(argument)?,
1888            inferred_type,
1889        )),
1890        "-" => Some(number_literal(
1891            line,
1892            source_file,
1893            -literal_number(argument)?,
1894            inferred_type,
1895        )),
1896        "¬" | "!" => Some(Expression::BooleanLiteral {
1897            line,
1898            source_file,
1899            value: !literal_truth(argument)?,
1900            inferred_type,
1901        }),
1902        "√" => Some(number_literal(
1903            line,
1904            source_file,
1905            literal_number(argument)?.sqrt(),
1906            inferred_type,
1907        )),
1908        "abs" => Some(number_literal(
1909            line,
1910            source_file,
1911            literal_number(argument)?.abs(),
1912            inferred_type,
1913        )),
1914        "floor" => Some(number_literal(
1915            line,
1916            source_file,
1917            literal_number(argument)?.floor(),
1918            inferred_type,
1919        )),
1920        "ceil" => Some(number_literal(
1921            line,
1922            source_file,
1923            literal_number(argument)?.ceil(),
1924            inferred_type,
1925        )),
1926        "round" => Some(number_literal(
1927            line,
1928            source_file,
1929            literal_number(argument)?.round(),
1930            inferred_type,
1931        )),
1932        "int" => Some(number_literal(
1933            line,
1934            source_file,
1935            literal_number(argument)?.trunc(),
1936            inferred_type,
1937        )),
1938        "uc" => Some(Expression::StringLiteral {
1939            line,
1940            source_file,
1941            value: literal_string(argument)?.to_uppercase(),
1942            inferred_type,
1943        }),
1944        "lc" => Some(Expression::StringLiteral {
1945            line,
1946            source_file,
1947            value: literal_string(argument)?.to_lowercase(),
1948            inferred_type,
1949        }),
1950        "length" => Some(number_literal(
1951            line,
1952            source_file,
1953            literal_string(argument)?.chars().count() as f64,
1954            inferred_type,
1955        )),
1956        _ => None,
1957    }
1958}
1959
1960fn fold_binary(
1961    line: usize,
1962    source_file: Option<String>,
1963    operator: &str,
1964    left: &Expression,
1965    right: &Expression,
1966    inferred_type: Option<String>,
1967) -> Option<Expression> {
1968    let operator = preferred_operator(operator);
1969    match operator {
1970        "+" | "-" | "×" | "÷" | "mod" | "**" => {
1971            let lhs = literal_number(left)?;
1972            let rhs = literal_number(right)?;
1973            let value = match operator {
1974                "+" => lhs + rhs,
1975                "-" => lhs - rhs,
1976                "×" => lhs * rhs,
1977                "÷" => lhs / rhs,
1978                "mod" => lhs % rhs,
1979                "**" => lhs.powf(rhs),
1980                _ => unreachable!(),
1981            };
1982            Some(number_literal(line, source_file, value, inferred_type))
1983        }
1984        "_" => Some(Expression::StringLiteral {
1985            line,
1986            source_file,
1987            value: format!("{}{}", literal_render(left)?, literal_render(right)?),
1988            inferred_type,
1989        }),
1990        "⋀" => Some(Expression::BooleanLiteral {
1991            line,
1992            source_file,
1993            value: literal_truth(left)? && literal_truth(right)?,
1994            inferred_type,
1995        }),
1996        "⋁" => Some(Expression::BooleanLiteral {
1997            line,
1998            source_file,
1999            value: literal_truth(left)? || literal_truth(right)?,
2000            inferred_type,
2001        }),
2002        "⊻" => Some(Expression::BooleanLiteral {
2003            line,
2004            source_file,
2005            value: literal_truth(left)? ^ literal_truth(right)?,
2006            inferred_type,
2007        }),
2008        "⊼" => Some(Expression::BooleanLiteral {
2009            line,
2010            source_file,
2011            value: !(literal_truth(left)? && literal_truth(right)?),
2012            inferred_type,
2013        }),
2014        "=" | "≠" | "<" | ">" | "≤" | "≥" => {
2015            let lhs = literal_number(left)?;
2016            let rhs = literal_number(right)?;
2017            let value = match operator {
2018                "=" => lhs == rhs,
2019                "≠" => lhs != rhs,
2020                "<" => lhs < rhs,
2021                ">" => lhs > rhs,
2022                "≤" => lhs <= rhs,
2023                "≥" => lhs >= rhs,
2024                _ => unreachable!(),
2025            };
2026            Some(Expression::BooleanLiteral {
2027                line,
2028                source_file,
2029                value,
2030                inferred_type,
2031            })
2032        }
2033        "eq" | "ne" | "gt" | "ge" | "lt" | "le" | "eqi" | "nei" | "gti" | "gei" | "lti" | "lei" => {
2034            let mut lhs = literal_render(left)?;
2035            let mut rhs = literal_render(right)?;
2036            if matches!(operator, "eqi" | "nei" | "gti" | "gei" | "lti" | "lei") {
2037                lhs = lhs.to_lowercase();
2038                rhs = rhs.to_lowercase();
2039            }
2040            let value = match operator {
2041                "eq" | "eqi" => lhs == rhs,
2042                "ne" | "nei" => lhs != rhs,
2043                "gt" | "gti" => lhs > rhs,
2044                "ge" | "gei" => lhs >= rhs,
2045                "lt" | "lti" => lhs < rhs,
2046                "le" | "lei" => lhs <= rhs,
2047                _ => unreachable!(),
2048            };
2049            Some(Expression::BooleanLiteral {
2050                line,
2051                source_file,
2052                value,
2053                inferred_type,
2054            })
2055        }
2056        "≡" | "≢" => {
2057            let value = literal_eq(left, right)?;
2058            Some(Expression::BooleanLiteral {
2059                line,
2060                source_file,
2061                value: if operator == "≡" { value } else { !value },
2062                inferred_type,
2063            })
2064        }
2065        "..." => None,
2066        _ => None,
2067    }
2068}
2069
2070fn number_literal(
2071    line: usize,
2072    source_file: Option<String>,
2073    value: f64,
2074    inferred_type: Option<String>,
2075) -> Expression {
2076    Expression::NumberLiteral {
2077        line,
2078        source_file,
2079        value: if value.fract() == 0.0 {
2080            format!("{value:.0}")
2081        } else {
2082            value.to_string()
2083        },
2084        inferred_type,
2085    }
2086}
2087
2088fn literal_number(expr: &Expression) -> Option<f64> {
2089    match expr {
2090        Expression::NumberLiteral { value, .. } => value.parse().ok(),
2091        _ => None,
2092    }
2093}
2094
2095fn literal_string(expr: &Expression) -> Option<&str> {
2096    match expr {
2097        Expression::StringLiteral { value, .. } => Some(value),
2098        _ => None,
2099    }
2100}
2101
2102fn literal_truth(expr: &Expression) -> Option<bool> {
2103    match expr {
2104        Expression::BooleanLiteral { value, .. } => Some(*value),
2105        Expression::NullLiteral { .. } => Some(false),
2106        Expression::NumberLiteral { value, .. } => Some(value.parse::<f64>().ok()? != 0.0),
2107        Expression::StringLiteral { value, .. } => Some(!value.is_empty()),
2108        _ => None,
2109    }
2110}
2111
2112fn literal_boolean(expr: &Expression) -> Option<bool> {
2113    match expr {
2114        Expression::BooleanLiteral { value, .. } => Some(*value),
2115        _ => None,
2116    }
2117}
2118
2119fn literal_render(expr: &Expression) -> Option<String> {
2120    match expr {
2121        Expression::StringLiteral { value, .. } => Some(value.clone()),
2122        Expression::NumberLiteral { value, .. } => Some(value.clone()),
2123        Expression::BooleanLiteral { value, .. } => {
2124            Some(if *value { "true" } else { "false" }.to_owned())
2125        }
2126        Expression::NullLiteral { .. } => Some(String::new()),
2127        _ => None,
2128    }
2129}
2130
2131fn literal_eq(left: &Expression, right: &Expression) -> Option<bool> {
2132    match (left, right) {
2133        (Expression::NullLiteral { .. }, Expression::NullLiteral { .. }) => Some(true),
2134        (
2135            Expression::BooleanLiteral { value: lhs, .. },
2136            Expression::BooleanLiteral { value: rhs, .. },
2137        ) => Some(lhs == rhs),
2138        (
2139            Expression::NumberLiteral { value: lhs, .. },
2140            Expression::NumberLiteral { value: rhs, .. },
2141        ) => Some(lhs.parse::<f64>().ok()? == rhs.parse::<f64>().ok()?),
2142        (
2143            Expression::StringLiteral { value: lhs, .. },
2144            Expression::StringLiteral { value: rhs, .. },
2145        ) => Some(lhs == rhs),
2146        _ => None,
2147    }
2148}
2149
2150pub fn preferred_operator(operator: &str) -> &str {
2151    match operator {
2152        "sqrt" => "√",
2153        "*" => "×",
2154        "/" => "÷",
2155        "<=" => "≤",
2156        ">=" => "≥",
2157        "<=>" | "≷" => "≶",
2158        "==" => "≡",
2159        "!=" => "≢",
2160        "not" => "¬",
2161        "and" => "⋀",
2162        "and?" => "⋀?",
2163        "nand" => "⊼",
2164        "nand?" => "⊼?",
2165        "nor" => "⊽",
2166        "nor?" => "⊽?",
2167        "xor" => "⊻",
2168        "xor?" => "⊻?",
2169        "xnor" => "↔",
2170        "xnor?" => "↔?",
2171        "or" => "⋁",
2172        "or?" => "⋁?",
2173        "onlyif" => "⊨",
2174        "onlyif?" => "⊨?",
2175        "butnot" => "⊭",
2176        "butnot?" => "⊭?",
2177        "union" => "⋃",
2178        "intersection" => "⋂",
2179        "\\" => "∖",
2180        "in" => "∈",
2181        "subsetof" => "⊂",
2182        "supersetof" => "⊃",
2183        "equivalentof" => "⊂⊃",
2184        "|>" => "▷",
2185        "<|" => "◁",
2186        "*=" => "×=",
2187        "/=" => "÷=",
2188        other => other,
2189    }
2190}
2191
2192fn build_switch_index(node: &SwitchStatement) -> Option<Vec<SwitchIndexEntry>> {
2193    let comparator = node.comparator.as_deref().map(preferred_operator);
2194    let mut entries = Vec::new();
2195    let mut seen = HashSet::new();
2196    for (case_index, case) in node.cases.iter().enumerate() {
2197        for (value_index, value) in case.values.iter().enumerate() {
2198            let operator = case
2199                .operators
2200                .get(value_index)
2201                .and_then(|value| value.as_deref())
2202                .map(preferred_operator)
2203                .or(comparator);
2204            let key = switch_key(operator, value)?;
2205            if !seen.insert(key.clone()) {
2206                return None;
2207            }
2208            entries.push(SwitchIndexEntry { key, case_index });
2209        }
2210    }
2211    if entries.is_empty() {
2212        None
2213    } else {
2214        Some(entries)
2215    }
2216}
2217
2218fn switch_key(operator: Option<&str>, expr: &Expression) -> Option<String> {
2219    match operator {
2220        None | Some("≡") => switch_strict_key(expr),
2221        Some("=") => literal_integer(expr).map(|value| format!("i:{value}")),
2222        Some("eq") => match expr {
2223            Expression::StringLiteral { value, .. } => Some(format!("q:{value}")),
2224            _ => None,
2225        },
2226        Some("eqi") => match expr {
2227            Expression::StringLiteral { value, .. } => Some(format!("qi:{}", value.to_lowercase())),
2228            _ => None,
2229        },
2230        _ => None,
2231    }
2232}
2233
2234fn switch_strict_key(expr: &Expression) -> Option<String> {
2235    match expr {
2236        Expression::NullLiteral { .. } => Some("n:".to_owned()),
2237        Expression::BooleanLiteral { value, .. } => Some(format!("b:{value}")),
2238        Expression::NumberLiteral { value, .. } => {
2239            Some(format!("f:{}", value.parse::<f64>().ok()?))
2240        }
2241        Expression::StringLiteral { value, .. } => Some(format!("s:{value}")),
2242        _ => None,
2243    }
2244}
2245
2246fn literal_integer(expr: &Expression) -> Option<i64> {
2247    match expr {
2248        Expression::NumberLiteral { value, .. } => {
2249            let value = value.parse::<f64>().ok()?;
2250            if value.is_finite() && value.fract() == 0.0 {
2251                Some(value as i64)
2252            } else {
2253                None
2254            }
2255        }
2256        _ => None,
2257    }
2258}
2259
2260fn propagate_constants(statements: &mut [Statement]) {
2261    let mut scopes = vec![HashMap::<String, Expression>::new()];
2262    propagate_constants_statements(statements, &mut scopes);
2263}
2264
2265fn propagate_constants_statements(
2266    statements: &mut [Statement],
2267    scopes: &mut Vec<HashMap<String, Expression>>,
2268) {
2269    for statement in statements {
2270        propagate_constants_statement(statement, scopes);
2271        record_constant_declaration(statement, scopes);
2272    }
2273}
2274
2275fn propagate_constants_statement(
2276    statement: &mut Statement,
2277    scopes: &mut Vec<HashMap<String, Expression>>,
2278) {
2279    match statement {
2280        Statement::Block(block) => propagate_constants_block(block, scopes),
2281        Statement::VariableDeclaration(node) => {
2282            if let Some(init) = &mut node.init {
2283                propagate_constants_expression(init, scopes, false);
2284            }
2285        }
2286        Statement::VariableUnpackDeclaration(node) => {
2287            propagate_constants_expression(&mut node.init, scopes, false);
2288            for entry in node.pattern.entries_mut() {
2289                propagate_constants_dict_key(&mut entry.key, scopes);
2290                if let Some(default_value) = &mut entry.default_value {
2291                    propagate_constants_expression(default_value, scopes, false);
2292                }
2293            }
2294        }
2295        Statement::FunctionDeclaration(node) => {
2296            forget_assigned_in_block(scopes, &node.body);
2297        }
2298        Statement::ClassDeclaration(node) => {
2299            for member in &mut node.body {
2300                propagate_constants_class_member(member);
2301            }
2302        }
2303        Statement::TraitDeclaration(node) => {
2304            for member in &mut node.body {
2305                propagate_constants_class_member(member);
2306            }
2307        }
2308        Statement::ImportDeclaration(node) => {
2309            if let Some(condition) = &mut node.condition {
2310                propagate_constants_expression(&mut condition.test, scopes, false);
2311            }
2312        }
2313        Statement::IfStatement(node) => {
2314            propagate_constants_expression(&mut node.test, scopes, false);
2315            let mut consequent_scopes = scopes.clone();
2316            propagate_constants_block(&mut node.consequent, &mut consequent_scopes);
2317            if let Some(alternate) = &mut node.alternate {
2318                let mut alternate_scopes = scopes.clone();
2319                propagate_constants_statement(alternate, &mut alternate_scopes);
2320            }
2321            forget_assigned_in_block(scopes, &node.consequent);
2322            if let Some(alternate) = &node.alternate {
2323                forget_assigned_in_statement(scopes, alternate);
2324            }
2325        }
2326        Statement::WhileStatement(node) => {
2327            let mut body_scopes = scopes.clone();
2328            forget_assigned_in_block(&mut body_scopes, &node.body);
2329            propagate_constants_expression(&mut node.test, &mut body_scopes, false);
2330            propagate_constants_block(&mut node.body, &mut body_scopes);
2331            forget_assigned_in_block(scopes, &node.body);
2332        }
2333        Statement::ForStatement(node) => {
2334            propagate_constants_expression(&mut node.iterable, scopes, false);
2335            let mut body_scopes = scopes.clone();
2336            body_scopes.push(HashMap::new());
2337            forget_assigned_in_block(&mut body_scopes, &node.body);
2338            forget_constant(&mut body_scopes, &node.variable);
2339            propagate_constants_statements(&mut node.body.statements, &mut body_scopes);
2340            if let Some(else_block) = &mut node.else_block {
2341                let mut else_scopes = scopes.clone();
2342                propagate_constants_block(else_block, &mut else_scopes);
2343            }
2344            forget_assigned_in_block(scopes, &node.body);
2345            if let Some(else_block) = &node.else_block {
2346                forget_assigned_in_block(scopes, else_block);
2347            }
2348        }
2349        Statement::SwitchStatement(node) => {
2350            propagate_constants_expression(&mut node.discriminant, scopes, false);
2351            let mut case_assigned_scopes = scopes.clone();
2352            for case in &node.cases {
2353                forget_assigned_in_statements(&mut case_assigned_scopes, &case.consequent);
2354            }
2355            if let Some(default) = &node.default {
2356                forget_assigned_in_statements(&mut case_assigned_scopes, default);
2357            }
2358            for case in &mut node.cases {
2359                for value in &mut case.values {
2360                    propagate_constants_expression(value, scopes, false);
2361                }
2362                let mut case_scopes = case_assigned_scopes.clone();
2363                case_scopes.push(HashMap::new());
2364                propagate_constants_statements(&mut case.consequent, &mut case_scopes);
2365            }
2366            if let Some(default) = &mut node.default {
2367                let mut default_scopes = case_assigned_scopes.clone();
2368                default_scopes.push(HashMap::new());
2369                propagate_constants_statements(default, &mut default_scopes);
2370            }
2371            for case in &node.cases {
2372                forget_assigned_in_statements(scopes, &case.consequent);
2373            }
2374            if let Some(default) = &node.default {
2375                forget_assigned_in_statements(scopes, default);
2376            }
2377        }
2378        Statement::TryStatement(node) => {
2379            let mut body_scopes = scopes.clone();
2380            propagate_constants_block(&mut node.body, &mut body_scopes);
2381            for handler in &mut node.handlers {
2382                let mut handler_scopes = scopes.clone();
2383                handler_scopes.push(HashMap::new());
2384                if let Some(name) = handler
2385                    .binding
2386                    .as_ref()
2387                    .and_then(|binding| binding.name.as_ref())
2388                {
2389                    forget_constant(&mut handler_scopes, name);
2390                }
2391                propagate_constants_statements(&mut handler.body.statements, &mut handler_scopes);
2392            }
2393            forget_assigned_in_block(scopes, &node.body);
2394            for handler in &node.handlers {
2395                forget_assigned_in_block(scopes, &handler.body);
2396            }
2397        }
2398        Statement::ReturnStatement(node) => {
2399            if let Some(argument) = &mut node.argument {
2400                propagate_constants_expression(argument, scopes, false);
2401            }
2402        }
2403        Statement::ThrowStatement(node) => {
2404            propagate_constants_expression(&mut node.argument, scopes, false);
2405        }
2406        Statement::DieStatement(node) => {
2407            propagate_constants_expression(&mut node.argument, scopes, false);
2408        }
2409        Statement::PostfixConditionalStatement(node) => {
2410            propagate_constants_statement(&mut node.statement, scopes);
2411            propagate_constants_expression(&mut node.test, scopes, false);
2412        }
2413        Statement::KeywordStatement(node) => {
2414            for argument in &mut node.arguments {
2415                propagate_constants_expression(argument, scopes, false);
2416            }
2417        }
2418        Statement::ExpressionStatement(node) => {
2419            propagate_constants_expression(&mut node.expression, scopes, false);
2420        }
2421        Statement::LoopControlStatement(_) => {}
2422    }
2423}
2424
2425fn propagate_constants_block(
2426    block: &mut BlockStatement,
2427    scopes: &mut Vec<HashMap<String, Expression>>,
2428) {
2429    if block.needs_lexical_scope {
2430        scopes.push(HashMap::new());
2431        propagate_constants_statements(&mut block.statements, scopes);
2432        scopes.pop();
2433    } else {
2434        propagate_constants_statements(&mut block.statements, scopes);
2435    }
2436}
2437
2438fn propagate_constants_class_member(member: &mut ClassMember) {
2439    match member {
2440        ClassMember::Field(field) => {
2441            if let Some(default_value) = &mut field.default_value {
2442                let mut scopes = vec![HashMap::new()];
2443                propagate_constants_expression(default_value, &mut scopes, false);
2444            }
2445        }
2446        ClassMember::Method(method) => {
2447            let _ = method;
2448        }
2449        ClassMember::Class(class) => {
2450            for member in &mut class.body {
2451                propagate_constants_class_member(member);
2452            }
2453        }
2454        ClassMember::Trait(trait_decl) => {
2455            for member in &mut trait_decl.body {
2456                propagate_constants_class_member(member);
2457            }
2458        }
2459    }
2460}
2461
2462fn record_constant_declaration(
2463    statement: &Statement,
2464    scopes: &mut Vec<HashMap<String, Expression>>,
2465) {
2466    match statement {
2467        Statement::VariableDeclaration(node) => {
2468            if let Some(init) = node.init.as_ref().and_then(literal_constant) {
2469                if let Some(scope) = scopes.last_mut() {
2470                    scope.insert(node.name.clone(), init);
2471                }
2472            } else {
2473                forget_constant(scopes, &node.name);
2474            }
2475        }
2476        Statement::VariableUnpackDeclaration(node) => {
2477            for entry in node.pattern.entries() {
2478                forget_constant(scopes, &entry.name);
2479            }
2480        }
2481        Statement::FunctionDeclaration(node) => forget_constant(scopes, &node.name),
2482        Statement::ClassDeclaration(node) => forget_constant(scopes, &node.name),
2483        Statement::TraitDeclaration(node) => forget_constant(scopes, &node.name),
2484        Statement::ImportDeclaration(node) => {
2485            for specifier in &node.specifiers {
2486                forget_constant(scopes, &specifier.local);
2487            }
2488        }
2489        _ => {}
2490    }
2491}
2492
2493fn propagate_constants_expression(
2494    expr: &mut Expression,
2495    scopes: &mut Vec<HashMap<String, Expression>>,
2496    lvalue_context: bool,
2497) {
2498    match expr {
2499        Expression::Identifier {
2500            line,
2501            source_file,
2502            name,
2503            inferred_type,
2504            ..
2505        } if !lvalue_context => {
2506            if let Some(value) = lookup_constant(scopes, name) {
2507                *expr = constant_for_use(&value, *line, source_file.clone(), inferred_type.clone());
2508            }
2509        }
2510        Expression::Unary {
2511            operator, argument, ..
2512        } if operator == "\\" => {
2513            propagate_constants_expression(argument, scopes, true);
2514        }
2515        Expression::Unary {
2516            operator, argument, ..
2517        } if matches!(operator.as_str(), "++" | "--") => {
2518            propagate_constants_expression(argument, scopes, true);
2519            forget_lvalue_constants(scopes, argument);
2520        }
2521        Expression::Unary { argument, .. } => {
2522            propagate_constants_expression(argument, scopes, false);
2523        }
2524        Expression::Binary { left, right, .. } | Expression::DefinedOr { left, right, .. } => {
2525            propagate_constants_expression(left, scopes, false);
2526            propagate_constants_expression(right, scopes, false);
2527        }
2528        Expression::Assignment {
2529            operator,
2530            left,
2531            right,
2532            ..
2533        } => {
2534            propagate_constants_expression(left, scopes, true);
2535            if operator == "~=" {
2536                propagate_regex_replacement_pattern(right, scopes);
2537            } else {
2538                propagate_constants_expression(right, scopes, false);
2539            }
2540            forget_lvalue_constants(scopes, left);
2541        }
2542        Expression::Ternary {
2543            test,
2544            consequent,
2545            alternate,
2546            ..
2547        } => {
2548            propagate_constants_expression(test, scopes, false);
2549            let mut consequent_scopes = scopes.clone();
2550            propagate_constants_expression(consequent, &mut consequent_scopes, false);
2551            let mut alternate_scopes = scopes.clone();
2552            propagate_constants_expression(alternate, &mut alternate_scopes, false);
2553        }
2554        Expression::Call {
2555            callee, arguments, ..
2556        } => {
2557            let mutates_caller_scope = call_may_mutate_caller_scope(callee);
2558            propagate_constants_expression(callee, scopes, false);
2559            for argument in arguments {
2560                propagate_constants_call_argument(argument, scopes);
2561            }
2562            if mutates_caller_scope {
2563                clear_constants(scopes);
2564            }
2565        }
2566        Expression::MemberAccess { object, .. } => {
2567            propagate_constants_expression(object, scopes, lvalue_context);
2568        }
2569        Expression::DynamicMemberCall {
2570            object,
2571            member,
2572            arguments,
2573            ..
2574        } => {
2575            propagate_constants_expression(object, scopes, false);
2576            propagate_constants_expression(member, scopes, false);
2577            for argument in arguments {
2578                propagate_constants_call_argument(argument, scopes);
2579            }
2580        }
2581        Expression::Index { object, index, .. } => {
2582            propagate_constants_expression(object, scopes, lvalue_context);
2583            propagate_constants_expression(index, scopes, false);
2584        }
2585        Expression::Slice {
2586            object, start, end, ..
2587        } => {
2588            propagate_constants_expression(object, scopes, lvalue_context);
2589            if let Some(start) = start {
2590                propagate_constants_expression(start, scopes, false);
2591            }
2592            if let Some(end) = end {
2593                propagate_constants_expression(end, scopes, false);
2594            }
2595        }
2596        Expression::DictAccess { object, key, .. } => {
2597            propagate_constants_expression(object, scopes, lvalue_context);
2598            propagate_constants_dict_key(key, scopes);
2599        }
2600        Expression::PostfixUpdate { argument, .. } => {
2601            propagate_constants_expression(argument, scopes, true);
2602            forget_lvalue_constants(scopes, argument);
2603        }
2604        Expression::Lambda { body, .. } => {
2605            forget_assigned_in_expression(scopes, body);
2606        }
2607        Expression::FunctionExpression { body, .. } => {
2608            forget_assigned_in_block(scopes, body);
2609        }
2610        Expression::LetExpression { name, init, .. } => {
2611            if let Some(init) = init {
2612                propagate_constants_expression(init, scopes, false);
2613                if let Some(value) = literal_constant(init) {
2614                    if let Some(scope) = scopes.last_mut() {
2615                        scope.insert(name.clone(), value);
2616                    }
2617                } else {
2618                    forget_constant(scopes, name);
2619                }
2620            } else {
2621                forget_constant(scopes, name);
2622            }
2623        }
2624        Expression::TryExpression { body, handlers, .. } => {
2625            let mut body_scopes = scopes.clone();
2626            propagate_constants_block(body, &mut body_scopes);
2627            for handler in handlers {
2628                let mut handler_scopes = scopes.clone();
2629                handler_scopes.push(HashMap::new());
2630                if let Some(name) = handler
2631                    .binding
2632                    .as_ref()
2633                    .and_then(|binding| binding.name.as_ref())
2634                {
2635                    forget_constant(&mut handler_scopes, name);
2636                }
2637                propagate_constants_statements(&mut handler.body.statements, &mut handler_scopes);
2638            }
2639        }
2640        Expression::DoExpression { body, .. }
2641        | Expression::AwaitExpression { body, .. }
2642        | Expression::SpawnExpression { body, .. } => {
2643            let mut body_scopes = scopes.clone();
2644            propagate_constants_block(body, &mut body_scopes);
2645            forget_assigned_in_block(scopes, body);
2646        }
2647        Expression::ArrayLiteral { elements, .. }
2648        | Expression::SetLiteral { elements, .. }
2649        | Expression::BagLiteral { elements, .. } => {
2650            for element in elements {
2651                propagate_constants_expression(element, scopes, false);
2652            }
2653        }
2654        Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
2655            for entry in entries {
2656                propagate_constants_dict_key(&mut entry.key, scopes);
2657                propagate_constants_expression(&mut entry.value, scopes, false);
2658            }
2659        }
2660        Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
2661            for part in parts {
2662                if let TemplatePart::Expression { expression, .. } = part {
2663                    propagate_constants_expression(expression, scopes, false);
2664                }
2665            }
2666        }
2667        Expression::SuperCall { arguments, .. } => {
2668            for argument in arguments {
2669                propagate_constants_call_argument(argument, scopes);
2670            }
2671        }
2672        Expression::Identifier { .. }
2673        | Expression::NumberLiteral { .. }
2674        | Expression::StringLiteral { .. }
2675        | Expression::BinaryStringLiteral { .. }
2676        | Expression::BooleanLiteral { .. }
2677        | Expression::NullLiteral { .. } => {}
2678    }
2679    if !lvalue_context {
2680        if let Some(folded) = fold_expression(expr) {
2681            *expr = folded;
2682        }
2683    }
2684}
2685
2686fn propagate_constants_call_argument(
2687    argument: &mut CallArgument,
2688    scopes: &mut Vec<HashMap<String, Expression>>,
2689) {
2690    match argument {
2691        CallArgument::Positional { value, .. } => {
2692            propagate_constants_expression(value, scopes, false);
2693        }
2694        CallArgument::Spread { value, .. } => {
2695            propagate_constants_expression(value, scopes, false);
2696        }
2697        CallArgument::Named { name, value, .. } => {
2698            propagate_constants_dict_key(name, scopes);
2699            propagate_constants_expression(value, scopes, false);
2700        }
2701    }
2702}
2703
2704fn call_may_mutate_caller_scope(callee: &Expression) -> bool {
2705    matches!(callee, Expression::Identifier { name, .. } if name == "eval")
2706}
2707
2708fn clear_constants(scopes: &mut [HashMap<String, Expression>]) {
2709    for scope in scopes {
2710        scope.clear();
2711    }
2712}
2713
2714fn propagate_regex_replacement_pattern(
2715    replacement: &mut Expression,
2716    scopes: &mut Vec<HashMap<String, Expression>>,
2717) {
2718    if let Expression::Binary { operator, left, .. } = replacement {
2719        if operator == "->" {
2720            propagate_constants_expression(left, scopes, false);
2721        }
2722    }
2723}
2724
2725fn propagate_constants_dict_key(key: &mut DictKey, scopes: &mut Vec<HashMap<String, Expression>>) {
2726    if let DictKey::Expression { expression, .. } = key {
2727        propagate_constants_expression(expression, scopes, false);
2728    }
2729}
2730
2731fn lookup_constant(scopes: &[HashMap<String, Expression>], name: &str) -> Option<Expression> {
2732    for scope in scopes.iter().rev() {
2733        if let Some(value) = scope.get(name) {
2734            return Some(value.clone());
2735        }
2736    }
2737    None
2738}
2739
2740fn forget_constant(scopes: &mut [HashMap<String, Expression>], name: &str) {
2741    for scope in scopes.iter_mut().rev() {
2742        if scope.remove(name).is_some() {
2743            return;
2744        }
2745    }
2746}
2747
2748fn forget_lvalue_constants(scopes: &mut [HashMap<String, Expression>], target: &Expression) {
2749    match target {
2750        Expression::Identifier { name, .. } => forget_constant(scopes, name),
2751        Expression::Binary { left, .. } => forget_lvalue_constants(scopes, left),
2752        Expression::MemberAccess { object, .. }
2753        | Expression::Index { object, .. }
2754        | Expression::Slice { object, .. }
2755        | Expression::DictAccess { object, .. } => forget_lvalue_constants(scopes, object),
2756        _ => {}
2757    }
2758}
2759
2760fn forget_assigned_in_block(scopes: &mut [HashMap<String, Expression>], block: &BlockStatement) {
2761    forget_assigned_in_statements(scopes, &block.statements);
2762}
2763
2764fn forget_assigned_in_statements(
2765    scopes: &mut [HashMap<String, Expression>],
2766    statements: &[Statement],
2767) {
2768    for statement in statements {
2769        forget_assigned_in_statement(scopes, statement);
2770    }
2771}
2772
2773fn forget_assigned_in_statement(scopes: &mut [HashMap<String, Expression>], statement: &Statement) {
2774    match statement {
2775        Statement::Block(block) => forget_assigned_in_block(scopes, block),
2776        Statement::VariableDeclaration(_) => {}
2777        Statement::VariableUnpackDeclaration(_) => {}
2778        Statement::FunctionDeclaration(_) => {}
2779        Statement::ClassDeclaration(_) => {}
2780        Statement::TraitDeclaration(_) => {}
2781        Statement::ImportDeclaration(_) => {}
2782        Statement::IfStatement(node) => {
2783            forget_assigned_in_block(scopes, &node.consequent);
2784            if let Some(alternate) = &node.alternate {
2785                forget_assigned_in_statement(scopes, alternate);
2786            }
2787        }
2788        Statement::WhileStatement(node) => forget_assigned_in_block(scopes, &node.body),
2789        Statement::ForStatement(node) => {
2790            forget_assigned_in_block(scopes, &node.body);
2791            if let Some(else_block) = &node.else_block {
2792                forget_assigned_in_block(scopes, else_block);
2793            }
2794        }
2795        Statement::SwitchStatement(node) => {
2796            for case in &node.cases {
2797                forget_assigned_in_statements(scopes, &case.consequent);
2798            }
2799            if let Some(default) = &node.default {
2800                forget_assigned_in_statements(scopes, default);
2801            }
2802        }
2803        Statement::TryStatement(node) => {
2804            forget_assigned_in_block(scopes, &node.body);
2805            for handler in &node.handlers {
2806                forget_assigned_in_block(scopes, &handler.body);
2807            }
2808        }
2809        Statement::ReturnStatement(node) => {
2810            if let Some(argument) = &node.argument {
2811                forget_assigned_in_expression(scopes, argument);
2812            }
2813        }
2814        Statement::ThrowStatement(node) => forget_assigned_in_expression(scopes, &node.argument),
2815        Statement::DieStatement(node) => forget_assigned_in_expression(scopes, &node.argument),
2816        Statement::PostfixConditionalStatement(node) => {
2817            forget_assigned_in_statement(scopes, &node.statement);
2818            forget_assigned_in_expression(scopes, &node.test);
2819        }
2820        Statement::KeywordStatement(node) => {
2821            for argument in &node.arguments {
2822                forget_assigned_in_expression(scopes, argument);
2823            }
2824        }
2825        Statement::ExpressionStatement(node) => {
2826            forget_assigned_in_expression(scopes, &node.expression);
2827        }
2828        Statement::LoopControlStatement(_) => {}
2829    }
2830}
2831
2832fn forget_assigned_in_expression(scopes: &mut [HashMap<String, Expression>], expr: &Expression) {
2833    match expr {
2834        Expression::Unary {
2835            operator, argument, ..
2836        } if matches!(operator.as_str(), "++" | "--") => {
2837            forget_lvalue_constants(scopes, argument);
2838        }
2839        Expression::Unary { argument, .. } => forget_assigned_in_expression(scopes, argument),
2840        Expression::Binary { left, right, .. } | Expression::DefinedOr { left, right, .. } => {
2841            forget_assigned_in_expression(scopes, left);
2842            forget_assigned_in_expression(scopes, right);
2843        }
2844        Expression::Assignment { left, right, .. } => {
2845            forget_lvalue_constants(scopes, left);
2846            forget_assigned_in_expression(scopes, right);
2847        }
2848        Expression::Ternary {
2849            test,
2850            consequent,
2851            alternate,
2852            ..
2853        } => {
2854            forget_assigned_in_expression(scopes, test);
2855            forget_assigned_in_expression(scopes, consequent);
2856            forget_assigned_in_expression(scopes, alternate);
2857        }
2858        Expression::Call {
2859            callee, arguments, ..
2860        } => {
2861            forget_assigned_in_expression(scopes, callee);
2862            for argument in arguments {
2863                forget_assigned_in_call_argument(scopes, argument);
2864            }
2865        }
2866        Expression::MemberAccess { object, .. } => forget_assigned_in_expression(scopes, object),
2867        Expression::DynamicMemberCall {
2868            object,
2869            member,
2870            arguments,
2871            ..
2872        } => {
2873            forget_assigned_in_expression(scopes, object);
2874            forget_assigned_in_expression(scopes, member);
2875            for argument in arguments {
2876                forget_assigned_in_call_argument(scopes, argument);
2877            }
2878        }
2879        Expression::Index { object, index, .. } => {
2880            forget_assigned_in_expression(scopes, object);
2881            forget_assigned_in_expression(scopes, index);
2882        }
2883        Expression::Slice {
2884            object, start, end, ..
2885        } => {
2886            forget_assigned_in_expression(scopes, object);
2887            if let Some(start) = start {
2888                forget_assigned_in_expression(scopes, start);
2889            }
2890            if let Some(end) = end {
2891                forget_assigned_in_expression(scopes, end);
2892            }
2893        }
2894        Expression::DictAccess { object, key, .. } => {
2895            forget_assigned_in_expression(scopes, object);
2896            forget_assigned_in_dict_key(scopes, key);
2897        }
2898        Expression::PostfixUpdate { argument, .. } => forget_lvalue_constants(scopes, argument),
2899        Expression::LetExpression { init, .. } => {
2900            if let Some(init) = init {
2901                forget_assigned_in_expression(scopes, init);
2902            }
2903        }
2904        Expression::TryExpression { body, handlers, .. } => {
2905            forget_assigned_in_block(scopes, body);
2906            for handler in handlers {
2907                forget_assigned_in_block(scopes, &handler.body);
2908            }
2909        }
2910        Expression::DoExpression { body, .. }
2911        | Expression::AwaitExpression { body, .. }
2912        | Expression::SpawnExpression { body, .. } => forget_assigned_in_block(scopes, body),
2913        Expression::ArrayLiteral { elements, .. }
2914        | Expression::SetLiteral { elements, .. }
2915        | Expression::BagLiteral { elements, .. } => {
2916            for element in elements {
2917                forget_assigned_in_expression(scopes, element);
2918            }
2919        }
2920        Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
2921            for entry in entries {
2922                forget_assigned_in_dict_key(scopes, &entry.key);
2923                forget_assigned_in_expression(scopes, &entry.value);
2924            }
2925        }
2926        Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
2927            for part in parts {
2928                if let TemplatePart::Expression { expression, .. } = part {
2929                    forget_assigned_in_expression(scopes, expression);
2930                }
2931            }
2932        }
2933        Expression::SuperCall { arguments, .. } => {
2934            for argument in arguments {
2935                forget_assigned_in_call_argument(scopes, argument);
2936            }
2937        }
2938        Expression::Lambda { .. }
2939        | Expression::FunctionExpression { .. }
2940        | Expression::Identifier { .. }
2941        | Expression::NumberLiteral { .. }
2942        | Expression::StringLiteral { .. }
2943        | Expression::BinaryStringLiteral { .. }
2944        | Expression::BooleanLiteral { .. }
2945        | Expression::NullLiteral { .. } => {}
2946    }
2947}
2948
2949fn forget_assigned_in_call_argument(
2950    scopes: &mut [HashMap<String, Expression>],
2951    argument: &CallArgument,
2952) {
2953    match argument {
2954        CallArgument::Positional { value, .. } => forget_assigned_in_expression(scopes, value),
2955        CallArgument::Spread { value, .. } => forget_assigned_in_expression(scopes, value),
2956        CallArgument::Named { name, value, .. } => {
2957            forget_assigned_in_dict_key(scopes, name);
2958            forget_assigned_in_expression(scopes, value);
2959        }
2960    }
2961}
2962
2963fn forget_assigned_in_dict_key(scopes: &mut [HashMap<String, Expression>], key: &DictKey) {
2964    if let DictKey::Expression { expression, .. } = key {
2965        forget_assigned_in_expression(scopes, expression);
2966    }
2967}
2968
2969fn literal_constant(expr: &Expression) -> Option<Expression> {
2970    match expr {
2971        Expression::NumberLiteral { .. }
2972        | Expression::StringLiteral { .. }
2973        | Expression::BooleanLiteral { .. }
2974        | Expression::NullLiteral { .. } => Some(expr.clone()),
2975        _ => None,
2976    }
2977}
2978
2979fn constant_for_use(
2980    value: &Expression,
2981    line: usize,
2982    source_file: Option<String>,
2983    inferred_type: Option<String>,
2984) -> Expression {
2985    match value {
2986        Expression::NumberLiteral { value, .. } => Expression::NumberLiteral {
2987            line,
2988            source_file,
2989            value: value.clone(),
2990            inferred_type,
2991        },
2992        Expression::StringLiteral { value, .. } => Expression::StringLiteral {
2993            line,
2994            source_file,
2995            value: value.clone(),
2996            inferred_type,
2997        },
2998        Expression::BooleanLiteral { value, .. } => Expression::BooleanLiteral {
2999            line,
3000            source_file,
3001            value: *value,
3002            inferred_type,
3003        },
3004        Expression::NullLiteral { .. } => Expression::NullLiteral {
3005            line,
3006            source_file,
3007            inferred_type,
3008        },
3009        other => other.clone(),
3010    }
3011}
3012
3013fn annotate_identifier_depths(program: &mut Program) {
3014    let mut scopes = vec![root_scope()];
3015    annotate_statements(&mut program.statements, &mut scopes);
3016}
3017
3018fn root_scope() -> HashSet<String> {
3019    [
3020        "Exception",
3021        "AssertionException",
3022        "TypeException",
3023        "CancelledException",
3024        "TimeoutException",
3025        "ChannelClosedException",
3026        "Array",
3027        "Dict",
3028        "PairList",
3029        "Set",
3030        "Bag",
3031        "Pair",
3032        "String",
3033        "BinaryString",
3034        "Number",
3035        "Boolean",
3036        "Regexp",
3037        "Function",
3038        "__file__",
3039        "__global__",
3040        "__system__",
3041    ]
3042    .into_iter()
3043    .map(str::to_owned)
3044    .collect()
3045}
3046
3047fn annotate_statements(statements: &mut [Statement], scopes: &mut Vec<HashSet<String>>) {
3048    for statement in statements {
3049        annotate_statement(statement, scopes);
3050        declare_statement_binding(statement, scopes);
3051    }
3052}
3053
3054fn annotate_statement(statement: &mut Statement, scopes: &mut Vec<HashSet<String>>) {
3055    match statement {
3056        Statement::Block(block) => {
3057            annotate_block(block, scopes);
3058        }
3059        Statement::VariableDeclaration(node) => {
3060            if let Some(init) = &mut node.init {
3061                annotate_expression(init, scopes);
3062            }
3063        }
3064        Statement::VariableUnpackDeclaration(node) => {
3065            annotate_expression(&mut node.init, scopes);
3066            for entry in node.pattern.entries_mut() {
3067                annotate_dict_key(&mut entry.key, scopes);
3068                if let Some(default_value) = &mut entry.default_value {
3069                    annotate_expression(default_value, scopes);
3070                }
3071            }
3072        }
3073        Statement::FunctionDeclaration(node) => {
3074            scopes.push(function_scope(&node.params));
3075            annotate_statements(&mut node.body.statements, scopes);
3076            scopes.pop();
3077        }
3078        Statement::ClassDeclaration(node) => {
3079            for member in &mut node.body {
3080                annotate_class_member(member, scopes);
3081            }
3082        }
3083        Statement::TraitDeclaration(node) => {
3084            for member in &mut node.body {
3085                annotate_class_member(member, scopes);
3086            }
3087        }
3088        Statement::ImportDeclaration(node) => {
3089            if let Some(condition) = &mut node.condition {
3090                annotate_expression(&mut condition.test, scopes);
3091            }
3092        }
3093        Statement::IfStatement(node) => {
3094            annotate_expression(&mut node.test, scopes);
3095            annotate_block(&mut node.consequent, scopes);
3096            if let Some(alternate) = &mut node.alternate {
3097                annotate_statement(alternate, scopes);
3098            }
3099        }
3100        Statement::WhileStatement(node) => {
3101            annotate_expression(&mut node.test, scopes);
3102            annotate_block(&mut node.body, scopes);
3103        }
3104        Statement::ForStatement(node) => {
3105            annotate_expression(&mut node.iterable, scopes);
3106            let mut scope = HashSet::new();
3107            scope.insert(node.variable.clone());
3108            scopes.push(scope);
3109            annotate_statements(&mut node.body.statements, scopes);
3110            scopes.pop();
3111            if let Some(else_block) = &mut node.else_block {
3112                annotate_block(else_block, scopes);
3113            }
3114        }
3115        Statement::SwitchStatement(node) => {
3116            annotate_expression(&mut node.discriminant, scopes);
3117            for case in &mut node.cases {
3118                for value in &mut case.values {
3119                    annotate_expression(value, scopes);
3120                }
3121                scopes.push(HashSet::new());
3122                annotate_statements(&mut case.consequent, scopes);
3123                scopes.pop();
3124            }
3125            if let Some(default) = &mut node.default {
3126                scopes.push(HashSet::new());
3127                annotate_statements(default, scopes);
3128                scopes.pop();
3129            }
3130        }
3131        Statement::TryStatement(node) => {
3132            annotate_block(&mut node.body, scopes);
3133            for handler in &mut node.handlers {
3134                let mut scope = HashSet::new();
3135                if let Some(name) = handler
3136                    .binding
3137                    .as_ref()
3138                    .and_then(|binding| binding.name.clone())
3139                {
3140                    scope.insert(name);
3141                }
3142                scopes.push(scope);
3143                annotate_statements(&mut handler.body.statements, scopes);
3144                scopes.pop();
3145            }
3146        }
3147        Statement::ReturnStatement(node) => {
3148            if let Some(argument) = &mut node.argument {
3149                annotate_expression(argument, scopes);
3150            }
3151        }
3152        Statement::ThrowStatement(node) => annotate_expression(&mut node.argument, scopes),
3153        Statement::DieStatement(node) => annotate_expression(&mut node.argument, scopes),
3154        Statement::PostfixConditionalStatement(node) => {
3155            annotate_statement(&mut node.statement, scopes);
3156            annotate_expression(&mut node.test, scopes);
3157        }
3158        Statement::KeywordStatement(node) => {
3159            for argument in &mut node.arguments {
3160                annotate_expression(argument, scopes);
3161            }
3162        }
3163        Statement::ExpressionStatement(node) => annotate_expression(&mut node.expression, scopes),
3164        Statement::LoopControlStatement(_) => {}
3165    }
3166}
3167
3168fn declare_statement_binding(statement: &Statement, scopes: &mut [HashSet<String>]) {
3169    let Some(scope) = scopes.last_mut() else {
3170        return;
3171    };
3172    match statement {
3173        Statement::VariableDeclaration(node) => {
3174            scope.insert(node.name.clone());
3175        }
3176        Statement::VariableUnpackDeclaration(node) => {
3177            for entry in node.pattern.entries() {
3178                scope.insert(entry.name.clone());
3179            }
3180        }
3181        Statement::FunctionDeclaration(node) => {
3182            scope.insert(node.name.clone());
3183        }
3184        Statement::ClassDeclaration(node) => {
3185            scope.insert(node.name.clone());
3186        }
3187        Statement::TraitDeclaration(node) => {
3188            scope.insert(node.name.clone());
3189        }
3190        Statement::ImportDeclaration(node) => {
3191            for specifier in &node.specifiers {
3192                scope.insert(specifier.local.clone());
3193            }
3194        }
3195        _ => {}
3196    }
3197}
3198
3199fn annotate_block(block: &mut BlockStatement, scopes: &mut Vec<HashSet<String>>) {
3200    if block.needs_lexical_scope {
3201        scopes.push(HashSet::new());
3202        annotate_statements(&mut block.statements, scopes);
3203        scopes.pop();
3204    } else {
3205        annotate_statements(&mut block.statements, scopes);
3206    }
3207}
3208
3209fn function_scope(params: &[crate::ast::Parameter]) -> HashSet<String> {
3210    params.iter().map(|param| param.name.clone()).collect()
3211}
3212
3213fn annotate_class_member(member: &mut ClassMember, scopes: &mut Vec<HashSet<String>>) {
3214    match member {
3215        ClassMember::Field(field) => {
3216            if let Some(default_value) = &mut field.default_value {
3217                annotate_expression(default_value, scopes);
3218            }
3219        }
3220        ClassMember::Method(method) => {
3221            let mut scope = function_scope(&method.params);
3222            scope.insert("self".to_owned());
3223            scopes.push(scope);
3224            annotate_statements(&mut method.body.statements, scopes);
3225            scopes.pop();
3226        }
3227        ClassMember::Class(class) => {
3228            for member in &mut class.body {
3229                annotate_class_member(member, scopes);
3230            }
3231        }
3232        ClassMember::Trait(trait_decl) => {
3233            for member in &mut trait_decl.body {
3234                annotate_class_member(member, scopes);
3235            }
3236        }
3237    }
3238}
3239
3240fn annotate_expression(expr: &mut Expression, scopes: &mut Vec<HashSet<String>>) {
3241    match expr {
3242        Expression::Identifier {
3243            name,
3244            binding_depth,
3245            ..
3246        } => {
3247            *binding_depth = resolve_depth(name, scopes);
3248        }
3249        Expression::Unary { argument, .. } => annotate_expression(argument, scopes),
3250        Expression::Binary { left, right, .. }
3251        | Expression::DefinedOr { left, right, .. }
3252        | Expression::Assignment { left, right, .. } => {
3253            annotate_expression(left, scopes);
3254            annotate_expression(right, scopes);
3255        }
3256        Expression::Ternary {
3257            test,
3258            consequent,
3259            alternate,
3260            ..
3261        } => {
3262            annotate_expression(test, scopes);
3263            annotate_expression(consequent, scopes);
3264            annotate_expression(alternate, scopes);
3265        }
3266        Expression::Call {
3267            callee, arguments, ..
3268        } => {
3269            annotate_expression(callee, scopes);
3270            for argument in arguments {
3271                annotate_call_argument(argument, scopes);
3272            }
3273        }
3274        Expression::MemberAccess { object, .. } => annotate_expression(object, scopes),
3275        Expression::DynamicMemberCall {
3276            object,
3277            member,
3278            arguments,
3279            ..
3280        } => {
3281            annotate_expression(object, scopes);
3282            annotate_expression(member, scopes);
3283            for argument in arguments {
3284                annotate_call_argument(argument, scopes);
3285            }
3286        }
3287        Expression::Index { object, index, .. } => {
3288            annotate_expression(object, scopes);
3289            annotate_expression(index, scopes);
3290        }
3291        Expression::Slice {
3292            object, start, end, ..
3293        } => {
3294            annotate_expression(object, scopes);
3295            if let Some(start) = start {
3296                annotate_expression(start, scopes);
3297            }
3298            if let Some(end) = end {
3299                annotate_expression(end, scopes);
3300            }
3301        }
3302        Expression::DictAccess { object, key, .. } => {
3303            annotate_expression(object, scopes);
3304            annotate_dict_key(key, scopes);
3305        }
3306        Expression::PostfixUpdate { argument, .. } => annotate_expression(argument, scopes),
3307        Expression::Lambda { params, body, .. } => {
3308            scopes.push(function_scope(params));
3309            annotate_expression(body, scopes);
3310            scopes.pop();
3311        }
3312        Expression::FunctionExpression { params, body, .. } => {
3313            scopes.push(function_scope(params));
3314            annotate_statements(&mut body.statements, scopes);
3315            scopes.pop();
3316        }
3317        Expression::LetExpression { name, init, .. } => {
3318            if let Some(init) = init {
3319                annotate_expression(init, scopes);
3320            }
3321            if let Some(scope) = scopes.last_mut() {
3322                scope.insert(name.clone());
3323            }
3324        }
3325        Expression::TryExpression { body, handlers, .. } => {
3326            annotate_block(body, scopes);
3327            for handler in handlers {
3328                let mut scope = HashSet::new();
3329                if let Some(name) = handler
3330                    .binding
3331                    .as_ref()
3332                    .and_then(|binding| binding.name.clone())
3333                {
3334                    scope.insert(name);
3335                }
3336                scopes.push(scope);
3337                annotate_statements(&mut handler.body.statements, scopes);
3338                scopes.pop();
3339            }
3340        }
3341        Expression::DoExpression { body, .. }
3342        | Expression::AwaitExpression { body, .. }
3343        | Expression::SpawnExpression { body, .. } => {
3344            annotate_block(body, scopes);
3345        }
3346        Expression::ArrayLiteral { elements, .. }
3347        | Expression::SetLiteral { elements, .. }
3348        | Expression::BagLiteral { elements, .. } => {
3349            for element in elements {
3350                annotate_expression(element, scopes);
3351            }
3352        }
3353        Expression::DictLiteral { entries, .. } | Expression::PairListLiteral { entries, .. } => {
3354            for entry in entries {
3355                annotate_dict_key(&mut entry.key, scopes);
3356                annotate_expression(&mut entry.value, scopes);
3357            }
3358        }
3359        Expression::TemplateLiteral { parts, .. } | Expression::RegexLiteral { parts, .. } => {
3360            for part in parts {
3361                if let TemplatePart::Expression { expression, .. } = part {
3362                    annotate_expression(expression, scopes);
3363                }
3364            }
3365        }
3366        Expression::SuperCall { arguments, .. } => {
3367            for argument in arguments {
3368                annotate_call_argument(argument, scopes);
3369            }
3370        }
3371        Expression::NumberLiteral { .. }
3372        | Expression::StringLiteral { .. }
3373        | Expression::BinaryStringLiteral { .. }
3374        | Expression::BooleanLiteral { .. }
3375        | Expression::NullLiteral { .. } => {}
3376    }
3377}
3378
3379fn annotate_call_argument(argument: &mut CallArgument, scopes: &mut Vec<HashSet<String>>) {
3380    match argument {
3381        CallArgument::Positional { value, .. } => annotate_expression(value, scopes),
3382        CallArgument::Spread { value, .. } => annotate_expression(value, scopes),
3383        CallArgument::Named { name, value, .. } => {
3384            annotate_dict_key(name, scopes);
3385            annotate_expression(value, scopes);
3386        }
3387    }
3388}
3389
3390fn annotate_dict_key(key: &mut DictKey, scopes: &mut Vec<HashSet<String>>) {
3391    if let DictKey::Expression { expression, .. } = key {
3392        annotate_expression(expression, scopes);
3393    }
3394}
3395
3396fn resolve_depth(name: &str, scopes: &[HashSet<String>]) -> Option<usize> {
3397    for (depth, scope) in scopes.iter().rev().enumerate() {
3398        if scope.contains(name) {
3399            return Some(depth);
3400        }
3401    }
3402    None
3403}