Resolver - Better errors when 'super' is used where it shouldn't

This commit is contained in:
Emmanuel BENOîT 2023-01-17 07:39:53 +01:00
parent 18d9bfb74c
commit 3861062565

View file

@ -54,6 +54,17 @@ enum ScopeType {
Method, 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. /// General information about a symbol.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct SymInfo<'a> { struct SymInfo<'a> {
@ -79,6 +90,8 @@ struct ResolverState<'a> {
scopes: Vec<SymScope<'a>>, scopes: Vec<SymScope<'a>>,
/// The result of the resolver pass. /// The result of the resolver pass.
resolved: ResolvedVariables, resolved: ResolvedVariables,
/// The type of class being visited.
current_class_type: ClassType,
} }
impl<'a> SymScope<'a> { 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> { impl<'a> ResolverState<'a> {
/// Execute some function with a new scope. The scope will be disposed /// Execute some function with a new scope. The scope will be disposed
/// of after the function has been executed. /// of after the function has been executed.
@ -397,14 +416,20 @@ impl VarResolver for StmtNode {
StmtNode::ClassDecl(decl) => { StmtNode::ClassDecl(decl) => {
rs.declare(&decl.name, SymKind::Class)?; rs.declare(&decl.name, SymKind::Class)?;
let enclosing = rs.current_class_type;
if let Some(superclass) = &decl.superclass { if let Some(superclass) = &decl.superclass {
rs.resolve_use(superclass)?; rs.resolve_use(superclass)?;
rs.current_class_type = ClassType::Subclass;
} else {
rs.current_class_type = ClassType::Class;
} }
rs.define(&decl.name); rs.define(&decl.name);
rs.with_scope( let result = rs.with_scope(
|rs| resolve_class(rs, decl.superclass.is_some(), &decl.members), |rs| resolve_class(rs, decl.superclass.is_some(), &decl.members),
rs.current_type(), rs.current_type(),
) );
rs.current_class_type = enclosing;
result
} }
StmtNode::If { StmtNode::If {
@ -503,7 +528,16 @@ impl VarResolver for ExprNode {
.resolve(rs) .resolve(rs)
.and_then(|_| set_expr.value.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",
),
},
} }
} }
} }