Skip to main content

parser/
validation.rs

1use std::collections::HashSet;
2use std::fmt;
3
4use lexer::token::Span;
5
6use crate::ast::*;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct ValidationError {
10    pub message: String,
11    pub span: Span,
12}
13
14impl fmt::Display for ValidationError {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "{}", self.message)
17    }
18}
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21enum CallableKind {
22    Function,
23    Method,
24    Constructor,
25}
26
27struct Validator {
28    scopes: Vec<HashSet<String>>,
29    callable_kinds: Vec<CallableKind>,
30    receiver_available: bool,
31    context: Vec<String>,
32}
33
34pub fn validate_program(
35    program: &Program,
36    predefined_globals: &[&str],
37) -> Result<(), ValidationError> {
38    let mut globals = HashSet::new();
39    globals.extend(predefined_globals.iter().map(|name| (*name).to_string()));
40    let mut validator = Validator {
41        scopes: vec![globals],
42        callable_kinds: Vec::new(),
43        receiver_available: false,
44        context: Vec::new(),
45    };
46    validator.validate_statements(&program.body)
47}
48
49impl Validator {
50    fn validate_statements(&mut self, statements: &[Statement]) -> Result<(), ValidationError> {
51        for statement in statements {
52            self.validate_statement(statement)?;
53        }
54        Ok(())
55    }
56
57    fn validate_statement(&mut self, statement: &Statement) -> Result<(), ValidationError> {
58        match statement {
59            Statement::Let(statement) => {
60                self.validate_expression(&statement.expr)?;
61                self.scopes
62                    .last_mut()
63                    .unwrap()
64                    .insert(statement.identifier.name.clone());
65                Ok(())
66            }
67            Statement::Return(statement) => {
68                if self.callable_kinds.last() == Some(&CallableKind::Constructor) {
69                    return Err(ValidationError {
70                        message: "constructor cannot return a value".to_string(),
71                        span: statement.span.clone(),
72                    });
73                }
74                self.validate_expression(&statement.argument)
75            }
76            Statement::Class(class) => self.validate_class(class),
77            Statement::SetProperty(statement) => {
78                self.validate_expression(&statement.object)?;
79                self.validate_expression(&statement.value)
80            }
81            Statement::Debugger(_) => Ok(()),
82            Statement::Expr(expression) => self.validate_expression(expression),
83        }
84    }
85
86    fn validate_class(&mut self, class: &ClassDeclaration) -> Result<(), ValidationError> {
87        self.scopes
88            .last_mut()
89            .unwrap()
90            .insert(class.name.name.clone());
91        self.context.push(format!("class {}", class.name.name));
92        for method in &class.methods {
93            self.validate_method(method)?;
94        }
95        self.context.pop();
96        Ok(())
97    }
98
99    fn validate_method(&mut self, method: &MethodDefinition) -> Result<(), ValidationError> {
100        let callable_kind = match method.kind {
101            MethodKind::Constructor => CallableKind::Constructor,
102            MethodKind::Method => CallableKind::Method,
103        };
104        self.context.push(method.name.name.clone());
105        self.callable_kinds.push(callable_kind);
106        let old_receiver_available = self.receiver_available;
107        self.receiver_available = true;
108        self.scopes.push(
109            method
110                .params
111                .iter()
112                .map(|parameter| parameter.identifier.name.clone())
113                .collect(),
114        );
115
116        let result = self.validate_statements(&method.body.body);
117
118        self.scopes.pop();
119        self.receiver_available = old_receiver_available;
120        self.callable_kinds.pop();
121        self.context.pop();
122        result
123    }
124
125    fn validate_function(&mut self, function: &FunctionDeclaration) -> Result<(), ValidationError> {
126        self.callable_kinds.push(CallableKind::Function);
127        let mut scope = function
128            .params
129            .iter()
130            .map(|parameter| parameter.identifier.name.clone())
131            .collect::<HashSet<_>>();
132        if !function.name.is_empty() {
133            // A directly let-bound function gets its binding name from the
134            // parser. The compiler exposes that name only inside the function
135            // body, which permits recursion without exposing an uninitialized
136            // let binding to the initializer as a whole.
137            scope.insert(function.name.clone());
138        }
139        self.scopes.push(scope);
140        let result = self.validate_statements(&function.body.body);
141        self.scopes.pop();
142        self.callable_kinds.pop();
143        result
144    }
145
146    fn validate_expression(&mut self, expression: &Expression) -> Result<(), ValidationError> {
147        match expression {
148            Expression::IDENTIFIER(identifier) => self.validate_identifier(identifier),
149            Expression::LITERAL(literal) => self.validate_literal(literal),
150            Expression::PREFIX(expression) => self.validate_expression(&expression.operand),
151            Expression::INFIX(expression) => {
152                self.validate_expression(&expression.left)?;
153                self.validate_expression(&expression.right)
154            }
155            Expression::IF(expression) => {
156                self.validate_expression(&expression.condition)?;
157                self.validate_statements(&expression.consequent.body)?;
158                if let Some(alternate) = &expression.alternate {
159                    self.validate_statements(&alternate.body)?;
160                }
161                Ok(())
162            }
163            Expression::FUNCTION(function) => self.validate_function(function),
164            Expression::FunctionCall(call) => {
165                self.validate_expression(&call.callee)?;
166                self.validate_expressions(&call.arguments)
167            }
168            Expression::Index(index) => {
169                self.validate_expression(&index.object)?;
170                self.validate_expression(&index.index)
171            }
172            Expression::This(this) => {
173                if self.receiver_available {
174                    Ok(())
175                } else {
176                    Err(ValidationError {
177                        message: "this is only available inside a method".to_string(),
178                        span: this.span.clone(),
179                    })
180                }
181            }
182            Expression::Property(property) => self.validate_expression(&property.object),
183            Expression::New(new_expression) => {
184                self.validate_identifier(&new_expression.callee)?;
185                self.validate_expressions(&new_expression.arguments)
186            }
187        }
188    }
189
190    fn validate_literal(&mut self, literal: &Literal) -> Result<(), ValidationError> {
191        match literal {
192            Literal::Array(array) => self.validate_expressions(&array.elements),
193            Literal::Hash(hash) => {
194                for (key, value) in &hash.elements {
195                    self.validate_expression(key)?;
196                    self.validate_expression(value)?;
197                }
198                Ok(())
199            }
200            _ => Ok(()),
201        }
202    }
203
204    fn validate_expressions(&mut self, expressions: &[Expression]) -> Result<(), ValidationError> {
205        for expression in expressions {
206            self.validate_expression(expression)?;
207        }
208        Ok(())
209    }
210
211    fn validate_identifier(&self, identifier: &IDENTIFIER) -> Result<(), ValidationError> {
212        if self
213            .scopes
214            .iter()
215            .rev()
216            .any(|scope| scope.contains(&identifier.name))
217        {
218            return Ok(());
219        }
220
221        let context = if self.context.is_empty() {
222            String::new()
223        } else {
224            format!(" in {}", self.context.join("."))
225        };
226        Err(ValidationError {
227            message: format!("undefined variable '{}'{}", identifier.name, context),
228            span: identifier.span.clone(),
229        })
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236    use crate::{ast::Node, parse};
237
238    fn validate(input: &str) -> Result<(), ValidationError> {
239        let Node::Program(program) = parse(input).unwrap() else { panic!("expected program") };
240        validate_program(&program, &["len"])
241    }
242
243    #[test]
244    fn validates_source_order_constructor_return_and_lexical_this() {
245        validate(
246            r#"class Box {
247  constructor(value) { this.value = value; }
248  reader() { fn() { fn() { this.value } } }
249}
250let box = new Box(1);"#,
251        )
252        .unwrap();
253
254        assert!(validate("this")
255            .unwrap_err()
256            .message
257            .contains("only available"));
258        assert!(validate("let f = fn() { this };")
259            .unwrap_err()
260            .message
261            .contains("only available"));
262        assert!(validate("class A { constructor() { return 1; } }")
263            .unwrap_err()
264            .message
265            .contains("cannot return"));
266        assert!(validate("class A { make() { new B(); } } class B {}")
267            .unwrap_err()
268            .message
269            .contains("undefined variable 'B'"));
270        validate("class A { make() { new A(); } }").unwrap();
271        validate("len([])").unwrap();
272    }
273
274    #[test]
275    fn let_initializer_sees_only_previous_bindings_and_named_function_self() {
276        assert!(validate("let x = x;")
277            .unwrap_err()
278            .message
279            .contains("undefined variable 'x'"));
280
281        validate("let x = 1; let x = x + 1; x;").unwrap();
282        validate("let f = fn(n) { if (n == 0) { 0 } else { f(n - 1) } }; f(1);").unwrap();
283
284        assert!(validate("let f = if (true) { fn() { f(); } };")
285            .unwrap_err()
286            .message
287            .contains("undefined variable 'f'"));
288    }
289}