From 3861062565735a5cf88be3c29e51dc2f45247ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Tue, 17 Jan 2023 07:39:53 +0100 Subject: [PATCH] Resolver - Better errors when 'super' is used where it shouldn't --- src/resolver.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/resolver.rs b/src/resolver.rs index 34ef932..31ed0ff 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -54,6 +54,17 @@ enum ScopeType { Method, } +/// The type of a class. +#[derive(Clone, Debug, PartialEq, Eq, Copy)] +enum ClassType { + /// No class is being visited. + None, + /// A top-level class is being visited. + Class, + /// A sub-class is being visited. + Subclass, +} + /// General information about a symbol. #[derive(Clone, Debug)] struct SymInfo<'a> { @@ -79,6 +90,8 @@ struct ResolverState<'a> { scopes: Vec>, /// The result of the resolver pass. resolved: ResolvedVariables, + /// The type of class being visited. + current_class_type: ClassType, } impl<'a> SymScope<'a> { @@ -91,6 +104,12 @@ impl<'a> SymScope<'a> { } } +impl Default for ClassType { + fn default() -> Self { + ClassType::None + } +} + impl<'a> ResolverState<'a> { /// Execute some function with a new scope. The scope will be disposed /// of after the function has been executed. @@ -397,14 +416,20 @@ impl VarResolver for StmtNode { StmtNode::ClassDecl(decl) => { rs.declare(&decl.name, SymKind::Class)?; + let enclosing = rs.current_class_type; if let Some(superclass) = &decl.superclass { rs.resolve_use(superclass)?; + rs.current_class_type = ClassType::Subclass; + } else { + rs.current_class_type = ClassType::Class; } rs.define(&decl.name); - rs.with_scope( + let result = rs.with_scope( |rs| resolve_class(rs, decl.superclass.is_some(), &decl.members), rs.current_type(), - ) + ); + rs.current_class_type = enclosing; + result } StmtNode::If { @@ -503,7 +528,16 @@ impl VarResolver for ExprNode { .resolve(rs) .and_then(|_| set_expr.value.resolve(rs)), - ExprNode::Super(expr) => rs.resolve_use(&expr.keyword), + ExprNode::Super(expr) => match rs.current_class_type { + ClassType::Subclass => rs.resolve_use(&expr.keyword), + ClassType::None => { + rs.error(&expr.keyword.token, "can't use 'super' outside of a class") + } + ClassType::Class => rs.error( + &expr.keyword.token, + "can't use 'super' in a top-level class", + ), + }, } } }