From 533c12cb57e7ddf7328bbdb13aa2c389736ef089 Mon Sep 17 00:00:00 2001 From: Giorgio Dell'Immagine Date: Wed, 21 Aug 2024 16:20:53 +0200 Subject: [PATCH] Improve type checker errors --- src/error.rs | 21 +++++++++++++++++ src/type_checker/checker.rs | 46 +++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/error.rs b/src/error.rs index 06268b8e0..12e984f0b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -281,12 +281,18 @@ pub enum ErrorKind { #[error("method call can only be applied on custom structs")] MethodCallOnNonCustomStruct, + #[error("field access can only be applied on custom structs")] + FieldAccessOnNonCustomStruct, + #[error("array access can only be performed on arrays")] ArrayAccessOnNonArray, #[error("struct `{0}` does not exist (are you sure it is defined?)")] UndefinedStruct(String), + #[error("struct `{0}` does not have a method called `{1}`")] + UndefinedMethod(String, String), + #[error("struct `{0}` does not have a field called `{1}`")] UndefinedField(String, String), @@ -301,4 +307,19 @@ pub enum ErrorKind { #[error("invalid hexadecimal literal `${0}`")] InvalidHexLiteral(String), + + #[error("if-else condition is expected to be a boolean, but has type `{0}`")] + IfElseInvalidConditionType(TyKind), + + #[error("`if` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.")] + IfElseInvalidIfBranch(), + + #[error("`else` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints.")] + IfElseInvalidElseBranch(), + + #[error("`if` branch and `else` branch must have matching types")] + IfElseMismatchingBranchesTypes(), + + #[error("invalid range, the end value can't be smaller than the start value")] + InvalidRange, } diff --git a/src/type_checker/checker.rs b/src/type_checker/checker.rs index 50b95110b..a7a45bbd2 100644 --- a/src/type_checker/checker.rs +++ b/src/type_checker/checker.rs @@ -90,7 +90,7 @@ impl TypeChecker { // obtain the type of the field let (module, struct_name) = match lhs_node.typ { TyKind::Custom { module, name } => (module, name), - _ => panic!("field access must be done on a custom struct"), + _ => return Err(self.error(ErrorKind::FieldAccessOnNonCustomStruct, expr.span)), }; // get struct info @@ -104,10 +104,16 @@ impl TypeChecker { .fields .iter() .find(|(name, _)| name == &rhs.value) - .map(|(_, typ)| typ.clone()) - .expect("could not find field"); + .map(|(_, typ)| typ.clone()); - Some(ExprTyInfo::new(lhs_node.var_name, res)) + if let Some(res) = res { + Some(ExprTyInfo::new(lhs_node.var_name, res)) + } else { + return Err(self.error( + ErrorKind::UndefinedField(struct_info.name.clone(), rhs.value.clone()), + expr.span, + )); + } } // `module::fn_name(args)` @@ -154,10 +160,15 @@ impl TypeChecker { .clone(); // get method info - let method_type = struct_info - .methods - .get(&method_name.value) - .expect("method not found on custom struct (TODO: better error)"); + let method_type = struct_info.methods.get(&method_name.value); + + if method_type.is_none() { + return Err(self.error( + ErrorKind::UndefinedMethod(struct_name.clone(), method_name.value.clone()), + method_name.span, + )); + } + let method_type = method_type.unwrap(); // type check the method call let method_call = true; @@ -238,7 +249,10 @@ impl TypeChecker { let rhs_typ = self.compute_type(rhs, typed_fn_env)?.unwrap(); if !rhs_typ.typ.match_expected(&lhs_node.typ) { - panic!("lhs type doesn't match rhs type (TODO: replace with error)"); + return Err(self.error( + ErrorKind::MismatchType(lhs_node.typ.clone(), rhs_typ.typ.clone()), + expr.span, + )); } None @@ -412,7 +426,10 @@ impl TypeChecker { .compute_type(cond, typed_fn_env)? .expect("can't compute type of condition"); if !matches!(cond_node.typ, TyKind::Bool) { - panic!("`if` must be followed by a boolean"); + return Err(self.error( + ErrorKind::IfElseInvalidConditionType(cond_node.typ.clone()), + cond.span, + )); } // then_ and else_ can only be variables, field accesses, or array accesses @@ -422,7 +439,7 @@ impl TypeChecker { | ExprKind::FieldAccess { .. } | ExprKind::ArrayAccess { .. } ) { - panic!("`if` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints."); + return Err(self.error(ErrorKind::IfElseInvalidIfBranch(), then_.span)); } if !matches!( @@ -431,7 +448,7 @@ impl TypeChecker { | ExprKind::FieldAccess { .. } | ExprKind::ArrayAccess { .. } ) { - panic!("`else` branch must be a variable, a field access, or an array access. It can't be logic that creates constraints."); + return Err(self.error(ErrorKind::IfElseInvalidElseBranch(), else_.span)); } // compute type of if/else branches @@ -444,10 +461,9 @@ impl TypeChecker { // make sure that the type of then_ and else_ match if then_node.typ != else_node.typ { - panic!("`if` branch and `else` branch must have matching types"); + return Err(self.error(ErrorKind::IfElseMismatchingBranchesTypes(), expr.span)); } - // Some(ExprTyInfo::new_anon(then_node.typ)) } @@ -585,7 +601,7 @@ impl TypeChecker { // ensure start..end makes sense if range.end < range.start { - panic!("end can't be smaller than start (TODO: better error)"); + return Err(self.error(ErrorKind::InvalidRange, range.span)); } // check block