Skip to main content

parser/
validation.rs

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