Skip to main content

rl_lang/checker/scope/
mod.rs

1//! Scope management and name lookup for the type checker.
2
3mod assign;
4mod call;
5mod declare;
6
7use crate::{
8    checker::structs::{CheckType, TypeChecker},
9    utils::{span::Span, suggest::closest_match},
10};
11use std::collections::HashMap;
12
13impl TypeChecker {
14    /// Pushes a new empty scope onto the scope stack.
15    pub fn push_scope(&mut self) {
16        self.scopes.push(HashMap::new());
17    }
18    /// Pops the innermost scope from the stack.
19    pub fn pop_scope(&mut self) {
20        self.scopes.pop();
21    }
22
23    /// Looks up `name` in all scopes from innermost to outermost.
24    ///
25    /// On success, pushes a hover entry with the variable's type and kind,
26    /// then returns the [`CheckType`]. On failure, emits an undefined variable
27    /// error with a "did you mean?" suggestion and returns [`CheckType::Unknown`].
28    pub fn lookup(&mut self, name: &str, span: Span) -> CheckType {
29        let found = self.scopes.iter().rev().find_map(|scope| {
30            scope
31                .get(name)
32                .map(|item| (item.type_annotation.clone(), item.is_const))
33        });
34
35        if let Some((item_type, is_const)) = found {
36            let kind = if is_const { "const" } else { "variable" };
37            self.push_hover(
38                span,
39                format!("```rl\n{} {}: {}\n```", kind, name, item_type.info()),
40            );
41            return item_type;
42        }
43
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        self.error_with_help(format!("undefined variable {}", name), span, suggestion);
51        CheckType::Unknown
52    }
53}