From 9d3ba49d9d811421eb19eb0e8325576ae3436fe8 Mon Sep 17 00:00:00 2001 From: Fred Roth Date: Tue, 9 Apr 2024 22:06:14 +0200 Subject: [PATCH] Fail static analysis on this outside of class --- src/lox.rs | 2 ++ src/resolver/mod.rs | 33 +++++++++++++++++-- src/resolver/resolution_error.rs | 7 ++++ .../resolver_errors/this_outside_of_class.lox | 22 +++++++++++++ tests/this.lox | 2 +- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/resolver_errors/this_outside_of_class.lox diff --git a/src/lox.rs b/src/lox.rs index a104acc..2eacf31 100644 --- a/src/lox.rs +++ b/src/lox.rs @@ -53,6 +53,8 @@ impl Lox { eprintln!("No statement found. Fallback to expression:"); eprintln!("{}", expr); } + let locals = Resolver::resolve_expression(expr, self.verbose)?; + self.interpreter.add_locals(locals); let result = self.interpreter.interpret_expr(expr)?; Ok(Some(result)) } diff --git a/src/resolver/mod.rs b/src/resolver/mod.rs index 09afe07..5ded90c 100644 --- a/src/resolver/mod.rs +++ b/src/resolver/mod.rs @@ -16,6 +16,7 @@ pub struct Resolver { locals: HashMap, scopes: Vec>, current_function: Option, + current_class: Option, } #[derive(Debug)] @@ -24,6 +25,11 @@ enum FunctionType { Method, } +#[derive(Debug)] +enum ClassType { + Class, +} + type Result = std::result::Result; impl Resolver { @@ -36,6 +42,18 @@ impl Resolver { Ok(resolver.locals) } + pub fn resolve_expression( + expression: &Expr, + verbose: bool, + ) -> Result> { + let mut resolver = Resolver::default(); + resolver.resolve_expr(expression)?; + if verbose { + eprintln!("{:?}", resolver.locals); + } + Ok(resolver.locals) + } + fn resolve_statements(&mut self, statements: &[Stmt]) -> Result<()> { statements .iter() @@ -113,6 +131,7 @@ impl Resolver { } fn resolve_class(&mut self, name: &Name, methods: &[Function]) -> Result<()> { + let enclosing_class = std::mem::replace(&mut self.current_class, Some(ClassType::Class)); self.declare(name); self.define(name); self.begin_scope(); @@ -122,6 +141,7 @@ impl Resolver { self.resolve_function(&m.parameters, &m.body, FunctionType::Method) })?; self.end_scope(); + self.current_class = enclosing_class; Ok(()) } @@ -171,9 +191,16 @@ impl Resolver { } fn resolve_this(&mut self, location: SourceSpan, src: &Arc>) -> Result<()> { - let name_expr = NameExpr::this(location, src.clone()); - self.resolve_local(&name_expr); - Ok(()) + if self.current_class.is_none() { + Err(ResolutionError::InvalidThis { + src: src.clone(), + location, + }) + } else { + let name_expr = NameExpr::this(location, src.clone()); + self.resolve_local(&name_expr); + Ok(()) + } } fn declare(&mut self, name: &Name) { diff --git a/src/resolver/resolution_error.rs b/src/resolver/resolution_error.rs index 8b43686..f93a75f 100644 --- a/src/resolver/resolution_error.rs +++ b/src/resolver/resolution_error.rs @@ -21,4 +21,11 @@ pub enum ResolutionError { #[label("here")] location: SourceSpan, }, + #[error("Can't use 'this' outside of a class.")] + InvalidThis { + #[source_code] + src: Arc>, + #[label("here")] + location: SourceSpan, + }, } diff --git a/tests/resolver_errors/this_outside_of_class.lox b/tests/resolver_errors/this_outside_of_class.lox new file mode 100644 index 0000000..cd5a269 --- /dev/null +++ b/tests/resolver_errors/this_outside_of_class.lox @@ -0,0 +1,22 @@ +error +this; +---- +---- +{ + "causes": [], + "filename": "tests/resolver_errors/this_outside_of_class.lox", + "labels": [ + { + "label": "here", + "span": { + "length": 4, + "offset": 0 + } + } + ], + "message": "Can't use 'this' outside of a class.", + "related": [], + "severity": "error" +} +---- +---- (no newline) diff --git a/tests/this.lox b/tests/this.lox index 565f73a..d4f66e7 100644 --- a/tests/this.lox +++ b/tests/this.lox @@ -12,4 +12,4 @@ var callback = thing.getCallback(); thing.name = "my_thing"; callback(); ---- -my_thing \ No newline at end of file +my_thing