Resolver - Better errors when 'super' is used where it shouldn't
This commit is contained in:
parent
18d9bfb74c
commit
3861062565
1 changed files with 37 additions and 3 deletions
|
@ -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",
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue