rush_analyzer/
analyzer.rs

1use std::{
2    borrow::Cow,
3    collections::{HashMap, HashSet},
4    vec,
5};
6
7use rush_parser::{ast::*, Span};
8
9use crate::{ast::*, Diagnostic, DiagnosticLevel, ErrorKind};
10
11#[derive(Default, Debug)]
12pub struct Analyzer<'src> {
13    functions: HashMap<&'src str, Function<'src>>,
14    diagnostics: Vec<Diagnostic<'src>>,
15    builtin_functions: HashMap<&'static str, BuiltinFunction>,
16    scopes: Vec<HashMap<&'src str, Variable<'src>>>,
17    curr_func_name: &'src str,
18    /// The names of all used builtin functions
19    used_builtins: HashSet<&'src str>,
20    /// Specifies the depth of loops.
21    loop_count: usize,
22    /// Specifies whether there is at least one `break` statement inside the current loop.
23    current_loop_is_terminated: bool,
24    /// The source code of the program to be analyzed
25    source: &'src str,
26}
27
28#[derive(Debug)]
29struct Function<'src> {
30    pub ident: Spanned<'src, &'src str>,
31    pub params: Spanned<'src, Vec<Parameter<'src>>>,
32    pub return_type: Spanned<'src, Option<Type>>,
33    pub used: bool,
34}
35
36#[derive(Debug, Clone)]
37struct BuiltinFunction {
38    param_types: Vec<Type>,
39    return_type: Type,
40}
41
42impl BuiltinFunction {
43    fn new(param_types: Vec<Type>, return_type: Type) -> Self {
44        Self {
45            param_types,
46            return_type,
47        }
48    }
49}
50
51#[derive(Debug)]
52struct Variable<'src> {
53    pub type_: Type,
54    pub span: Span<'src>,
55    pub used: bool,
56    pub mutable: bool,
57    pub mutated: bool,
58}
59
60impl<'src> Analyzer<'src> {
61    /// Creates a new [`Analyzer`].
62    pub fn new(source: &'src str) -> Self {
63        Self {
64            builtin_functions: HashMap::from([(
65                "exit",
66                BuiltinFunction::new(vec![Type::Int(0)], Type::Never),
67            )]),
68            source,
69            scopes: vec![HashMap::new()], // start with empty global scope
70            ..Default::default()
71        }
72    }
73
74    /// Adds a new diagnostic with the `Hint` level.
75    fn hint(&mut self, message: impl Into<Cow<'static, str>>, span: Span<'src>) {
76        self.diagnostics.push(Diagnostic::new(
77            DiagnosticLevel::Hint,
78            message,
79            vec![],
80            span,
81            self.source,
82        ))
83    }
84
85    /// Adds a new diagnostic with the `Info` level.
86    fn info(
87        &mut self,
88        message: impl Into<Cow<'static, str>>,
89        notes: Vec<Cow<'static, str>>,
90        span: Span<'src>,
91    ) {
92        self.diagnostics.push(Diagnostic::new(
93            DiagnosticLevel::Info,
94            message,
95            notes,
96            span,
97            self.source,
98        ))
99    }
100
101    /// Adds a new diagnostic with the `Warning` level.
102    fn warn(
103        &mut self,
104        message: impl Into<Cow<'static, str>>,
105        notes: Vec<Cow<'static, str>>,
106        span: Span<'src>,
107    ) {
108        self.diagnostics.push(Diagnostic::new(
109            DiagnosticLevel::Warning,
110            message,
111            notes,
112            span,
113            self.source,
114        ))
115    }
116
117    /// Adds a new diagnostic with the `Error` level using the specified error kind.
118    fn error(
119        &mut self,
120        kind: ErrorKind,
121        message: impl Into<Cow<'static, str>>,
122        notes: Vec<Cow<'static, str>>,
123        span: Span<'src>,
124    ) {
125        self.diagnostics.push(Diagnostic::new(
126            DiagnosticLevel::Error(kind),
127            message,
128            notes,
129            span,
130            self.source,
131        ))
132    }
133
134    /// Analyzes a parsed AST and returns an analyzed AST whilst emitting diagnostics.
135    pub fn analyze(
136        mut self,
137        program: Program<'src>,
138    ) -> Result<(AnalyzedProgram<'src>, Vec<Diagnostic>), Vec<Diagnostic>> {
139        // add all function signatures first
140        for func in &program.functions {
141            // check for duplicate function names
142            if let Some(prev_def) = self.functions.get(func.name.inner) {
143                let prev_def_span = prev_def.ident.span;
144                self.error(
145                    ErrorKind::Semantic,
146                    format!("duplicate function definition `{}`", func.name.inner),
147                    vec![],
148                    func.name.span,
149                );
150                self.hint(
151                    format!("function `{}` previously defined here", func.name.inner),
152                    prev_def_span,
153                );
154            }
155            self.functions.insert(
156                func.name.inner,
157                Function {
158                    ident: func.name.clone(),
159                    params: func.params.clone(),
160                    return_type: func.return_type.clone(),
161                    used: false,
162                },
163            );
164        }
165
166        // analyze global let stmts
167        // `self.global(node)` has side effects that have to happen here
168        #[allow(clippy::needless_collect)]
169        let globals: Vec<AnalyzedLetStmt> = program
170            .globals
171            .into_iter()
172            .map(|node| self.global(node))
173            .collect();
174
175        // then analyze each function body
176        let mut functions = vec![];
177        let mut main_fn = None;
178        for func in program.functions {
179            let func = self.function_definition(func);
180            match func.name {
181                "main" => {
182                    main_fn = Some(func.block);
183                }
184                _ => functions.push(func),
185            }
186        }
187
188        // pop the global scope
189        let (unused_globals, non_mut_globals) = self.pop_scope();
190        let globals: Vec<AnalyzedLetStmt> = globals
191            .into_iter()
192            .map(|g| AnalyzedLetStmt {
193                used: !unused_globals.contains(&g.name),
194                mutable: g.mutable && !non_mut_globals.contains(&g.name),
195                ..g
196            })
197            .collect();
198
199        // check if there are any unused functions
200        let unused_funcs: Vec<_> = self
201            .functions
202            .values()
203            .filter(|func| {
204                func.ident.inner != "main" && !func.ident.inner.starts_with('_') && !func.used
205            })
206            .map(|func| {
207                // set used = false in tree
208                functions
209                    .iter_mut()
210                    .find(|func_def| func_def.name == func.ident.inner)
211                    .expect("every unused function is defined")
212                    .used = false;
213
214                func.ident.clone()
215            })
216            .collect();
217
218        // add warnings to unused functions
219        for ident in unused_funcs {
220            self.warn(
221                format!("function `{}` is never called", ident.inner),
222                vec![format!(
223                    "if this is intentional, change the name to `_{}` to hide this warning",
224                    ident.inner,
225                )
226                .into()],
227                ident.span,
228            )
229        }
230
231        match main_fn {
232            Some(main_fn) => Ok((
233                AnalyzedProgram {
234                    globals,
235                    functions,
236                    main_fn,
237                    used_builtins: self.used_builtins,
238                },
239                self.diagnostics,
240            )),
241            None => {
242                self.error(
243                    ErrorKind::Semantic,
244                    "missing `main` function",
245                    vec![
246                        "the `main` function can be implemented like this: `fn main() { ... }`"
247                            .into(),
248                    ],
249                    // empty span including filename
250                    program.span.start.until(program.span.start),
251                );
252                Err(self.diagnostics)
253            }
254        }
255    }
256
257    fn push_scope(&mut self) {
258        self.scopes.push(HashMap::new());
259    }
260
261    /// Removes the current scope of the function and checks whether the
262    /// variables in the scope have been used and/or mutated.
263    /// Returns the names of variables which are unused and those which do
264    /// not need to be mutable.
265    fn pop_scope(&mut self) -> (Vec<&'src str>, Vec<&'src str>) {
266        // consume / drop the scope
267        let scope = self.scopes.pop().expect("is only called after a scope");
268
269        let mut unused = vec![];
270        let mut non_mut = vec![];
271
272        // analyze its values for their use
273        for (name, var) in scope {
274            // allow unused values if they start with `_`
275            if !name.starts_with('_') && !var.used {
276                unused.push(name);
277                self.warn(
278                    format!("unused variable `{}`", name),
279                    vec![format!(
280                        "if this is intentional, change the name to `_{name}` to hide this warning"
281                    )
282                    .into()],
283                    var.span,
284                );
285            } else if var.mutable && !var.mutated {
286                non_mut.push(name);
287                self.info(
288                    format!("variable `{name}` does not need to be mutable"),
289                    vec![],
290                    var.span,
291                );
292            }
293        }
294
295        (unused, non_mut)
296    }
297
298    // Returns a mutable reference to the current scope
299    fn scope_mut(&mut self) -> &mut HashMap<&'src str, Variable<'src>> {
300        self.scopes.last_mut().expect("only called in scopes")
301    }
302
303    fn warn_unreachable(
304        &mut self,
305        unreachable_span: Span<'src>,
306        causing_span: Span<'src>,
307        expr: bool,
308    ) {
309        self.warn(
310            match expr {
311                true => "unreachable expression",
312                false => "unreachable statement",
313            },
314            vec![],
315            unreachable_span,
316        );
317        self.hint(
318            "any code following this expression is unreachable",
319            causing_span,
320        );
321    }
322
323    fn global(&mut self, node: LetStmt<'src>) -> AnalyzedLetStmt<'src> {
324        // analyze the right hand side first
325        let expr_span = node.expr.span();
326        let expr = self.expression(node.expr);
327
328        // check if the expression is constant
329        if !expr.constant() {
330            self.error(
331                ErrorKind::Semantic,
332                "global initializer is not constant",
333                vec!["global variables must have a constant initializer".into()],
334                expr_span,
335            );
336        }
337
338        // check if the optional type conflicts with the rhs
339        if let Some(declared) = &node.type_ {
340            if declared.inner != expr.result_type()
341                && !matches!(expr.result_type(), Type::Unknown | Type::Never)
342            {
343                self.error(
344                    ErrorKind::Type,
345                    format!(
346                        "mismatched types: expected `{}`, found `{}`",
347                        declared.inner,
348                        expr.result_type(),
349                    ),
350                    vec![],
351                    expr_span,
352                );
353                self.hint("expected due to this", declared.span);
354            }
355        }
356
357        // do not allow duplicate globals
358        if let Some(prev) = self.scopes[0].get(node.name.inner) {
359            let prev_span = prev.span;
360            self.error(
361                ErrorKind::Semantic,
362                format!("duplicate definition of global `{}`", node.name.inner),
363                vec![],
364                node.name.span,
365            );
366            self.hint(
367                format!("previous definition of global `{}` here", node.name.inner),
368                prev_span,
369            );
370        } else {
371            self.scopes[0].insert(
372                node.name.inner,
373                Variable {
374                    // use `{unknown}` type for non-constant globals to prevent further misleading
375                    // warnings
376                    type_: match expr.constant() {
377                        true => node.type_.map_or(expr.result_type(), |type_| type_.inner),
378                        false => Type::Unknown,
379                    },
380                    span: node.name.span,
381                    used: false,
382                    mutable: node.mutable,
383                    mutated: false,
384                },
385            );
386        }
387
388        AnalyzedLetStmt {
389            name: node.name.inner,
390            expr,
391            mutable: node.mutable,
392            used: false,
393        }
394    }
395
396    fn function_definition(
397        &mut self,
398        node: FunctionDefinition<'src>,
399    ) -> AnalyzedFunctionDefinition<'src> {
400        // set the function name
401        self.curr_func_name = node.name.inner;
402
403        if node.name.inner == "main" {
404            // the main function must have 0 parameters
405            if !node.params.inner.is_empty() {
406                self.error(
407                    ErrorKind::Semantic,
408                    format!(
409                        "the `main` function must have 0 parameters, however {} {} defined",
410                        node.params.inner.len(),
411                        match node.params.inner.len() {
412                            1 => "is",
413                            _ => "are",
414                        },
415                    ),
416                    vec!["remove the parameters: `fn main() { ... }`".into()],
417                    node.params.span,
418                )
419            }
420
421            // the main function must return `()`
422            if let Some(return_type) = node.return_type.inner {
423                if return_type != Type::Unit {
424                    self.error(
425                        ErrorKind::Semantic,
426                        format!(
427                            "the `main` function's return type must be `()`, but is declared as `{}`",
428                            return_type,
429                        ),
430                        vec!["remove the return type: `fn main() { ... }`".into()],
431                        node.return_type.span,
432                    )
433                }
434            }
435        }
436
437        // info for explicit unit return type
438        if node.return_type.inner == Some(Type::Unit) {
439            self.info(
440                "unnecessary explicit unit return type",
441                vec![
442                    "functions implicitly return `()` by default".into(),
443                    format!(
444                        "remove the explicit type: `fn {}(...) {{ ... }}`",
445                        node.name.inner,
446                    )
447                    .into(),
448                ],
449                node.return_type.span,
450            );
451        }
452
453        // push a new scope for the new function
454        self.push_scope();
455
456        // check the function parameters
457        let mut params = vec![];
458        let mut param_names = HashSet::new();
459
460        // only analyze parameters if this is not the main function
461        for param in node.params.inner {
462            // check for duplicate function parameters
463            if !param_names.insert(param.name.inner) && node.name.inner != "main" {
464                self.error(
465                    ErrorKind::Semantic,
466                    format!("duplicate parameter name `{}`", param.name.inner),
467                    vec![],
468                    param.name.span,
469                );
470            }
471            self.scope_mut().insert(
472                param.name.inner,
473                Variable {
474                    type_: param.type_.inner,
475                    span: param.name.span,
476                    used: false,
477                    mutable: param.mutable,
478                    mutated: false,
479                },
480            );
481            params.push(AnalyzedParameter {
482                mutable: param.mutable,
483                name: param.name.inner,
484                type_: param.type_.inner,
485            });
486        }
487
488        // analyze the function body
489        let block_result_span = node.block.result_span();
490        let block = self.block(node.block, false);
491
492        // check that the block results in the expected type
493        if block.result_type != node.return_type.inner.unwrap_or(Type::Unit)
494            // unknown and never types are tolerated
495            && !matches!(block.result_type, Type::Unknown | Type::Never)
496        {
497            self.error(
498                ErrorKind::Type,
499                format!(
500                    "mismatched types: expected `{}`, found `{}`",
501                    node.return_type.inner.unwrap_or(Type::Unit),
502                    block.result_type,
503                ),
504                match node.return_type.inner.is_some() {
505                    true => vec![],
506                    false => vec![format!(
507                        "specify a function return type like this: `fn {}(...) -> {} {{ ... }}`",
508                        node.name.inner, block.result_type,
509                    )
510                    .into()],
511                },
512                block_result_span,
513            );
514            self.hint(
515                match node.return_type.inner.is_some() {
516                    true => "function return type defined here",
517                    false => "no explicit return type specified",
518                },
519                node.return_type.span,
520            );
521        }
522
523        // drop the scope when finished
524        self.pop_scope();
525
526        // issue a warning if there are more than 6 parameters
527        if params.len() > 6 {
528            self.warn(
529                "function takes more than 6 parameters".to_string(),
530                vec!["using more than 6 parameters will be slower on some backends".into()],
531                node.params.span,
532            )
533        }
534
535        AnalyzedFunctionDefinition {
536            used: true, // is modified in Self::analyze()
537            name: node.name.inner,
538            params,
539            return_type: node.return_type.inner.unwrap_or(Type::Unit),
540            block,
541        }
542    }
543
544    fn block(&mut self, node: Block<'src>, new_scope: bool) -> AnalyzedBlock<'src> {
545        if new_scope {
546            self.push_scope();
547        }
548
549        let mut stmts = vec![];
550
551        let mut never_type_span = None;
552        let mut warned_unreachable = false;
553
554        for stmt in node.stmts {
555            if let Some(span) = never_type_span {
556                if !warned_unreachable {
557                    self.warn("unreachable statement", vec![], stmt.span());
558                    self.hint("any code following this statement is unreachable", span);
559                    warned_unreachable = true;
560                }
561            }
562            let stmt_span = stmt.span();
563            if let Some(stmt) = self.statement(stmt) {
564                if stmt.result_type() == Type::Never {
565                    never_type_span = Some(stmt_span);
566                }
567                stmts.push(stmt);
568            }
569        }
570
571        // possibly mark trailing expression as unreachable
572        if let (Some(expr), Some(span), false) = (&node.expr, never_type_span, warned_unreachable) {
573            self.warn("unreachable expression", vec![], expr.span());
574            self.hint("any code following this statement is unreachable", span);
575        }
576
577        // analyze the expression
578        let expr = node.expr.map(|expr| self.expression(expr));
579
580        // result type is `!` when any statement had type `!`, otherwise the type of the expr
581        let result_type = match never_type_span {
582            Some(_) => Type::Never,
583            None => expr.as_ref().map_or(Type::Unit, |expr| expr.result_type()),
584        };
585
586        if new_scope {
587            self.pop_scope();
588        }
589
590        AnalyzedBlock {
591            result_type,
592            stmts,
593            expr,
594        }
595    }
596
597    /// Analyzes a [`Statement`].
598    /// Can return [`None`] if the statement is a `while` loop which never loops.
599    fn statement(&mut self, node: Statement<'src>) -> Option<AnalyzedStatement<'src>> {
600        Some(match node {
601            Statement::Let(node) => self.let_stmt(node),
602            Statement::Return(node) => self.return_stmt(node),
603            Statement::Loop(node) => self.loop_stmt(node),
604            Statement::While(node) => return self.while_stmt(node),
605            Statement::For(node) => self.for_stmt(node),
606            Statement::Break(node) => self.break_stmt(node),
607            Statement::Continue(node) => self.continue_stmt(node),
608            Statement::Expr(node) => AnalyzedStatement::Expr(self.expression(node.expr)),
609        })
610    }
611
612    fn let_stmt(&mut self, node: LetStmt<'src>) -> AnalyzedStatement<'src> {
613        // save the expression's span for later use
614        let expr_span = node.expr.span();
615
616        // analyze the right hand side first
617        let expr = self.expression(node.expr);
618
619        // check if the optional type conflicts with the rhs
620        if let Some(declared) = &node.type_ {
621            if declared.inner != expr.result_type()
622                && !matches!(expr.result_type(), Type::Unknown | Type::Never)
623            {
624                self.error(
625                    ErrorKind::Type,
626                    format!(
627                        "mismatched types: expected `{}`, found `{}`",
628                        declared.inner,
629                        expr.result_type(),
630                    ),
631                    vec![],
632                    expr_span,
633                );
634                self.hint("expected due to this", declared.span);
635            }
636        }
637
638        // warn unreachable if never type
639        if expr.result_type() == Type::Never {
640            self.warn_unreachable(node.span, expr_span, false);
641        }
642
643        // insert and do additional checks if variable is shadowed
644        if let Some(old) = self.scope_mut().insert(
645            node.name.inner,
646            Variable {
647                type_: match node.type_.map_or(expr.result_type(), |type_| type_.inner) {
648                    // map `!` to `{unknown}` to prevent further misleading warnings
649                    Type::Never => Type::Unknown,
650                    type_ => type_,
651                },
652                span: node.name.span,
653                used: false,
654                mutable: node.mutable,
655                mutated: false,
656            },
657        ) {
658            // a previous variable is shadowed by this declaration, analyze its use
659            if !old.used && !node.name.inner.starts_with('_') {
660                self.warn(
661                    format!("unused variable `{}`", node.name.inner),
662                    vec![format!(
663                        "if this is intentional, change the name to `_{}` to hide this warning",
664                        node.name.inner
665                    )
666                    .into()],
667                    old.span,
668                );
669                self.hint(
670                    format!("variable `{}` shadowed here", node.name.inner),
671                    node.name.span,
672                );
673            } else if old.mutable && !old.mutated {
674                self.info(
675                    format!("variable `{}` does not need to be mutable", node.name.inner),
676                    vec![],
677                    old.span,
678                );
679            }
680        }
681
682        AnalyzedStatement::Let(AnalyzedLetStmt {
683            name: node.name.inner,
684            expr,
685            mutable: node.mutable,
686            used: true,
687        })
688    }
689
690    fn return_stmt(&mut self, node: ReturnStmt<'src>) -> AnalyzedStatement<'src> {
691        // if there is an expression, visit it
692        let expr_span = node.expr.as_ref().map(|expr| expr.span());
693        let expr = node.expr.map(|expr| self.expression(expr));
694
695        // get the return type based on the expr (Unit as fallback)
696        let expr_type = expr.as_ref().map_or(Type::Unit, |expr| expr.result_type());
697
698        if expr_type == Type::Never {
699            self.warn_unreachable(
700                node.span,
701                expr_span.expect("the never type was caused by an expression"),
702                false,
703            );
704        }
705
706        let curr_fn = &self.functions[self.curr_func_name];
707
708        // test if the return type is correct
709        if curr_fn.return_type.inner.unwrap_or(Type::Unit) != expr_type
710            // unknown and never types are tolerated
711            && !matches!(expr_type, Type::Unknown | Type::Never)
712        {
713            let fn_type_span = curr_fn.return_type.span;
714            let fn_type_explicit = curr_fn.return_type.inner.is_some();
715
716            self.error(
717                ErrorKind::Type,
718                format!(
719                    "mismatched types: expected `{}`, found `{}`",
720                    curr_fn.return_type.inner.unwrap_or(Type::Unit),
721                    expr_type
722                ),
723                vec![],
724                node.span,
725            );
726            self.hint(
727                match fn_type_explicit {
728                    true => "function return type defined here",
729                    false => "no explicit return type specified",
730                },
731                fn_type_span,
732            );
733        }
734
735        AnalyzedStatement::Return(expr)
736    }
737
738    fn loop_stmt(&mut self, node: LoopStmt<'src>) -> AnalyzedStatement<'src> {
739        let old_loop_is_terminated = self.current_loop_is_terminated;
740
741        self.loop_count += 1;
742        let block_result_span = node.block.result_span();
743        let block = self.block(node.block, true);
744        self.loop_count -= 1;
745
746        if !matches!(block.result_type, Type::Unit | Type::Never | Type::Unknown) {
747            self.error(
748                ErrorKind::Type,
749                format!(
750                    "loop-statement requires a block of type `()` or `!`, found `{}`",
751                    block.result_type
752                ),
753                vec![],
754                block_result_span,
755            );
756        }
757
758        // restore loop termination count
759        let never_terminates = !self.current_loop_is_terminated;
760        self.current_loop_is_terminated = old_loop_is_terminated;
761
762        AnalyzedStatement::Loop(AnalyzedLoopStmt {
763            block,
764            never_terminates,
765        })
766    }
767
768    /// Analyzes a [`WhileStmt`].
769    /// Will return [`None`] if the loop never iterates (condition is constant `false`)
770    /// Can also return an [`AnalyzedLoopStmt`] if the expression is constant `true`.
771    fn while_stmt(&mut self, node: WhileStmt<'src>) -> Option<AnalyzedStatement<'src>> {
772        let mut condition_is_const_true = false;
773        let mut never_loops = false;
774
775        let cond_span = node.cond.span();
776        let cond = self.expression(node.cond);
777
778        // check that the condition is of type bool
779        if !matches!(
780            cond.result_type(),
781            Type::Bool(0) | Type::Never | Type::Unknown
782        ) {
783            self.error(
784                ErrorKind::Type,
785                format!(
786                    "expected value of type `bool`, found `{}`",
787                    cond.result_type()
788                ),
789                vec!["a condition must have the type `bool`".into()],
790                cond_span,
791            )
792        } else {
793            // check that the condition is non-constant
794            if cond.constant() {
795                let cond_val = match cond {
796                    AnalyzedExpression::Bool(true) => {
797                        condition_is_const_true = true;
798                        true
799                    }
800                    AnalyzedExpression::Bool(false) => {
801                        never_loops = true;
802                        false
803                    }
804                    _ => unreachable!("type is checked above and expr is constant"),
805                };
806                self.warn(
807                    format!("redundant while-statement: condition is always {cond_val}"),
808                    match cond_val {
809                        true => vec!["for unconditional loops, use a loop-statement".into()],
810                        false => vec![
811                            "since the condition is `false`, the loop will never iterate".into(),
812                        ],
813                    },
814                    cond_span,
815                )
816            }
817        }
818
819        let old_loop_is_terminated = self.current_loop_is_terminated;
820
821        self.loop_count += 1;
822        let block_result_span = node.block.result_span();
823        let body_is_empty = node.block.stmts.is_empty() && node.block.expr.is_none();
824        let block = self.block(node.block, true);
825        self.loop_count -= 1;
826
827        if body_is_empty {
828            self.warn(
829                "empty loop body",
830                vec!["empty loop wastes CPU cycles".into()],
831                node.span,
832            )
833        }
834
835        if !matches!(block.result_type, Type::Unit | Type::Never | Type::Unknown) {
836            self.error(
837                ErrorKind::Type,
838                format!(
839                    "while-statement requires a block of type `()` or `!`, found `{}`",
840                    block.result_type
841                ),
842                vec![],
843                block_result_span,
844            );
845        }
846
847        // restore loop termination count
848        let never_terminates = condition_is_const_true && !self.current_loop_is_terminated;
849        self.current_loop_is_terminated = old_loop_is_terminated;
850
851        match (never_loops, condition_is_const_true) {
852            // if the condition is always `false`, return nothing
853            (true, _) => None,
854            // else if the condition is always `true`, return an `AnalyzedLoopStmt`
855            (false, true) => Some(AnalyzedStatement::Loop(AnalyzedLoopStmt {
856                block,
857                never_terminates,
858            })),
859            // else return an `AnalyzedWhileStmt`
860            (false, false) => Some(AnalyzedStatement::While(AnalyzedWhileStmt {
861                cond,
862                block,
863                never_terminates,
864            })),
865        }
866    }
867
868    fn for_stmt(&mut self, node: ForStmt<'src>) -> AnalyzedStatement<'src> {
869        let mut never_terminates = false;
870
871        // push the scope here so that the initializer is in the new scope
872        self.push_scope();
873
874        // analyze the initializer
875        let initializer = self.expression(node.initializer);
876        self.scope_mut().insert(
877            node.ident.inner,
878            Variable {
879                type_: initializer.result_type(),
880                span: node.ident.span,
881                used: false,
882                mutable: true,
883                // always set mutated = true, even if it is not mutated, to prevent weird warnings
884                mutated: true,
885            },
886        );
887
888        // check that the condition is of type bool
889        let cond_span = node.cond.span();
890        let cond = self.expression(node.cond);
891
892        if !matches!(
893            cond.result_type(),
894            Type::Bool(0) | Type::Never | Type::Unknown
895        ) {
896            self.error(
897                ErrorKind::Type,
898                format!(
899                    "expected value of type `bool`, found `{}`",
900                    cond.result_type()
901                ),
902                vec!["a condition must have the type `bool`".into()],
903                cond_span,
904            )
905        } else {
906            // check that the condition is non-constant
907            if cond.constant() {
908                let cond_val = match cond {
909                    AnalyzedExpression::Bool(true) => {
910                        never_terminates = true;
911                        true
912                    }
913                    AnalyzedExpression::Bool(false) => false,
914                    _ => unreachable!("type is checked above and expr is constant"),
915                };
916                self.warn(
917                    format!("redundant for-statement: condition is always {cond_val}",),
918                    match cond_val {
919                        true => vec!["for unconditional loops, use a loop-statement".into()],
920                        false => vec![
921                            "since the condition is `false`, the loop will never iterate".into(),
922                        ],
923                    },
924                    cond_span,
925                )
926            }
927        }
928
929        // check that the update expr results in `()`, `!` or `{unknown}`
930        let upd_span = node.update.span();
931        let update = self.expression(node.update);
932        if !matches!(
933            update.result_type(),
934            Type::Unit | Type::Never | Type::Unknown
935        ) {
936            self.error(
937                ErrorKind::Type,
938                format!(
939                    "expected value of type `()`, found `{}`",
940                    update.result_type()
941                ),
942                vec!["an update expression must have the type `()` or `!`".into()],
943                upd_span,
944            )
945        }
946
947        // save the old status of loop termination
948        let old_loop_is_terminated = self.current_loop_is_terminated;
949
950        self.loop_count += 1;
951        let block_result_span = node.block.result_span();
952        let block = self.block(node.block, false);
953        self.pop_scope();
954        self.loop_count -= 1;
955
956        // restore loop termination count
957        let never_terminates = never_terminates && !self.current_loop_is_terminated;
958        self.current_loop_is_terminated = old_loop_is_terminated;
959
960        if !matches!(block.result_type, Type::Unit | Type::Never | Type::Unknown) {
961            self.error(
962                ErrorKind::Type,
963                format!(
964                    "for-statement requires a block of type `()` or `!`, found `{}`",
965                    block.result_type
966                ),
967                vec![],
968                block_result_span,
969            );
970        }
971
972        AnalyzedStatement::For(AnalyzedForStmt {
973            ident: node.ident.inner,
974            initializer,
975            cond,
976            update,
977            block,
978            never_terminates,
979        })
980    }
981
982    fn break_stmt(&mut self, node: BreakStmt<'src>) -> AnalyzedStatement<'src> {
983        if self.loop_count == 0 {
984            self.error(
985                ErrorKind::Semantic,
986                "`break` outside of loop",
987                vec![],
988                node.span,
989            );
990        }
991        self.current_loop_is_terminated = true;
992        AnalyzedStatement::Break
993    }
994
995    fn continue_stmt(&mut self, node: ContinueStmt<'src>) -> AnalyzedStatement<'src> {
996        if self.loop_count == 0 {
997            self.error(
998                ErrorKind::Semantic,
999                "`continue` outside of loop",
1000                vec![],
1001                node.span,
1002            );
1003        }
1004        AnalyzedStatement::Continue
1005    }
1006
1007    fn expression(&mut self, node: Expression<'src>) -> AnalyzedExpression<'src> {
1008        let res = match node {
1009            Expression::Block(node) => self.block_expr(*node),
1010            Expression::If(node) => self.if_expr(*node),
1011            Expression::Int(node) => AnalyzedExpression::Int(node.inner),
1012            Expression::Float(node) => AnalyzedExpression::Float(node.inner),
1013            Expression::Bool(node) => AnalyzedExpression::Bool(node.inner),
1014            Expression::Char(node) => {
1015                if node.inner > 0x7f {
1016                    self.error(
1017                        ErrorKind::Type,
1018                        "char literal out of range".to_string(),
1019                        vec![
1020                            format!("allowed range is `0x00..=0x7f`, got `0x{:x}`", node.inner)
1021                                .into(),
1022                        ],
1023                        node.span,
1024                    )
1025                }
1026                AnalyzedExpression::Char(node.inner)
1027            }
1028            Expression::Ident(node) => self.ident_expr(node),
1029            Expression::Prefix(node) => self.prefix_expr(*node),
1030            Expression::Infix(node) => self.infix_expr(*node),
1031            Expression::Assign(node) => self.assign_expr(*node),
1032            Expression::Call(node) => self.call_expr(*node),
1033            Expression::Cast(node) => self.cast_expr(*node),
1034            Expression::Grouped(node) => {
1035                let expr = self.expression(*node.inner);
1036                match expr.as_constant() {
1037                    Some(expr) => expr,
1038                    None => AnalyzedExpression::Grouped(expr.into()),
1039                }
1040            }
1041        };
1042
1043        // if this is a `!` expression, count it like a loop termination
1044        if res.result_type() == Type::Never {
1045            self.current_loop_is_terminated = true;
1046        }
1047
1048        res
1049    }
1050
1051    fn block_expr(&mut self, node: Block<'src>) -> AnalyzedExpression<'src> {
1052        let block = self.block(node, true);
1053
1054        match Self::eval_block(&block) {
1055            Some(expr) => expr,
1056            None => AnalyzedExpression::Block(block.into()),
1057        }
1058    }
1059
1060    fn eval_block(block: &AnalyzedBlock<'src>) -> Option<AnalyzedExpression<'src>> {
1061        if block.stmts.iter().all(|stmt| stmt.constant()) {
1062            if let Some(expr) = block.expr.as_ref().and_then(|expr| expr.as_constant()) {
1063                return Some(expr);
1064            }
1065        }
1066        None
1067    }
1068
1069    fn if_expr(&mut self, node: IfExpr<'src>) -> AnalyzedExpression<'src> {
1070        let cond_span = node.cond.span();
1071        let cond = self.expression(node.cond);
1072
1073        // check that the condition is of type bool
1074        if !matches!(
1075            cond.result_type(),
1076            Type::Bool(0) | Type::Never | Type::Unknown
1077        ) {
1078            self.error(
1079                ErrorKind::Type,
1080                format!(
1081                    "expected value of type `bool`, found `{}`",
1082                    cond.result_type()
1083                ),
1084                vec!["a condition must have the type `bool`".into()],
1085                cond_span,
1086            )
1087        } else {
1088            // check that the condition is non-constant
1089            if cond.constant() {
1090                self.warn(
1091                    format!(
1092                        "redundant if-expression: condition is always {}",
1093                        match cond {
1094                            AnalyzedExpression::Bool(true) => "true",
1095                            AnalyzedExpression::Bool(false) => "false",
1096                            _ => unreachable!("type is checked above and expr is constant"),
1097                        }
1098                    ),
1099                    vec![],
1100                    cond_span,
1101                )
1102            }
1103        }
1104
1105        // analyze then_block
1106        let then_result_span = node.then_block.result_span();
1107        let then_block = self.block(node.then_block, true);
1108
1109        // analyze else_block if it exists
1110        let result_type;
1111        let else_block = match node.else_block {
1112            Some(else_block) => {
1113                let else_result_span = else_block.result_span();
1114                let else_block = self.block(else_block, true);
1115
1116                // check type equality of the `then` and `else` branches
1117                result_type = match (then_block.result_type, else_block.result_type) {
1118                    // unknown when any branch is unknown
1119                    (Type::Unknown, _) | (_, Type::Unknown) => Type::Unknown,
1120                    // never when both branches are never
1121                    (Type::Never, Type::Never) => Type::Never,
1122                    // the type of the non-never branch when one branch is never
1123                    (type_, Type::Never) | (Type::Never, type_) => type_,
1124                    // the then_type when both branches have the same type
1125                    (then_type, else_type) if then_type == else_type => then_type,
1126                    // unknown and error otherwise
1127                    _ => {
1128                        self.error(
1129                            ErrorKind::Type,
1130                            format!(
1131                                "mismatched types: expected `{}`, found `{}`",
1132                                then_block.result_type, else_block.result_type
1133                            ),
1134                            vec!["the `if` and `else` branches must result in the same type".into()],
1135                            else_result_span,
1136                        );
1137                        self.hint("expected due to this", then_result_span);
1138                        Type::Unknown
1139                    }
1140                };
1141
1142                Some(else_block)
1143            }
1144            None => {
1145                result_type = match then_block.result_type {
1146                    Type::Unknown => Type::Unknown,
1147                    Type::Unit | Type::Never => Type::Unit,
1148                    _ => {
1149                        self.error(
1150                            ErrorKind::Type,
1151                            format!(
1152                                "mismatched types: missing else branch with `{}` result type",
1153                                then_block.result_type
1154                            ),
1155                            vec![format!("the `if` branch results in `{}`, therefore an else branch was expected", then_block.result_type).into()],
1156                            node.span,
1157                        );
1158                        Type::Unknown
1159                    }
1160                };
1161
1162                None
1163            }
1164        };
1165
1166        // evaluate constant if-exprs
1167        match (
1168            cond.as_constant(),
1169            Self::eval_block(&then_block),
1170            else_block.as_ref().and_then(Self::eval_block),
1171        ) {
1172            (Some(AnalyzedExpression::Bool(true)), Some(val), Some(_)) => return val,
1173            (Some(AnalyzedExpression::Bool(false)), Some(_), Some(val)) => return val,
1174            _ => {}
1175        }
1176
1177        AnalyzedExpression::If(
1178            AnalyzedIfExpr {
1179                result_type,
1180                cond,
1181                then_block,
1182                else_block,
1183            }
1184            .into(),
1185        )
1186    }
1187
1188    /// Searches all scopes for the requested variable.
1189    /// Starts at the current scope (last) and works its way down to the global scope (first).
1190    fn ident_expr(&mut self, node: Spanned<'src, &'src str>) -> AnalyzedExpression<'src> {
1191        for scope in self.scopes.iter_mut().rev() {
1192            if let Some(var) = scope.get_mut(node.inner) {
1193                var.used = true;
1194                return AnalyzedExpression::Ident(AnalyzedIdentExpr {
1195                    result_type: var.type_,
1196                    ident: node.inner,
1197                });
1198            };
1199        }
1200
1201        // ignore empty identifiers (cannot be created by users)
1202        if !node.inner.is_empty() {
1203            self.error(
1204                ErrorKind::Reference,
1205                format!("use of undeclared variable `{}`", node.inner),
1206                vec![],
1207                node.span,
1208            );
1209        }
1210
1211        AnalyzedExpression::Ident(AnalyzedIdentExpr {
1212            result_type: Type::Unknown,
1213            ident: node.inner,
1214        })
1215    }
1216
1217    fn prefix_expr(&mut self, node: PrefixExpr<'src>) -> AnalyzedExpression<'src> {
1218        let expr_span = node.expr.span();
1219        let expr = self.expression(node.expr);
1220
1221        let result_type = match node.op {
1222            PrefixOp::Not => match expr.result_type() {
1223                Type::Bool(0) => Type::Bool(0),
1224                Type::Int(0) => Type::Int(0),
1225                Type::Unknown => Type::Unknown,
1226                Type::Never => {
1227                    self.warn_unreachable(node.span, expr_span, true);
1228                    Type::Never
1229                }
1230                _ => {
1231                    self.error(
1232                        ErrorKind::Type,
1233                        format!(
1234                            "prefix operator `!` does not allow values of type `{}`",
1235                            expr.result_type()
1236                        ),
1237                        vec![],
1238                        node.span,
1239                    );
1240                    Type::Unknown
1241                }
1242            },
1243            PrefixOp::Neg => match expr.result_type() {
1244                Type::Int(0) => Type::Int(0),
1245                Type::Float(0) => Type::Float(0),
1246                Type::Unknown => Type::Unknown,
1247                Type::Never => {
1248                    self.warn_unreachable(node.span, expr_span, true);
1249                    Type::Never
1250                }
1251                _ => {
1252                    self.error(
1253                        ErrorKind::Type,
1254                        format!(
1255                            "prefix operator `-` does not allow values of type `{}`",
1256                            expr.result_type()
1257                        ),
1258                        vec![],
1259                        node.span,
1260                    );
1261                    Type::Unknown
1262                }
1263            },
1264            PrefixOp::Ref => match &expr {
1265                AnalyzedExpression::Ident(ident) => match ident.result_type.add_ref() {
1266                    Some(res) => {
1267                        let mut var = self
1268                            .scopes
1269                            .iter_mut()
1270                            .rev()
1271                            .find_map(|s| s.get_mut(ident.ident))
1272                            .expect("variable references are valid here");
1273
1274                        // references (`&`) count as mutable variable accesses
1275                        var.mutated = true;
1276
1277                        // validate that the variable is mutable
1278                        let var_span = var.span;
1279                        if !var.mutable {
1280                            self.error(
1281                                ErrorKind::Semantic,
1282                                format!("cannot reference immutable variable `{}`", ident.ident),
1283                                vec!["only mutable variables can be referenced".into()],
1284                                node.span,
1285                            );
1286                            self.hint("variable not declared as `mut`", var_span);
1287                        }
1288
1289                        res
1290                    }
1291                    None if ident.result_type == Type::Unknown => Type::Unknown,
1292                    None => {
1293                        self.error(
1294                            ErrorKind::Type,
1295                            format!("cannot reference a value of type `{}`", ident.result_type),
1296                            vec![],
1297                            node.span,
1298                        );
1299                        Type::Unknown
1300                    }
1301                },
1302                _ => unreachable!("parser guarantees that only identifiers are referenced"),
1303            },
1304            PrefixOp::Deref => match &expr {
1305                // TODO: improve this
1306                AnalyzedExpression::Ident(ident) => match ident.result_type.sub_deref() {
1307                    Some(res) => res,
1308                    None => {
1309                        self.error(
1310                            ErrorKind::Type,
1311                            format!("cannot dereference a value of type `{}`", ident.result_type),
1312                            vec!["only pointers `*` can be dereferenced".into()],
1313                            node.span,
1314                        );
1315                        dbg!(ident);
1316                        Type::Unknown
1317                    }
1318                },
1319                AnalyzedExpression::Prefix(expr) => match expr.result_type.sub_deref() {
1320                    Some(res) => res,
1321                    None => {
1322                        self.error(
1323                            ErrorKind::Type,
1324                            format!("cannot dereference a value of type `{}`", expr.result_type),
1325                            vec!["only pointers `*` can be dereferenced".into()],
1326                            node.span,
1327                        );
1328                        Type::Unknown
1329                    }
1330                },
1331                _ => unreachable!("can only dereference identifiers or prefix expressions"),
1332            },
1333        };
1334
1335        // evaluate constant expressions
1336        match (&expr, node.op) {
1337            (AnalyzedExpression::Int(num), PrefixOp::Not) => return AnalyzedExpression::Int(!num),
1338            (AnalyzedExpression::Int(num), PrefixOp::Neg) => {
1339                return AnalyzedExpression::Int(num.wrapping_neg())
1340            }
1341            (AnalyzedExpression::Float(num), PrefixOp::Neg) => {
1342                return AnalyzedExpression::Float(-num)
1343            }
1344            (AnalyzedExpression::Bool(bool), PrefixOp::Not) => {
1345                return AnalyzedExpression::Bool(!bool)
1346            }
1347            _ => {}
1348        }
1349
1350        AnalyzedExpression::Prefix(
1351            AnalyzedPrefixExpr {
1352                result_type,
1353                op: node.op,
1354                expr,
1355            }
1356            .into(),
1357        )
1358    }
1359
1360    fn infix_expr(&mut self, node: InfixExpr<'src>) -> AnalyzedExpression<'src> {
1361        let lhs_span = node.lhs.span();
1362        let rhs_span = node.rhs.span();
1363        let lhs = self.expression(node.lhs);
1364        let rhs = self.expression(node.rhs);
1365
1366        let allowed_types: &[Type];
1367        let mut override_result_type = None;
1368        let mut inherits_never_type = true;
1369        match node.op {
1370            InfixOp::Plus | InfixOp::Minus => {
1371                allowed_types = &[Type::Int(0), Type::Char(0), Type::Float(0)];
1372            }
1373            InfixOp::Mul | InfixOp::Div => {
1374                allowed_types = &[Type::Int(0), Type::Float(0)];
1375            }
1376            InfixOp::Lt | InfixOp::Gt | InfixOp::Lte | InfixOp::Gte => {
1377                allowed_types = &[Type::Int(0), Type::Char(0), Type::Float(0)];
1378                override_result_type = Some(Type::Bool(0));
1379            }
1380            InfixOp::Rem | InfixOp::Shl | InfixOp::Shr | InfixOp::Pow => {
1381                allowed_types = &[Type::Int(0)];
1382            }
1383            InfixOp::Eq | InfixOp::Neq => {
1384                allowed_types = &[Type::Int(0), Type::Float(0), Type::Bool(0), Type::Char(0)];
1385                override_result_type = Some(Type::Bool(0));
1386            }
1387            InfixOp::BitOr | InfixOp::BitAnd | InfixOp::BitXor => {
1388                allowed_types = &[Type::Int(0), Type::Bool(0)];
1389            }
1390            InfixOp::And | InfixOp::Or => {
1391                allowed_types = &[Type::Bool(0)];
1392                inherits_never_type = false;
1393            }
1394        }
1395
1396        let result_type = match (lhs.result_type(), rhs.result_type()) {
1397            (Type::Unknown, _) | (_, Type::Unknown) => Type::Unknown,
1398            (Type::Never, Type::Never) => {
1399                self.warn_unreachable(node.span, lhs_span, true);
1400                self.warn_unreachable(node.span, rhs_span, true);
1401                Type::Never
1402            }
1403            (Type::Never, _) if inherits_never_type => {
1404                self.warn_unreachable(node.span, lhs_span, true);
1405                Type::Never
1406            }
1407            (_, Type::Never) if inherits_never_type => {
1408                self.warn_unreachable(node.span, rhs_span, true);
1409                Type::Never
1410            }
1411            (Type::Never, _) => rhs.result_type(),
1412            (_, Type::Never) => lhs.result_type(),
1413            (left, right) if left == right && allowed_types.contains(&left) => {
1414                override_result_type.unwrap_or(left)
1415            }
1416            (left, right) if left != right => {
1417                self.error(
1418                    ErrorKind::Type,
1419                    format!(
1420                        "infix expressions require equal types on both sides, got `{left}` and `{right}`"
1421                    ),
1422                    vec![],
1423                    node.span,
1424                );
1425                Type::Unknown
1426            }
1427            (type_, _) => {
1428                self.error(
1429                    ErrorKind::Type,
1430                    format!(
1431                        "infix operator `{}` does not allow values of type `{type_}`",
1432                        node.op
1433                    ),
1434                    vec![],
1435                    node.span,
1436                );
1437                Type::Unknown
1438            }
1439        };
1440
1441        // evaluate constant expressions
1442        match (&lhs, &rhs) {
1443            (AnalyzedExpression::Char(left), AnalyzedExpression::Char(right)) => match node.op {
1444                InfixOp::Plus => return AnalyzedExpression::Char(left.wrapping_add(*right) & 0x7f),
1445                InfixOp::Minus => {
1446                    return AnalyzedExpression::Char(left.wrapping_sub(*right) & 0x7f)
1447                }
1448                InfixOp::Eq => return AnalyzedExpression::Bool(left == right),
1449                InfixOp::Neq => return AnalyzedExpression::Bool(left != right),
1450                InfixOp::Lt => return AnalyzedExpression::Bool(left < right),
1451                InfixOp::Lte => return AnalyzedExpression::Bool(left <= right),
1452                InfixOp::Gt => return AnalyzedExpression::Bool(left > right),
1453                InfixOp::Gte => return AnalyzedExpression::Bool(left >= right),
1454                _ => {}
1455            },
1456            (AnalyzedExpression::Int(left), AnalyzedExpression::Int(right)) => match node.op {
1457                InfixOp::Plus => return AnalyzedExpression::Int(left.wrapping_add(*right)),
1458                InfixOp::Minus => return AnalyzedExpression::Int(left.wrapping_sub(*right)),
1459                InfixOp::Mul => return AnalyzedExpression::Int(left.wrapping_mul(*right)),
1460                InfixOp::Div if *right == 0 => self.error(
1461                    ErrorKind::Semantic,
1462                    format!("cannot divide {left} by 0"),
1463                    vec!["division by 0 is undefined".into()],
1464                    node.span,
1465                ),
1466                InfixOp::Div => return AnalyzedExpression::Int(left.wrapping_div(*right)),
1467                InfixOp::Rem if *right == 0 => self.error(
1468                    ErrorKind::Semantic,
1469                    format!("cannot calculate remainder of {left} with a divisor of 0"),
1470                    vec!["division by 0 is undefined".into()],
1471                    node.span,
1472                ),
1473                InfixOp::Rem => return AnalyzedExpression::Int(left.wrapping_rem(*right)),
1474                InfixOp::Pow => {
1475                    return AnalyzedExpression::Int(if *right < 0 {
1476                        0
1477                    } else {
1478                        left.wrapping_pow(*right as u32)
1479                    })
1480                }
1481                InfixOp::Eq => return AnalyzedExpression::Bool(left == right),
1482                InfixOp::Neq => return AnalyzedExpression::Bool(left != right),
1483                InfixOp::Lt => return AnalyzedExpression::Bool(left < right),
1484                InfixOp::Gt => return AnalyzedExpression::Bool(left > right),
1485                InfixOp::Lte => return AnalyzedExpression::Bool(left <= right),
1486                InfixOp::Gte => return AnalyzedExpression::Bool(left >= right),
1487                InfixOp::Shl | InfixOp::Shr => match *right {
1488                    0..=63 => {
1489                        return AnalyzedExpression::Int(match node.op == InfixOp::Shl {
1490                            true => left << right,
1491                            false => left >> right,
1492                        })
1493                    }
1494                    _ => self.error(
1495                        ErrorKind::Semantic,
1496                        format!("cannot shift by {right}"),
1497                        vec!["shifting by a number outside the range `0..=63` is undefined".into()],
1498                        node.span,
1499                    ),
1500                },
1501                InfixOp::BitOr => return AnalyzedExpression::Int(left | right),
1502                InfixOp::BitAnd => return AnalyzedExpression::Int(left & right),
1503                InfixOp::BitXor => return AnalyzedExpression::Int(left ^ right),
1504                _ => {}
1505            },
1506            (AnalyzedExpression::Float(left), AnalyzedExpression::Float(right)) => match node.op {
1507                InfixOp::Plus => return AnalyzedExpression::Float(left + right),
1508                InfixOp::Minus => return AnalyzedExpression::Float(left - right),
1509                InfixOp::Mul => return AnalyzedExpression::Float(left * right),
1510                InfixOp::Div => return AnalyzedExpression::Float(left / right),
1511                InfixOp::Eq => return AnalyzedExpression::Bool(left == right),
1512                InfixOp::Neq => return AnalyzedExpression::Bool(left != right),
1513                InfixOp::Lt => return AnalyzedExpression::Bool(left < right),
1514                InfixOp::Gt => return AnalyzedExpression::Bool(left > right),
1515                InfixOp::Lte => return AnalyzedExpression::Bool(left <= right),
1516                InfixOp::Gte => return AnalyzedExpression::Bool(left >= right),
1517                _ => {}
1518            },
1519            (AnalyzedExpression::Bool(left), AnalyzedExpression::Bool(right)) => match node.op {
1520                InfixOp::Eq => return AnalyzedExpression::Bool(left == right),
1521                InfixOp::Neq => return AnalyzedExpression::Bool(left != right),
1522                InfixOp::BitOr => return AnalyzedExpression::Bool(left | right),
1523                InfixOp::BitAnd => return AnalyzedExpression::Bool(left & right),
1524                InfixOp::BitXor => return AnalyzedExpression::Bool(left ^ right),
1525                InfixOp::And => return AnalyzedExpression::Bool(*left && *right),
1526                InfixOp::Or => return AnalyzedExpression::Bool(*left || *right),
1527                _ => {}
1528            },
1529            _ => {}
1530        }
1531
1532        AnalyzedExpression::Infix(
1533            AnalyzedInfixExpr {
1534                result_type,
1535                lhs,
1536                op: node.op,
1537                rhs,
1538            }
1539            .into(),
1540        )
1541    }
1542
1543    fn assign_type_error(&mut self, op: AssignOp, type_: Type, span: Span<'src>) -> Type {
1544        self.error(
1545            ErrorKind::Type,
1546            format!("assignment operator `{op}` does not allow values of type `{type_}`"),
1547            vec![],
1548            span,
1549        );
1550        Type::Unknown
1551    }
1552
1553    fn assign_expr(&mut self, node: AssignExpr<'src>) -> AnalyzedExpression<'src> {
1554        let var_type = match self
1555            .scopes
1556            .iter_mut()
1557            .rev()
1558            .find_map(|scope| scope.get_mut(node.assignee.inner))
1559        {
1560            Some(var) => {
1561                var.mutated = true;
1562                let mut type_ = var.type_;
1563                if !var.mutable && node.assignee_ptr_count == 0 {
1564                    let span = var.span;
1565                    self.error(
1566                        ErrorKind::Semantic,
1567                        format!(
1568                            "cannot re-assign to immutable variable `{}`",
1569                            node.assignee.inner
1570                        ),
1571                        vec![],
1572                        node.span,
1573                    );
1574                    self.hint("variable not declared as `mut`", span);
1575                } else if node.assignee_ptr_count > 0 {
1576                    // using a pointer inside an assignment counts as use of the pointer variable
1577                    var.used = true;
1578                }
1579
1580                for _ in 0..node.assignee_ptr_count {
1581                    type_ = match type_.sub_deref() {
1582                        Some(type_) => type_,
1583                        None => {
1584                            self.error(
1585                                ErrorKind::Type,
1586                                format!("cannot dereference a value of type `{type_}`"),
1587                                vec!["only pointers `*` can be dereferenced".into()],
1588                                node.assignee.span,
1589                            );
1590                            Type::Unknown
1591                        }
1592                    };
1593                }
1594
1595                type_
1596            }
1597            None => match self.functions.get(node.assignee.inner) {
1598                Some(_) => {
1599                    self.error(
1600                        ErrorKind::Type,
1601                        "cannot assign to functions",
1602                        vec![],
1603                        node.assignee.span,
1604                    );
1605                    Type::Unknown
1606                }
1607                None if node.assignee.inner.is_empty() => Type::Unknown,
1608                None => {
1609                    self.error(
1610                        ErrorKind::Reference,
1611                        format!("use of undeclared name `{}`", node.assignee.inner),
1612                        vec![],
1613                        node.assignee.span,
1614                    );
1615                    Type::Unknown
1616                }
1617            },
1618        };
1619
1620        let expr_span = node.expr.span();
1621        let expr = self.expression(node.expr);
1622        let result_type = match (node.op, var_type, expr.result_type()) {
1623            (_, Type::Unknown, _) | (_, _, Type::Unknown) => Type::Unknown,
1624            (_, _, Type::Never) => {
1625                self.warn_unreachable(node.span, expr_span, true);
1626                Type::Never
1627            }
1628            (_, left, right) if left != right => {
1629                self.error(
1630                    ErrorKind::Type,
1631                    format!("mismatched types: expected `{left}`, found `{right}`"),
1632                    vec![],
1633                    expr_span,
1634                );
1635                self.hint(
1636                    format!("this variable has type `{left}`"),
1637                    node.assignee.span,
1638                );
1639                Type::Unknown
1640            }
1641            (AssignOp::Plus | AssignOp::Minus, _, type_)
1642                if ![Type::Int(0), Type::Float(0), Type::Char(0)].contains(&type_) =>
1643            {
1644                self.assign_type_error(node.op, type_, expr_span)
1645            }
1646            (AssignOp::Mul | AssignOp::Div, _, type_)
1647                if ![Type::Int(0), Type::Float(0)].contains(&type_) =>
1648            {
1649                self.assign_type_error(node.op, type_, expr_span)
1650            }
1651            (AssignOp::Rem | AssignOp::Pow | AssignOp::Shl | AssignOp::Shr, _, type_)
1652                if type_ != Type::Int(0) =>
1653            {
1654                self.assign_type_error(node.op, type_, expr_span)
1655            }
1656            (AssignOp::BitOr | AssignOp::BitAnd | AssignOp::BitXor, _, type_)
1657                if ![Type::Int(0), Type::Bool(0)].contains(&type_) =>
1658            {
1659                self.assign_type_error(node.op, type_, expr_span)
1660            }
1661            (_, _, _) => Type::Unit,
1662        };
1663
1664        AnalyzedExpression::Assign(
1665            AnalyzedAssignExpr {
1666                result_type,
1667                assignee: node.assignee.inner,
1668                assignee_ptr_count: node.assignee_ptr_count,
1669                op: node.op,
1670                expr,
1671            }
1672            .into(),
1673        )
1674    }
1675
1676    fn call_expr(&mut self, node: CallExpr<'src>) -> AnalyzedExpression<'src> {
1677        let func = match (
1678            self.functions.get_mut(node.func.inner),
1679            self.builtin_functions.get(node.func.inner),
1680        ) {
1681            (Some(func), _) => {
1682                // only mark the function as used if it is called from outside of its body
1683                if self.curr_func_name != node.func.inner {
1684                    func.used = true;
1685                }
1686                Some((
1687                    func.return_type.inner.unwrap_or(Type::Unit),
1688                    func.params.clone(),
1689                ))
1690            }
1691            (_, Some(builtin)) => {
1692                self.used_builtins.insert(node.func.inner);
1693                let builtin = builtin.clone();
1694                let (result_type, args) = if node.args.len() != builtin.param_types.len() {
1695                    self.error(
1696                        ErrorKind::Reference,
1697                        format!(
1698                            "function `{}` takes {} arguments, however {} were supplied",
1699                            node.func.inner,
1700                            builtin.param_types.len(),
1701                            node.args.len()
1702                        ),
1703                        vec![],
1704                        node.span,
1705                    );
1706                    (builtin.return_type, vec![])
1707                } else {
1708                    let mut result_type = builtin.return_type;
1709                    let args = node
1710                        .args
1711                        .into_iter()
1712                        .zip(builtin.param_types)
1713                        .map(|(arg, param_type)| {
1714                            self.arg(arg, param_type, node.span, &mut result_type)
1715                        })
1716                        .collect();
1717                    (result_type, args)
1718                };
1719
1720                return AnalyzedExpression::Call(
1721                    AnalyzedCallExpr {
1722                        result_type,
1723                        func: node.func.inner,
1724                        args,
1725                    }
1726                    .into(),
1727                );
1728            }
1729            (None, None) => {
1730                self.error(
1731                    ErrorKind::Reference,
1732                    format!("use of undeclared function `{}`", node.func.inner),
1733                    vec![format!(
1734                        "it can be declared like this: `fn {}(...) {{ ... }}`",
1735                        node.func.inner
1736                    )
1737                    .into()],
1738                    node.func.span,
1739                );
1740                None
1741            }
1742        };
1743        let (result_type, args) = match func {
1744            Some((func_type, func_params)) => {
1745                if node.args.len() != func_params.inner.len() {
1746                    self.error(
1747                        ErrorKind::Reference,
1748                        format!(
1749                            "function `{}` takes {} arguments, however {} were supplied",
1750                            node.func.inner,
1751                            func_params.inner.len(),
1752                            node.args.len()
1753                        ),
1754                        vec![],
1755                        node.span,
1756                    );
1757                    self.hint(
1758                        format!(
1759                            "function `{}` defined here with {} parameters",
1760                            node.func.inner,
1761                            func_params.inner.len()
1762                        ),
1763                        func_params.span,
1764                    );
1765                    (func_type, vec![])
1766                } else {
1767                    let mut result_type = func_type;
1768                    let args = node
1769                        .args
1770                        .into_iter()
1771                        .zip(func_params.inner)
1772                        .map(|(arg, param)| {
1773                            self.arg(arg, param.type_.inner, node.span, &mut result_type)
1774                        })
1775                        .collect();
1776                    (result_type, args)
1777                }
1778            }
1779            None => {
1780                let mut result_type = Type::Unknown;
1781                let args = node
1782                    .args
1783                    .into_iter()
1784                    .map(|arg| {
1785                        let arg = self.expression(arg);
1786                        if arg.result_type() == Type::Never {
1787                            result_type = Type::Never;
1788                        }
1789                        arg
1790                    })
1791                    .collect();
1792                (result_type, args)
1793            }
1794        };
1795
1796        AnalyzedExpression::Call(
1797            AnalyzedCallExpr {
1798                result_type,
1799                func: node.func.inner,
1800                args,
1801            }
1802            .into(),
1803        )
1804    }
1805
1806    fn arg(
1807        &mut self,
1808        arg: Expression<'src>,
1809        param_type: Type,
1810        call_span: Span<'src>,
1811        result_type: &mut Type,
1812    ) -> AnalyzedExpression<'src> {
1813        let arg_span = arg.span();
1814        let arg = self.expression(arg);
1815
1816        match (arg.result_type(), param_type) {
1817            (Type::Unknown, _) | (_, Type::Unknown) => {}
1818            (Type::Never, _) => {
1819                self.warn_unreachable(call_span, arg_span, true);
1820                *result_type = Type::Never;
1821            }
1822            (arg_type, param_type) if arg_type != param_type => self.error(
1823                ErrorKind::Type,
1824                format!("mismatched types: expected `{param_type}`, found `{arg_type}`"),
1825                vec![],
1826                arg_span,
1827            ),
1828            _ => {}
1829        }
1830
1831        arg
1832    }
1833
1834    fn cast_expr(&mut self, node: CastExpr<'src>) -> AnalyzedExpression<'src> {
1835        let expr_span = node.expr.span();
1836        let expr = self.expression(node.expr);
1837
1838        let result_type = match (expr.result_type(), node.type_.inner) {
1839            (Type::Unknown, _) => Type::Unknown,
1840            (Type::Never, _) => {
1841                self.warn_unreachable(node.span, expr_span, true);
1842                Type::Never
1843            }
1844            (left, right) if left == right => {
1845                self.info("unnecessary cast to same type", vec![], node.span);
1846                left
1847            }
1848            (
1849                Type::Int(0) | Type::Float(0) | Type::Bool(0) | Type::Char(0),
1850                Type::Int(0) | Type::Float(0) | Type::Bool(0) | Type::Char(0),
1851            ) => node.type_.inner,
1852            _ => {
1853                self.error(
1854                    ErrorKind::Type,
1855                    format!(
1856                        "invalid cast: cannot cast type `{}` to `{}",
1857                        expr.result_type(),
1858                        node.type_.inner
1859                    ),
1860                    vec![],
1861                    node.span,
1862                );
1863                Type::Unknown
1864            }
1865        };
1866
1867        // evaluate constant expressions
1868        match (expr, result_type) {
1869            (AnalyzedExpression::Int(val), Type::Int(0)) => AnalyzedExpression::Int(val),
1870            (AnalyzedExpression::Int(val), Type::Float(0)) => AnalyzedExpression::Float(val as f64),
1871            (AnalyzedExpression::Int(val), Type::Bool(0)) => AnalyzedExpression::Bool(val != 0),
1872            (AnalyzedExpression::Int(val), Type::Char(0)) => {
1873                AnalyzedExpression::Char(val.clamp(0, 127) as u8)
1874            }
1875            (AnalyzedExpression::Float(val), Type::Int(0)) => AnalyzedExpression::Int(val as i64),
1876            (AnalyzedExpression::Float(val), Type::Float(0)) => AnalyzedExpression::Float(val),
1877            (AnalyzedExpression::Float(val), Type::Bool(0)) => AnalyzedExpression::Bool(val != 0.0),
1878            (AnalyzedExpression::Float(val), Type::Char(0)) => {
1879                AnalyzedExpression::Char(val.clamp(0.0, 127.0) as u8)
1880            }
1881            (AnalyzedExpression::Bool(val), Type::Int(0)) => AnalyzedExpression::Int(val as i64),
1882            (AnalyzedExpression::Bool(val), Type::Float(0)) => {
1883                AnalyzedExpression::Float(val as u8 as f64)
1884            }
1885            (AnalyzedExpression::Bool(val), Type::Bool(0)) => AnalyzedExpression::Bool(val),
1886            (AnalyzedExpression::Bool(val), Type::Char(0)) => AnalyzedExpression::Char(val as u8),
1887            (AnalyzedExpression::Char(val), Type::Int(0)) => AnalyzedExpression::Int(val as i64),
1888            (AnalyzedExpression::Char(val), Type::Float(0)) => {
1889                AnalyzedExpression::Float(val as f64)
1890            }
1891            (AnalyzedExpression::Char(val), Type::Bool(0)) => AnalyzedExpression::Bool(val != 0),
1892            (AnalyzedExpression::Char(val), Type::Char(0)) => AnalyzedExpression::Char(val),
1893            (expr, result_type) => AnalyzedExpression::Cast(
1894                AnalyzedCastExpr {
1895                    result_type,
1896                    expr,
1897                    type_: node.type_.inner,
1898                }
1899                .into(),
1900            ),
1901        }
1902    }
1903}
1904
1905#[cfg(test)]
1906mod tests {
1907    use super::*;
1908
1909    use rush_parser::{span, tree};
1910
1911    fn program_test(
1912        parsed_tree: Program<'static>,
1913        analyzed_tree: AnalyzedProgram<'static>,
1914    ) -> Result<(), Vec<Diagnostic<'static>>> {
1915        let (tree, diagnostics) = dbg!(Analyzer::new("").analyze(parsed_tree))?;
1916        assert!(!diagnostics
1917            .iter()
1918            .any(|diag| matches!(diag.level, DiagnosticLevel::Error(_))));
1919        assert_eq!(tree, analyzed_tree);
1920        Ok(())
1921    }
1922
1923    #[test]
1924    fn programs() -> Result<(), Vec<Diagnostic<'static>>> {
1925        // fn add(left: int, right: int) -> int { return left + right; } fn main() {}
1926        program_test(
1927            tree! {
1928                (Program @ 0..61,
1929                    functions: [
1930                        (FunctionDefinition @ 0..61,
1931                            name: ("add", @ 3..6),
1932                            params @ 6..29: [
1933                                (Parameter,
1934                                    mutable: false,
1935                                    name: ("left", @ 7..11),
1936                                    type: (Type::Int(0), @ 13..16)),
1937                                (Parameter,
1938                                    mutable: false,
1939                                    name: ("right", @ 18..23),
1940                                    type: (Type::Int(0), @ 25..28))],
1941                            return_type: (Some(Type::Int(0)), @ 33..36),
1942                            block: (Block @ 37..61,
1943                                stmts: [
1944                                    (ReturnStmt @ 39..59, (Some(InfixExpr @ 46..58,
1945                                        lhs: (Ident "left", @ 46..50),
1946                                        op: InfixOp::Plus,
1947                                        rhs: (Ident "right", @ 53..58))))],
1948                                expr: (None))),
1949                        (FunctionDefinition @ 62..74,
1950                            name: ("main", @ 65..69),
1951                            params @ 69..71: [],
1952                            return_type: (None, @ 70..73),
1953                            block: (Block @ 72..74,
1954                                stmts: [],
1955                                expr: (None)))],
1956                    globals: [])
1957            },
1958            analyzed_tree! {
1959                (Program,
1960                    globals: [],
1961                    functions: [
1962                        (FunctionDefinition,
1963                            used: false,
1964                            name: "add",
1965                            params: [
1966                                (Parameter,
1967                                    mutable: false,
1968                                    name: "left",
1969                                    type: Type::Int(0)  ),
1970                                (Parameter,
1971                                    mutable: false,
1972                                    name: "right",
1973                                    type: Type::Int(0)  )],
1974                            return_type: Type::Int(0),
1975                            block: (Block -> Type::Never,
1976                                stmts: [
1977                                    (ReturnStmt, (Some(InfixExpr -> Type::Int(0),
1978                                        lhs: (Ident -> Type::Int(0), "left"),
1979                                        op: InfixOp::Plus,
1980                                        rhs: (Ident -> Type::Int(0), "right"))))],
1981                                expr: (None)))],
1982                    main_fn: (Block -> Type::Unit,
1983                        stmts: [],
1984                        expr: (None)),
1985                    used_builtins: [])
1986            },
1987        )?;
1988
1989        // fn main() { exit(1 + 2); }
1990        program_test(
1991            tree! {
1992                (Program @ 0..26,
1993                    functions: [
1994                        (FunctionDefinition @ 0..26,
1995                            name: ("main", @ 3..7),
1996                            params @ 7..9: [],
1997                            return_type: (None, @ 8..11),
1998                            block: (Block @ 10..26,
1999                                stmts: [
2000                                    (ExprStmt @ 12..24, (CallExpr @ 12..23,
2001                                        func: ("exit", @ 12..16),
2002                                        args: [
2003                                            (InfixExpr @ 17..22,
2004                                                lhs: (Int 1, @ 17..18),
2005                                                op: InfixOp::Plus,
2006                                                rhs: (Int 2, @ 21..22))]))],
2007                                expr: (None)))],
2008                    globals: [])
2009            },
2010            analyzed_tree! {
2011                (Program,
2012                    globals: [],
2013                    functions: [],
2014                    main_fn: (Block -> Type::Never,
2015                        stmts: [
2016                            (ExprStmt, (CallExpr -> Type::Never,
2017                                func: "exit",
2018                                args: [(Int 3)]))],
2019                        expr: (None)),
2020                    used_builtins: ["exit"])
2021            },
2022        )?;
2023
2024        Ok(())
2025    }
2026}