From f45a6c776d8b365a7e7b9693424b7ef2d82fe735 Mon Sep 17 00:00:00 2001 From: Vlamonster Date: Wed, 8 Nov 2023 01:13:26 +0100 Subject: [PATCH] Improve `UndeclaredVar` error. --- compiler/src/passes/parse/grammar.lalrpop | 19 +++++++------- compiler/src/passes/parse/mod.rs | 4 +-- .../src/passes/validate/type_check/error.rs | 8 ++++-- .../validate/type_check/validate_expr.rs | 26 ++++++++++++------- .../validate/type_check/validate_struct.rs | 1 + .../validate/type_check/validate_type.rs | 1 + programs/fail/type_check/undeclared_var.test | 3 +++ .../type_check/undeclared_var_assign.test | 3 +++ 8 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 programs/fail/type_check/undeclared_var.test create mode 100644 programs/fail/type_check/undeclared_var_assign.test diff --git a/compiler/src/passes/parse/grammar.lalrpop b/compiler/src/passes/parse/grammar.lalrpop index 2aedfec..6274d0f 100644 --- a/compiler/src/passes/parse/grammar.lalrpop +++ b/compiler/src/passes/parse/grammar.lalrpop @@ -1,4 +1,3 @@ -use std::str::FromStr; use crate::passes::parse::{Def, TypeDef, Expr, Lit, Op, Param}; use crate::passes::parse::PrgParsed; use crate::passes::parse::Type; @@ -158,14 +157,14 @@ ExprStmt: Expr<'input> = { } ExprInStmt: Expr<'input> = { - "=" >> => Expr::Assign { + > "=" >> => Expr::Assign { sym, bnd: Box::new(bnd), }, "if" >> "{" > "}" > "}")?> => Expr::If { cnd: Box::new(cnd), thn: Box::new(thn), - els: Box::new(els.unwrap_or(Spanned { span: (l, r), expr: Expr::Lit { val: Lit::Unit }})), + els: Box::new(els.unwrap_or(Spanned { span: (l, r), inner: Expr::Lit { val: Lit::Unit }})), }, "loop" "{" > "}" => Expr::Loop { bdy: Box::new(bdy), @@ -174,22 +173,22 @@ ExprInStmt: Expr<'input> = { "while" >> "{" > "}" => Expr::Loop { bdy: Box::new(Spanned { span: (l, r), - expr: Expr::If { + inner: Expr::If { cnd: Box::new(cnd), thn: Box::new(bdy), els: Box::new(Spanned { span: (l, r), - expr: Expr::Seq { + inner: Expr::Seq { stmt: Box::new(Spanned { span: (l, r), - expr: Expr::Break { bdy: Box::new(Spanned { + inner: Expr::Break { bdy: Box::new(Spanned { span: (l, r), - expr: Expr::Lit { val: Lit::Unit }, + inner: Expr::Lit { val: Lit::Unit }, })}, }), cnt: Box::new(Spanned { span: (l, r), - expr: Expr::Lit { val: Lit::Unit }, + inner: Expr::Lit { val: Lit::Unit }, }), }, }), @@ -305,7 +304,7 @@ Struct: Expr<'input> = { StructArg : (&'input str, Spanned>) = { ":" >, - => (sym, Spanned { span: (l, r), expr: Expr::Var { sym } }) + => (sym, Spanned { span: (l, r), inner: Expr::Var { sym } }) } Never: Expr<'input> = {}; @@ -330,5 +329,5 @@ Comma: Vec = { } } -Spanned: Spanned = => Spanned { span: (l, r), expr }; +Spanned: Spanned = => Spanned { span: (l, r), inner }; diff --git a/compiler/src/passes/parse/mod.rs b/compiler/src/passes/parse/mod.rs index c6e05d3..2a0e805 100644 --- a/compiler/src/passes/parse/mod.rs +++ b/compiler/src/passes/parse/mod.rs @@ -187,7 +187,7 @@ pub enum Expr<'p> { /// Only mutable or uninitialized immutable variables can be assigned a new value. Assign { /// Symbol representing the variable to which the assignment is made. - sym: &'p str, + sym: Spanned<&'p str>, /// The expression whose result is assigned to the variable. bnd: Box>>, }, @@ -225,7 +225,7 @@ pub enum Expr<'p> { #[derive(Debug, PartialEq, Functor)] pub struct Spanned { pub span: (usize, usize), - pub expr: T, + pub inner: T, } /// A primitive operation. diff --git a/compiler/src/passes/validate/type_check/error.rs b/compiler/src/passes/validate/type_check/error.rs index c29b6fd..1478791 100644 --- a/compiler/src/passes/validate/type_check/error.rs +++ b/compiler/src/passes/validate/type_check/error.rs @@ -4,8 +4,12 @@ use thiserror::Error; #[derive(Debug, Error, Diagnostic)] pub enum TypeError { - #[error("Variable '{sym}' was not declared yet.")] - UndeclaredVar { sym: String }, + #[error("Encountered an undeclared variable.")] + UndeclaredVar { + sym: String, + #[label = "This variable `{sym}` was not declared yet"] + span: (usize, usize), + }, #[error("Types were mismatched. Expected '{expect}', but found '{got}'.")] TypeMismatchExpect { expect: Type, diff --git a/compiler/src/passes/validate/type_check/validate_expr.rs b/compiler/src/passes/validate/type_check/validate_expr.rs index 1fda42b..1f8bbfd 100644 --- a/compiler/src/passes/validate/type_check/validate_expr.rs +++ b/compiler/src/passes/validate/type_check/validate_expr.rs @@ -8,16 +8,22 @@ use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env, EnvE use crate::passes::validate::{TExpr, TLit}; use crate::utils::expect::expect; +macro_rules! s { + ($span:expr) => { + ($span.0, $span.1 - $span.0) + }; +} + pub fn validate_expr<'p>( expr: Spanned>, env: &mut Env<'_, 'p>, ) -> Result, TypeError> { - let span = (expr.span.0, expr.span.1 - expr.span.0); - - Ok(match expr.expr { + Ok(match expr.inner { Expr::Lit { val } => match val { Lit::Int { val } => { - let val = val.parse().map_err(|_| IntegerOutOfBounds { span })?; + let val = val.parse().map_err(|_| IntegerOutOfBounds { + span: s!(expr.span), + })?; TExpr::Lit { val: TLit::Int { val }, typ: Type::Int, @@ -35,6 +41,7 @@ pub fn validate_expr<'p>( Expr::Var { sym, .. } => { let entry = env.scope.get(&sym).ok_or(UndeclaredVar { sym: (*sym).to_string(), + span: s!(expr.span), })?; if let EnvEntry::Type { typ, .. } = entry { @@ -160,26 +167,27 @@ pub fn validate_expr<'p>( } } Expr::Assign { sym, bnd, .. } => { - let entry = env.scope.get(&sym).ok_or(UndeclaredVar { - sym: (*sym).to_string(), + let entry = env.scope.get(&sym.inner).ok_or(UndeclaredVar { + sym: (*sym.inner).to_string(), + span: s!(sym.span), })?; let EnvEntry::Type { typ: _, mutable } = entry else { return Err(VariableShouldBeExpr { - sym: (*sym).to_string(), + sym: (*sym.inner).to_string(), }); }; expect( *mutable, ModifyImmutable { - sym: (*sym).to_string(), + sym: (*sym.inner).to_string(), }, )?; let bnd = validate_expr(*bnd, env)?; TExpr::Assign { - sym, + sym: sym.inner, bnd: Box::new(bnd), typ: Type::Unit, } diff --git a/compiler/src/passes/validate/type_check/validate_struct.rs b/compiler/src/passes/validate/type_check/validate_struct.rs index ba5c273..3c642ac 100644 --- a/compiler/src/passes/validate/type_check/validate_struct.rs +++ b/compiler/src/passes/validate/type_check/validate_struct.rs @@ -16,6 +16,7 @@ pub fn validate_struct<'p>( ) -> Result, TypeError> { let entry = env.scope.get(&sym).ok_or(UndeclaredVar { sym: sym.to_string(), + span: (0, 0), // todo: not correct })?; #[rustfmt::skip] diff --git a/compiler/src/passes/validate/type_check/validate_type.rs b/compiler/src/passes/validate/type_check/validate_type.rs index 7e16d5d..1d9c987 100644 --- a/compiler/src/passes/validate/type_check/validate_type.rs +++ b/compiler/src/passes/validate/type_check/validate_type.rs @@ -24,6 +24,7 @@ pub fn validate_type<'p>( scope.contains(sym), UndeclaredVar { sym: sym.to_string(), + span: (0, 0), // todo: not correct }, )?; } diff --git a/programs/fail/type_check/undeclared_var.test b/programs/fail/type_check/undeclared_var.test new file mode 100644 index 0000000..9fec7b4 --- /dev/null +++ b/programs/fail/type_check/undeclared_var.test @@ -0,0 +1,3 @@ +fn main() { + x +} diff --git a/programs/fail/type_check/undeclared_var_assign.test b/programs/fail/type_check/undeclared_var_assign.test new file mode 100644 index 0000000..75e0c32 --- /dev/null +++ b/programs/fail/type_check/undeclared_var_assign.test @@ -0,0 +1,3 @@ +fn main() { + x = 2 +}