Skip to main content

rl_lang/checker/scope/
assign.rs

1//! Assignment type checking - validates reassignment against the declared type.
2
3use crate::{
4    checker::structs::{CheckType, TypeChecker},
5    utils::{span::Span, suggest::closest_match},
6};
7
8impl TypeChecker {
9    /// Checks that assigning `value_type` to `name` is valid.
10    ///
11    /// Walks scopes from innermost to outermost. Emits an error if:
12    /// - The variable is declared `const`
13    /// - The value type doesn't match the declared type
14    /// - The name is not declared in any scope (with a "did you mean?" suggestion)
15    pub fn assign(&mut self, name: &str, value_type: CheckType, span: Span) {
16        let mut const_error: Option<String> = None;
17        let mut type_error: Option<String> = None;
18        let mut found = false;
19
20        for scope in self.scopes.iter_mut().rev() {
21            if let Some(item) = scope.get_mut(name) {
22                found = true;
23
24                if item.is_const {
25                    const_error = Some(format!("cannot assign to constant '{}'", name));
26                } else if !value_type.matches(&item.type_annotation) && !value_type.is_null() {
27                    type_error = Some(format!(
28                        "cannot assign {} to variable '{}' declared as {}",
29                        value_type.info(),
30                        name,
31                        item.type_annotation.info(),
32                    ));
33                }
34                break;
35            }
36        }
37
38        if let Some(msg) = const_error.or(type_error) {
39            self.error(msg, span);
40            return;
41        }
42
43        if !found {
44            let all_keys: Vec<String> = self
45                .scopes
46                .iter()
47                .flat_map(|s| s.keys().cloned().collect::<Vec<_>>())
48                .collect();
49            let suggestion = closest_match(name, all_keys.iter().map(|s| s.as_str()));
50
51            self.error_with_help(format!("undefined variable '{}'", name), span, suggestion);
52        }
53    }
54}