diff --git a/Cargo.lock b/Cargo.lock index 1c869ac..5e344bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -622,7 +622,7 @@ dependencies = [ "encoding8", "getrandom", "hex", - "indoc", + "indoc 1.0.9", "js-sys", "lazy_static", "linked-hash-map", @@ -1706,6 +1706,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "instant" version = "0.1.12" @@ -2823,13 +2829,13 @@ dependencies = [ "hex", "id-arena", "indexmap", + "indoc 2.0.5", "log", "num-bigint", "num-iter", "num-traits", "rowan", "rue-parser", - "thiserror", ] [[package]] @@ -2855,7 +2861,6 @@ dependencies = [ "num-traits", "rowan", "rue-lexer", - "thiserror", ] [[package]] diff --git a/crates/rue-cli/src/main.rs b/crates/rue-cli/src/main.rs index ab69da6..2af733e 100644 --- a/crates/rue-cli/src/main.rs +++ b/crates/rue-cli/src/main.rs @@ -28,7 +28,7 @@ fn main() { let line = line + 1; let col = col + 1; - eprintln!("{} at {line}:{col}", error.kind()); + eprintln!("Error: {} ({line}:{col})", error.kind()); } if args.analyze { @@ -71,10 +71,10 @@ fn print_diagnostics(source: &str, diagnostics: &[Diagnostic]) -> bool { match error.kind() { DiagnosticKind::Error(kind) => { has_error = true; - eprintln!("error: {kind} at {line}:{col}"); + eprintln!("Error: {kind} ({line}:{col})"); } DiagnosticKind::Warning(kind) => { - eprintln!("warning: {kind} at {line}:{col}"); + eprintln!("Warning: {kind} ({line}:{col})"); } } } diff --git a/crates/rue-compiler/Cargo.toml b/crates/rue-compiler/Cargo.toml index fd3748f..e355e07 100644 --- a/crates/rue-compiler/Cargo.toml +++ b/crates/rue-compiler/Cargo.toml @@ -12,9 +12,9 @@ clvmr = "0.6.1" id-arena = "2.2.1" indexmap = "2.2.6" rowan = "0.15.15" -thiserror = "1.0.58" num-traits = "0.2.18" num-iter = "0.1.44" num-bigint = "0.4.4" log = "0.4.21" hex = "0.4.3" +indoc = "2.0.5" diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index f1d9e8e..8b59c11 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -109,13 +109,13 @@ impl<'a> Compiler<'a> { } } - fn detect_cycle(&mut self, type_id: TypeId, text_range: TextRange) -> bool { - if self.db.is_cyclic(type_id) { - self.db.error(ErrorKind::RecursiveTypeAlias, text_range); - true - } else { - false + fn symbol_name(&self, symbol_id: SymbolId) -> Option { + for &scope_id in self.scope_stack.iter().rev() { + if let Some(name) = self.db.scope(scope_id).symbol_name(symbol_id) { + return Some(name.to_string()); + } } + None } fn type_name(&self, ty: TypeId) -> String { @@ -236,10 +236,7 @@ impl<'a> Compiler<'a> { if !comparison.is_assignable() { self.db.error( - ErrorKind::TypeMismatch { - expected: self.type_name(to), - found: self.type_name(from), - }, + ErrorKind::TypeMismatch(self.type_name(from), self.type_name(to)), range, ); } @@ -255,10 +252,7 @@ impl<'a> Compiler<'a> { if !comparison.is_castable() { self.db.error( - ErrorKind::CastMismatch { - expected: self.type_name(to), - found: self.type_name(from), - }, + ErrorKind::CastMismatch(self.type_name(from), self.type_name(to)), range, ); } diff --git a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs index b26f4fe..e467478 100644 --- a/crates/rue-compiler/src/compiler/expr/field_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/field_access_expr.rs @@ -44,10 +44,7 @@ impl Compiler<'_> { .extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string())) } else { self.db.error( - ErrorKind::UndefinedField { - field: field_name.to_string(), - ty: self.type_name(old_value.type_id), - }, + ErrorKind::UnknownField(field_name.to_string()), field_name.text_range(), ); return self.unknown(); @@ -74,10 +71,7 @@ impl Compiler<'_> { .extend_guard_path(old_value, GuardPathItem::Field(field_name.to_string())) } else { self.db.error( - ErrorKind::UndefinedField { - field: field_name.to_string(), - ty: self.type_name(old_value.type_id), - }, + ErrorKind::UnknownField(field_name.to_string()), field_name.text_range(), ); return self.unknown(); @@ -94,10 +88,10 @@ impl Compiler<'_> { } _ => { self.db.error( - ErrorKind::InvalidFieldAccess { - field: field_name.to_string(), - ty: self.type_name(old_value.type_id), - }, + ErrorKind::InvalidFieldAccess( + field_name.to_string(), + self.type_name(old_value.type_id), + ), field_name.text_range(), ); return self.unknown(); @@ -122,10 +116,10 @@ impl Compiler<'_> { } _ => { self.db.error( - ErrorKind::InvalidFieldAccess { - field: field_name.to_string(), - ty: self.type_name(old_value.type_id), - }, + ErrorKind::InvalidFieldAccess( + field_name.to_string(), + self.type_name(old_value.type_id), + ), field_name.text_range(), ); return self.unknown(); diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 772217f..b1b5f20 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -119,10 +119,7 @@ impl Compiler<'_> { Rest::Nil => { if args.len() != function.param_types.len() { self.db.error( - ErrorKind::ArgumentMismatch { - expected: function.param_types.len(), - found: args.len(), - }, + ErrorKind::ArgumentMismatch(args.len(), function.param_types.len()), ast.syntax().text_range(), ); } @@ -132,10 +129,7 @@ impl Compiler<'_> { && args.len() != function.param_types.len() - 1 { self.db.error( - ErrorKind::ArgumentMismatchOptional { - expected: function.param_types.len(), - found: args.len(), - }, + ErrorKind::ArgumentMismatchOptional(args.len(), function.param_types.len()), ast.syntax().text_range(), ); } @@ -148,19 +142,16 @@ impl Compiler<'_> { { if args.len() < function.param_types.len() - 1 { self.db.error( - ErrorKind::ArgumentMismatchSpread { - expected: function.param_types.len(), - found: args.len(), - }, + ErrorKind::ArgumentMismatchSpread( + args.len(), + function.param_types.len(), + ), ast.syntax().text_range(), ); } } else if args.len() != function.param_types.len() { self.db.error( - ErrorKind::ArgumentMismatch { - expected: function.param_types.len(), - found: args.len(), - }, + ErrorKind::ArgumentMismatch(args.len(), function.param_types.len()), ast.syntax().text_range(), ); } @@ -175,7 +166,7 @@ impl Compiler<'_> { if last && spread { if function.rest != Rest::Spread { self.db.error( - ErrorKind::DisallowedSpread, + ErrorKind::UnsupportedFunctionSpread, ast_args[i].syntax().text_range(), ); } else if i >= function.param_types.len() - 1 { @@ -188,8 +179,10 @@ impl Compiler<'_> { { self.type_check(arg, inner_list_type, ast_args[i].syntax().text_range()); } else if i == function.param_types.len() - 1 && !spread { - self.db - .error(ErrorKind::RequiredSpread, ast_args[i].syntax().text_range()); + self.db.error( + ErrorKind::RequiredFunctionSpread, + ast_args[i].syntax().text_range(), + ); } } else if i < function.param_types.len() { let param_type = function.param_types[i]; diff --git a/crates/rue-compiler/src/compiler/expr/guard_expr.rs b/crates/rue-compiler/src/compiler/expr/guard_expr.rs index 698ec04..b4ca9b8 100644 --- a/crates/rue-compiler/src/compiler/expr/guard_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/guard_expr.rs @@ -120,10 +120,7 @@ impl Compiler<'_> { } _ => { self.db.error( - ErrorKind::UnsupportedTypeGuard { - from: self.type_name(from), - to: self.type_name(to), - }, + ErrorKind::UnsupportedTypeGuard(self.type_name(from), self.type_name(to)), text_range, ); None diff --git a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs b/crates/rue-compiler/src/compiler/expr/index_access_expr.rs index 1e040a3..2b04cc5 100644 --- a/crates/rue-compiler/src/compiler/expr/index_access_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/index_access_expr.rs @@ -27,7 +27,7 @@ impl Compiler<'_> { let Type::List(item_type) = self.db.ty(value.type_id).clone() else { self.db.error( - ErrorKind::IndexAccess(self.type_name(value.type_id)), + ErrorKind::InvalidIndexAccess(self.type_name(value.type_id)), index_access.expr().unwrap().syntax().text_range(), ); return self.unknown(); diff --git a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs index d59ee0d..9da36c5 100644 --- a/crates/rue-compiler/src/compiler/expr/initializer_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/initializer_expr.rs @@ -20,7 +20,6 @@ impl Compiler<'_> { match ty.map(|ty| self.db.ty(ty)).cloned() { Some(Type::Struct(struct_type)) => { let hir_id = self.compile_initializer_fields( - ty.unwrap(), &struct_type.fields, struct_type.rest, initializer.fields(), @@ -35,7 +34,6 @@ impl Compiler<'_> { Some(Type::EnumVariant(enum_variant)) => { if let Some(fields) = enum_variant.fields { let fields_hir_id = self.compile_initializer_fields( - ty.unwrap(), &fields, enum_variant.rest, initializer.fields(), @@ -52,7 +50,7 @@ impl Compiler<'_> { } } else { self.db.error( - ErrorKind::EnumVariantWithoutFields, + ErrorKind::InvalidEnumVariantInitializer(self.type_name(ty.unwrap())), initializer.path().unwrap().syntax().text_range(), ); self.unknown() @@ -71,7 +69,6 @@ impl Compiler<'_> { fn compile_initializer_fields( &mut self, - struct_type: TypeId, struct_fields: &IndexMap, rest: Rest, initializer_fields: Vec, @@ -103,15 +100,12 @@ impl Compiler<'_> { // Insert the field if it exists and hasn't already been assigned. if specified_fields.contains_key(name.text()) { self.db.error( - ErrorKind::DuplicateField(name.to_string()), + ErrorKind::DuplicateInitializerField(name.to_string()), name.text_range(), ); } else if !struct_fields.contains_key(name.text()) { self.db.error( - ErrorKind::UndefinedField { - field: name.to_string(), - ty: self.type_name(struct_type), - }, + ErrorKind::UnknownInitializerField(name.to_string()), name.text_range(), ); } else { @@ -134,10 +128,7 @@ impl Compiler<'_> { if !missing_fields.is_empty() { self.db.error( - ErrorKind::MissingFields { - fields: missing_fields, - ty: self.type_name(struct_type), - }, + ErrorKind::MissingInitializerFields(missing_fields), text_range, ); } diff --git a/crates/rue-compiler/src/compiler/expr/pair_expr.rs b/crates/rue-compiler/src/compiler/expr/pair_expr.rs index 3cbebee..ad60ba3 100644 --- a/crates/rue-compiler/src/compiler/expr/pair_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/pair_expr.rs @@ -31,7 +31,7 @@ impl Compiler<'_> { // Compile the rest expression, if present. // It's a parser error if not, so it's fine to return unknown. let rest = pair - .first() + .rest() .map(|expr| { let value = self.compile_expr(&expr, rest); self.type_check( diff --git a/crates/rue-compiler/src/compiler/expr/path_expr.rs b/crates/rue-compiler/src/compiler/expr/path_expr.rs index 753422b..7d4485b 100644 --- a/crates/rue-compiler/src/compiler/expr/path_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/path_expr.rs @@ -20,12 +20,15 @@ impl Compiler<'_> { return self.unknown(); }; + let mut last_ident = idents[0].to_string(); + for (i, name) in idents.iter().enumerate().skip(1) { let Some(next_item) = self.resolve_next_path(item, name, PathKind::Symbol, i == idents.len() - 1) else { return self.unknown(); }; + last_ident = name.to_string(); item = next_item; } @@ -34,23 +37,28 @@ impl Compiler<'_> { PathItem::Type(type_id) => { if let Type::EnumVariant(variant_type) = self.db.ty(type_id).clone() { if variant_type.fields.is_some() { - self.db.error(ErrorKind::EnumVariantWithFields, text_range); + self.db.error( + ErrorKind::InvalidEnumVariantReference(self.type_name(type_id)), + text_range, + ); } return Value::new(variant_type.discriminant, type_id); } - self.db.error(ErrorKind::ExpectedSymbolPath, text_range); + self.db + .error(ErrorKind::ExpectedSymbolPath(last_ident), text_range); return self.unknown(); } }; if matches!(self.db.symbol(symbol_id), Symbol::Module(..)) { - self.db.error(ErrorKind::ModuleReference, text_range); + self.db + .error(ErrorKind::ModuleReference(last_ident), text_range); return self.unknown(); } if !self.is_callee && matches!(self.db.symbol(symbol_id), Symbol::InlineFunction(..)) { self.db - .error(ErrorKind::InlineFunctionReference, text_range); + .error(ErrorKind::InlineFunctionReference(last_ident), text_range); return self.unknown(); } diff --git a/crates/rue-compiler/src/compiler/item.rs b/crates/rue-compiler/src/compiler/item.rs index d1ffb02..19e5345 100644 --- a/crates/rue-compiler/src/compiler/item.rs +++ b/crates/rue-compiler/src/compiler/item.rs @@ -212,7 +212,7 @@ impl Compiler<'_> { if type_namespaces.contains(&name.to_string()) { self.db.error( - ErrorKind::NamespaceTakenType(name.to_string()), + ErrorKind::ModuleNameTakenByEnum(name.to_string()), name.text_range(), ); } @@ -233,7 +233,7 @@ impl Compiler<'_> { if symbol_namespaces.contains(&name.to_string()) { self.db.error( - ErrorKind::NamespaceTakenSymbol(name.to_string()), + ErrorKind::EnumNameTakenByModule(name.to_string()), name.text_range(), ); } diff --git a/crates/rue-compiler/src/compiler/item/type_alias_item.rs b/crates/rue-compiler/src/compiler/item/type_alias_item.rs index e6ca611..c37d70e 100644 --- a/crates/rue-compiler/src/compiler/item/type_alias_item.rs +++ b/crates/rue-compiler/src/compiler/item/type_alias_item.rs @@ -1,6 +1,6 @@ -use rue_parser::{AstNode, TypeAliasItem}; +use rue_parser::TypeAliasItem; -use crate::{compiler::Compiler, value::Type, TypeId}; +use crate::{compiler::Compiler, value::Type, ErrorKind, TypeId}; impl Compiler<'_> { /// Define a type for an alias in the current scope, but leave it as unknown for now. @@ -14,10 +14,10 @@ impl Compiler<'_> { } /// Compile and resolve the type that the alias points to. - pub fn compile_type_alias_item(&mut self, ty: &TypeAliasItem, alias_type_id: TypeId) { + pub fn compile_type_alias_item(&mut self, type_alias: &TypeAliasItem, alias_type_id: TypeId) { self.type_definition_stack.push(alias_type_id); - let type_id = ty + let type_id = type_alias .ty() .map_or(self.builtins.unknown, |ty| self.compile_type(ty)); @@ -26,7 +26,16 @@ impl Compiler<'_> { // A cycle between type aliases has been detected. // We set it to unknown to prevent stack overflow issues later. - if self.detect_cycle(alias_type_id, ty.syntax().text_range()) { + if self.db.is_cyclic(alias_type_id) { + let name = type_alias + .name() + .expect("the name should exist if it's in a cyclic reference"); + + self.db.error( + ErrorKind::RecursiveTypeAlias(name.to_string()), + name.text_range(), + ); + *self.db.ty_mut(alias_type_id) = Type::Unknown; } diff --git a/crates/rue-compiler/src/compiler/path.rs b/crates/rue-compiler/src/compiler/path.rs index 661a6e2..de98a7a 100644 --- a/crates/rue-compiler/src/compiler/path.rs +++ b/crates/rue-compiler/src/compiler/path.rs @@ -88,7 +88,7 @@ impl Compiler<'_> { let Some(variant_type) = enum_type.variants.get(name.text()).copied() else { self.db.error( - ErrorKind::UnknownEnumVariant(name.text().to_string()), + ErrorKind::UnknownEnumVariantPath(name.text().to_string()), name.text_range(), ); return None; @@ -99,8 +99,10 @@ impl Compiler<'_> { } PathItem::Symbol(module_id) => { let Symbol::Module(module) = self.db.symbol(module_id) else { - self.db - .error(ErrorKind::InvalidSymbolPath, name.text_range()); + self.db.error( + ErrorKind::InvalidSymbolPath(self.symbol_name(module_id)), + name.text_range(), + ); return None; }; diff --git a/crates/rue-compiler/src/compiler/ty/optional_type.rs b/crates/rue-compiler/src/compiler/ty/optional_type.rs index b3ee55b..9d06215 100644 --- a/crates/rue-compiler/src/compiler/ty/optional_type.rs +++ b/crates/rue-compiler/src/compiler/ty/optional_type.rs @@ -10,7 +10,7 @@ impl Compiler<'_> { if let Type::Optional(inner) = self.db.ty_raw(ty).clone() { self.db.warning( - WarningKind::RedundantOptional, + WarningKind::RedundantOptionalType(self.type_name(ty)), optional.syntax().text_range(), ); return inner; diff --git a/crates/rue-compiler/src/compiler/ty/path_type.rs b/crates/rue-compiler/src/compiler/ty/path_type.rs index 1dd746e..4886596 100644 --- a/crates/rue-compiler/src/compiler/ty/path_type.rs +++ b/crates/rue-compiler/src/compiler/ty/path_type.rs @@ -16,19 +16,23 @@ impl Compiler<'_> { return self.builtins.unknown; }; + let mut last_ident = idents[0].to_string(); + for (i, name) in idents.iter().enumerate().skip(1) { let Some(next_item) = self.resolve_next_path(item, name, PathKind::Type, i == idents.len() - 1) else { return self.builtins.unknown; }; + last_ident = name.to_string(); item = next_item; } match item { PathItem::Type(type_id) => type_id, PathItem::Symbol(..) => { - self.db.error(ErrorKind::ExpectedTypePath, text_range); + self.db + .error(ErrorKind::ExpectedTypePath(last_ident), text_range); self.builtins.unknown } } diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 18a723b..cb234f2 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -1,6 +1,6 @@ -use std::ops::Range; +use std::{fmt, ops::Range}; -use thiserror::Error; +use indoc::formatdoc; #[derive(Debug, Clone)] pub struct Diagnostic { @@ -36,213 +36,275 @@ pub enum DiagnosticKind { Error(ErrorKind), } -#[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum WarningKind { - #[error("unused function `{0}`")] UnusedFunction(String), - - #[error("unused inline function `{0}`")] UnusedInlineFunction(String), - - #[error("unused parameter `{0}`")] UnusedParameter(String), - - #[error("unused constant `{0}`")] UnusedConst(String), - - #[error("unused inline constant `{0}`")] UnusedInlineConst(String), - - #[error("unused let binding `{0}`")] UnusedLet(String), - - #[error("unused generic type `{0}`")] UnusedGenericType(String), - - #[error("unused enum `{0}`")] UnusedEnum(String), - - #[error("unused enum variant `{0}`")] UnusedEnumVariant(String), - - #[error("unused struct `{0}`")] UnusedStruct(String), - - #[error("unused type alias `{0}`")] UnusedTypeAlias(String), - - #[error("type is already optional")] - RedundantOptional, - - #[error("value already has type `{0}`")] + RedundantOptionalType(String), RedundantTypeCheck(String), } -#[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] -pub enum ErrorKind { - #[error("type with namespace `{0}` is already defined")] - NamespaceTakenType(String), - - #[error("symbol with namespace `{0}` is already defined")] - NamespaceTakenSymbol(String), +impl fmt::Display for WarningKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + Self::UnusedFunction(name) => format!("Unused function `{name}`."), + Self::UnusedInlineFunction(name) => format!("Unused inline function `{name}`."), + Self::UnusedParameter(name) => format!("Unused parameter `{name}`."), + Self::UnusedConst(name) => format!("Unused constant `{name}`."), + Self::UnusedInlineConst(name) => format!("Unused inline constant `{name}`."), + Self::UnusedLet(name) => format!("Unused let binding `{name}`."), + Self::UnusedGenericType(name) => format!("Unused generic type `{name}`."), + Self::UnusedEnum(name) => format!("Unused enum `{name}`."), + Self::UnusedEnumVariant(name) => format!("Unused enum variant `{name}`."), + Self::UnusedStruct(name) => format!("Unused struct `{name}`."), + Self::UnusedTypeAlias(name) => format!("Unused type alias `{name}`."), + Self::RedundantOptionalType(ty) => { + format!("This has no effect, since `{ty}` is already an optional type.") + } + Self::RedundantTypeCheck(ty) => format!( + "It's redundant to guard against `{ty}`, since the value already has that type." + ), + }; + write!(f, "{}", message.trim()) + } +} - #[error("type `{0}` is already defined")] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ErrorKind { + // Duplicate definitions. DuplicateType(String), - - #[error("symbol `{0}` is already defined")] DuplicateSymbol(String), + ModuleNameTakenByEnum(String), + EnumNameTakenByModule(String), - #[error("unknown symbol `{0}`")] + // Invalid symbol references. UnknownSymbol(String), - - #[error("unknown type `{0}`")] UnknownType(String), + InlineFunctionReference(String), + ModuleReference(String), - #[error("inline functions cannot be referenced without being called")] - InlineFunctionReference, - - #[error("modules cannot be referenced, since they are not values")] - ModuleReference, - - #[error("type aliases cannot reference themselves")] - RecursiveTypeAlias, - - #[error("expected type `{expected}`, but found `{found}`")] - TypeMismatch { expected: String, found: String }, + // Types. + RecursiveTypeAlias(String), + TypeMismatch(String, String), + CastMismatch(String, String), - #[error("cannot cast type `{found}` to `{expected}`")] - CastMismatch { expected: String, found: String }, - - #[error("cannot call expression with type `{0}`")] + // Function calls. UncallableType(String), + ArgumentMismatch(usize, usize), + ArgumentMismatchSpread(usize, usize), + ArgumentMismatchOptional(usize, usize), - #[error( - "expected {expected} argument{}, but found {found}", - if *expected == 1 { "" } else { "s" } - )] - ArgumentMismatch { expected: usize, found: usize }, - - #[error( - "expected at least {expected} argument{}, but found {found}", - if *expected == 1 { "" } else { "s" } - )] - ArgumentMismatchSpread { expected: usize, found: usize }, - - #[error("expected {} or {expected} arguments, but found {found}", expected - 1)] - ArgumentMismatchOptional { expected: usize, found: usize }, - - #[error("uninitializable type `{0}`")] + // Field initialization. UninitializableType(String), - - #[error("duplicate field `{0}`")] - DuplicateField(String), - - #[error("undefined field `{field}` on type `{ty}`")] - UndefinedField { field: String, ty: String }, - - #[error("missing fields on type `{ty}`: {}", join_names(.fields))] - MissingFields { fields: Vec, ty: String }, - - #[error("cannot access field `{field}` of non-struct type `{ty}`")] - InvalidFieldAccess { field: String, ty: String }, - - #[error("cannot index into non-list type `{0}`")] - IndexAccess(String), - - #[error("the spread operator can only be used on the last list item")] + InvalidEnumVariantInitializer(String), + InvalidEnumVariantReference(String), + DuplicateInitializerField(String), + UnknownInitializerField(String), + MissingInitializerFields(Vec), + + // Field access. + UnknownField(String), + InvalidFieldAccess(String, String), + InvalidIndexAccess(String), + + // Spread and optional. InvalidSpreadItem, - - #[error("the spread operator can only be used on the last argument")] InvalidSpreadArgument, - - #[error("the spread operator can only be used on the last parameter")] InvalidSpreadParameter, - - #[error("the spread operator can only be used on the last field")] InvalidSpreadField, - - #[error("optional can only be used on the last parameter")] InvalidOptionalParameter, - - #[error("optional can only be used on the last field")] InvalidOptionalField, - - #[error("the spread operator cannot be used on optional parameters")] OptionalParameterSpread, - - #[error("the spread operator cannot be used on optional fields")] OptionalFieldSpread, + UnsupportedFunctionSpread, + RequiredFunctionSpread, - #[error("the function does not support the spread operator")] - DisallowedSpread, - - #[error("the function requires the spread operator on the last argument")] - RequiredSpread, - - #[error("duplicate enum variant `{0}`")] + // Enum variant definitions. DuplicateEnumVariant(String), - - #[error("duplicate enum discriminant `{0}`")] DuplicateEnumDiscriminant(String), - - #[error("enum discriminant too large")] EnumDiscriminantTooLarge, - #[error("cannot directly reference enum variants with fields")] - EnumVariantWithFields, - - #[error("cannot initialize enum variants without fields")] - EnumVariantWithoutFields, - - #[error("unknown enum variant `{0}`")] - UnknownEnumVariant(String), - - #[error("could not resolve `{0}` in module")] + // Paths. + UnknownEnumVariantPath(String), UnknownModulePath(String), - - #[error("symbol `{0}` is private")] PrivateSymbol(String), - - #[error("type `{0}` is private")] PrivateType(String), - - #[error("cannot path into type `{0}`")] InvalidTypePath(String), + InvalidSymbolPath(Option), + ExpectedTypePath(String), + ExpectedSymbolPath(String), - #[error("cannot path into symbol")] - InvalidSymbolPath, - - #[error("expected path to type, but found symbol")] - ExpectedTypePath, - - #[error("expected path to symbol, but found type")] - ExpectedSymbolPath, - - #[error("cannot check type `{from}` against `{to}`")] - UnsupportedTypeGuard { from: String, to: String }, - - #[error("cannot check `Any` against pair with types other than `Any`")] + // Type guards. + UnsupportedTypeGuard(String, String), NonAnyPairTypeGuard, - - #[error("cannot check list against pair with types other than the list item type and the list itself")] NonListPairTypeGuard, - #[error("implicit return is not allowed in if statements, use an explicit return statement")] + // Blocks ImplicitReturnInIf, - - #[error("explicit return is not allowed in expressions")] ExplicitReturnInExpr, - - #[error("block missing return value")] EmptyBlock, - - #[error("cannot check equality on non-atom type `{0}`")] NonAtomEquality(String), - - #[error("integer too large to allocate in memory")] IntegerTooLarge, } +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + // Duplicate definitions. + Self::DuplicateType(name) => format!("There is already a type named `{name}` in this scope."), + Self::DuplicateSymbol(name) => format!("There is already a symbol named `{name}` in this scope."), + Self::ModuleNameTakenByEnum(name) => formatdoc!(" + There is already an enum type named `{name}` in this scope. \ + This isn't allowed to prevent ambiguity when referencing items. + "), + Self::EnumNameTakenByModule(name) => formatdoc!(" + There is already a module named `{name}` in this scope. \ + This isn't allowed to prevent ambiguity when referencing items. + "), + + // Invalid symbol references. + Self::UnknownSymbol(name) => format!("Reference to unknown symbol `{name}`."), + Self::UnknownType(name) => format!("Reference to unknown type `{name}`."), + Self::InlineFunctionReference(name) => formatdoc!(" + Cannot reference inline function `{name}`, since it is not a value. \ + Inline functions must be resolved at compile time. \ + Try calling the function instead. + "), + Self::ModuleReference(name) => formatdoc!(" + Cannot reference module `{name}`, since it is not a value. \ + Perhaps you meant to use the `::` operator to access a symbol in the module? + "), + + // Types. + Self::RecursiveTypeAlias(name) => formatdoc!(" + Cycle detected when resolving type alias `{name}`. \ + Type aliases cannot reference themselves. + "), + Self::TypeMismatch(found, expected) => format!("Expected type `{expected}`, but found `{found}`."), + Self::CastMismatch(found, expected) => format!("Cannot cast type `{found}` to `{expected}`."), + + // Function calls. + Self::UncallableType(ty) => format!("Expression with type `{ty}` cannot be called, since it is not a function."), + Self::ArgumentMismatch(found, expected) => { + format!( + "Expected {expected} argument{}, but found {found}.", + if *expected == 1 { "" } else { "s" } + ) + } + Self::ArgumentMismatchSpread (found, expected) => { + format!( + "Expected at least {expected} argument{}, but found {found}.", + if *expected == 1 { "" } else { "s" } + ) + } + Self::ArgumentMismatchOptional (found, expected)=> { + format!("Expected either {} or {expected} arguments, but found {found}.", expected - 1) + } + + // Field initialization. + Self::UninitializableType(ty) => formatdoc!(" + Cannot initializable type `{ty}`. \ + Only structs and enum variants with fields can be initialized. + "), + Self::InvalidEnumVariantReference(name) => formatdoc!(" + Cannot reference enum variant `{name}`. \ + Enum variants with fields cannot be referenced directly. \ + Consider initializing the enum variant instead. + "), + Self::InvalidEnumVariantInitializer(name) => formatdoc!(" + Cannot initialize enum variant `{name}`. \ + Enum variants without fields cannot be initialized. \ + Consider referencing the enum variant directly. + "), + Self::DuplicateInitializerField(name) => format!("Duplicate field `{name}` specified in initializer."), + Self::UnknownInitializerField(name) => format!("Unknown field `{name}` specified in initializer."), + Self::MissingInitializerFields(fields) => format!("Missing fields in initializer: {}.", join_names(fields)), + + // Field access. + Self::UnknownField(name) => format!("Cannot reference unknown field `{name}`."), + Self::InvalidFieldAccess(field, ty) => format!("Cannot reference field `{field}` of type `{ty}`."), + Self::InvalidIndexAccess(ty) => format!("Cannot index into type `{ty}`."), + + // Spread and optional. + Self::InvalidSpreadItem => formatdoc!(" + The spread operator can only be used on the last item in a list. \ + This is because it requires recursion at runtime to concatenate lists together. \ + By only allowing it on the last item by default, this additional complexity and runtime cost is avoided. + "), + Self::InvalidSpreadArgument => formatdoc!(" + The spread operator can only be used on the last argument in a function call. \ + This is because it requires recursion at runtime to concatenate lists together. \ + By only allowing it on the last item by default, this additional complexity and runtime cost is avoided. + "), + Self::InvalidSpreadParameter => formatdoc!(" + The spread operator can only be used on the last parameter in a function. \ + Otherwise, it would be ambiguous where the parameter should start and end. + "), + Self::InvalidSpreadField => formatdoc!(" + The spread operator can only be used on the last field. \ + Otherwise, it would be ambiguous where the field should start and end. + "), + Self::InvalidOptionalParameter => formatdoc!(" + Only the last parameter in a function can be optional. \ + Otherwise, it would be ambiguous which optional parameter was specified. + "), + Self::InvalidOptionalField => formatdoc!(" + Only the last field can be optional. \ + Otherwise, it would be ambiguous which optional field was specified. + "), + Self::OptionalParameterSpread => "The spread operator cannot be used on optional parameters.".to_string(), + Self::OptionalFieldSpread => "The spread operator cannot be used on optional fields.".to_string(), + Self::UnsupportedFunctionSpread => "This function does not support the spread operator on its last argument.".to_string(), + Self::RequiredFunctionSpread => "This function requires the spread operator on its last argument.".to_string(), + + // Enum variant definitions. + Self::DuplicateEnumVariant(name) => format!("Duplicate enum variant `{name}` specified."), + Self::DuplicateEnumDiscriminant(discriminant) => format!("Duplicate enum discriminant `{discriminant}` specified."), + Self::EnumDiscriminantTooLarge => "Enum discriminant is too large to allocate in CLVM.".to_string(), + + // Paths. + Self::UnknownEnumVariantPath(name) => format!("Unknown enum variant `{name}`."), + Self::UnknownModulePath(name) => format!("Could not resolve `{name}` in module."), + Self::PrivateSymbol(name) => format!("Cannot access private symbol `{name}` in module."), + Self::PrivateType(name) => format!("Cannot access private type `{name}` in module."), + Self::InvalidTypePath(ty) => format!("Cannot path into type `{ty}`."), + Self::InvalidSymbolPath(name) => if let Some(name) = name { + format!("Cannot path into symbol `{name}`.") + } else { + "Cannot path into symbol.".to_string() + }, + Self::ExpectedTypePath(name) => format!("Expected type, but found symbol `{name}` instead."), + Self::ExpectedSymbolPath(name) => format!("Expected symbol, but found type `{name}` instead."), + + // Type guards. + Self::UnsupportedTypeGuard(from, to) => format!("Cannot check type `{from}` against `{to}`."), + Self::NonAnyPairTypeGuard => "Cannot check `Any` against pair types other than `(Any, Any)`.".to_string(), + Self::NonListPairTypeGuard => "Cannot check `T[]` against pair types other than `(T, T[])`.".to_string(), + + // Blocks + Self::ImplicitReturnInIf => formatdoc!(" + Implicit returns are not allowed in if statements. \ + Either use an explicit return statement at the end of the block, \ + or raise an error. + "), + Self::ExplicitReturnInExpr => "Explicit return is not allowed within expressions.".to_string(), + Self::EmptyBlock => "Blocks must either return an expression or raise an error.".to_string(), + Self::NonAtomEquality(ty) => format!("Cannot check equality on non-atom type `{ty}`."), + Self::IntegerTooLarge => "Integer literal is too large to allocate in CLVM.".to_string(), + }; + write!(f, "{}", message.trim()) + } +} + /// Join a list of names into a string, wrapped in backticks. fn join_names(kinds: &[String]) -> String { let names: Vec = kinds.iter().map(|kind| format!("`{kind}`")).collect(); diff --git a/crates/rue-parser/Cargo.toml b/crates/rue-parser/Cargo.toml index b03b87c..daffd2d 100644 --- a/crates/rue-parser/Cargo.toml +++ b/crates/rue-parser/Cargo.toml @@ -12,4 +12,3 @@ num-derive = "0.4.2" num-traits = "0.2.18" rowan = "0.15.15" rue-lexer = { path = "../rue-lexer" } -thiserror = "1.0.58" diff --git a/crates/rue-parser/src/error.rs b/crates/rue-parser/src/error.rs index 6aa98b8..9418418 100644 --- a/crates/rue-parser/src/error.rs +++ b/crates/rue-parser/src/error.rs @@ -1,6 +1,4 @@ -use std::ops::Range; - -use thiserror::Error; +use std::{fmt, ops::Range}; use crate::SyntaxKind; @@ -24,27 +22,30 @@ impl ParserError { } } -#[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ParserErrorKind { - #[error("expected {}, found {found}", join_kinds(.expected))] - UnexpectedToken { - expected: Vec, - found: SyntaxKind, - }, - - #[error("unknown token `{0}`")] + UnexpectedToken(Vec, SyntaxKind), UnknownToken(String), - - #[error("unterminated string")] UnterminatedString, - - #[error("unterminated block comment")] UnterminatedBlockComment, - - #[error("missing digits in hex literal")] MissingHexDigits, } +impl fmt::Display for ParserErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + ParserErrorKind::UnexpectedToken(expected, found) => { + format!("Expected {}, found {}.", join_kinds(expected), found) + } + ParserErrorKind::UnknownToken(token) => format!("Unknown token `{token}`."), + ParserErrorKind::UnterminatedString => "Unterminated string literal.".to_string(), + ParserErrorKind::UnterminatedBlockComment => "Unterminated block comment.".to_string(), + ParserErrorKind::MissingHexDigits => "Missing digits in hex literal.".to_string(), + }; + write!(f, "{}", message.trim()) + } +} + /// Join a list of syntax kinds into a string, wrapped in backticks. /// Prepend the result with "one of" if there is more than one kind. fn join_kinds(kinds: &[SyntaxKind]) -> String { diff --git a/crates/rue-parser/src/parser.rs b/crates/rue-parser/src/parser.rs index 12617d1..fe8d05c 100644 --- a/crates/rue-parser/src/parser.rs +++ b/crates/rue-parser/src/parser.rs @@ -65,10 +65,7 @@ impl<'a> Parser<'a> { true } else { let found = self.nth(0); - self.push_error(ParserErrorKind::UnexpectedToken { - expected: vec![kind], - found, - }); + self.push_error(ParserErrorKind::UnexpectedToken(vec![kind], found)); self.expected_kinds.clear(); false } @@ -90,7 +87,7 @@ impl<'a> Parser<'a> { let expected: Vec = self.expected_kinds.drain(..).collect(); let found = self.nth(0); - self.push_error(ParserErrorKind::UnexpectedToken { expected, found }); + self.push_error(ParserErrorKind::UnexpectedToken(expected, found)); if self.at_end() || set.contains(&found) { return; diff --git a/crates/rue-tests/src/main.rs b/crates/rue-tests/src/main.rs index 567c027..6b5afc8 100644 --- a/crates/rue-tests/src/main.rs +++ b/crates/rue-tests/src/main.rs @@ -85,7 +85,7 @@ fn run_test(source: &str, input: &str) -> Result { let LineCol { line, col } = line_col(source, error.span().start); let line = line + 1; let col = col + 1; - format!("{} at {line}:{col}", error.kind()) + format!("Error: {} ({line}:{col})", error.kind()) }) .collect(); @@ -97,8 +97,8 @@ fn run_test(source: &str, input: &str) -> Result { let line = line + 1; let col = col + 1; match error.kind() { - DiagnosticKind::Error(kind) => format!("{kind} at {line}:{col}"), - DiagnosticKind::Warning(kind) => format!("{kind} at {line}:{col}"), + DiagnosticKind::Error(kind) => format!("Error: {kind} ({line}:{col})"), + DiagnosticKind::Warning(kind) => format!("Error: {kind} ({line}:{col})"), } }) .collect(); diff --git a/tests.toml b/tests.toml index dcf1a98..923e813 100644 --- a/tests.toml +++ b/tests.toml @@ -141,11 +141,11 @@ hash = "837525fb91b88dbf02d9ef5a0b322f6943f93424b6e8fe6d26dd1be6d3311871" [type_guards] parser_errors = [] compiler_errors = [ - "unused function `bytes_guard` at 15:5", - "unused function `bytes32_guard` at 23:5", - "unused function `public_key_guard` at 33:5", - "unused function `inverted_guard` at 43:5", - "unused function `pair_guard` at 51:5", + "Error: Unused function `bytes_guard`. (15:5)", + "Error: Unused function `bytes32_guard`. (23:5)", + "Error: Unused function `public_key_guard`. (33:5)", + "Error: Unused function `inverted_guard`. (43:5)", + "Error: Unused function `pair_guard`. (51:5)", ] [if_stmt] @@ -201,9 +201,9 @@ hash = "291e4594b43d58e833cab95e4b165c5fac6b4d8391c81ebfd20efdd8d58b92d8" [invalid_references] parser_errors = [] compiler_errors = [ - "inline functions cannot be referenced without being called at 2:22", - "unused inline function `some` at 6:12", - "unused let binding `inline_fun` at 2:9", + "Error: Cannot reference inline function `some`, since it is not a value. Inline functions must be resolved at compile time. Try calling the function instead. (2:22)", + "Error: Unused inline function `some`. (6:12)", + "Error: Unused let binding `inline_fun`. (2:9)", ] [p2_conditions] @@ -242,42 +242,42 @@ output = "42" hash = "ce97d04b4f84066533255fcc56345626c245afd50e24c6c14c52c8e78e8b9f33" [pair_expr] -parser_errors = [] -compiler_errors = [ - "expected type `Bytes`, but found `Int` at 2:31", - "expected type `(Int, Bytes)`, but found `(Int, Int)` at 2:5", -] +bytes = 38 +cost = 1588 +input = "()" +output = "72" +hash = "945e2133d0b2424b4811f3b292b98a94a339588457b014d34fcd29be27f49410" [duplicate_variants] parser_errors = [] compiler_errors = [ - "duplicate enum variant `SameName` at 3:5", - "duplicate enum discriminant `1` at 4:29", - "unused enum variant `SameName` at 2:5", - "unused enum variant `DuplicateDiscriminant` at 4:5", + "Error: Duplicate enum variant `SameName` specified. (3:5)", + "Error: Duplicate enum discriminant `1` specified. (4:29)", + "Error: Unused enum variant `SameName`. (2:5)", + "Error: Unused enum variant `DuplicateDiscriminant`. (4:5)", ] [unused_symbols] parser_errors = [] compiler_errors = [ - "unused inline constant `UNUSED_INDIRECT_CONSTANT` at 1:14", - "unused inline constant `UNUSED_CONSTANT` at 2:14", - "unused function `unused_indirect_function` at 21:5", - "unused function `unused_function` at 25:5", - "unused inline constant `UNUSED_INNER_CONSTANT` at 6:18", - "unused function `unused_inner_function` at 8:9", - "unused let binding `unused_let` at 5:9", + "Error: Unused inline constant `UNUSED_INDIRECT_CONSTANT`. (1:14)", + "Error: Unused inline constant `UNUSED_CONSTANT`. (2:14)", + "Error: Unused function `unused_indirect_function`. (21:5)", + "Error: Unused function `unused_function`. (25:5)", + "Error: Unused inline constant `UNUSED_INNER_CONSTANT`. (6:18)", + "Error: Unused function `unused_inner_function`. (8:9)", + "Error: Unused let binding `unused_let`. (5:9)", ] [unused_types] parser_errors = [] compiler_errors = [ - "unused type alias `A` at 2:6", - "unused enum variant `A` at 7:5", - "unused enum variant `B` at 10:5", - "unused enum variant `C` at 14:5", - "unused enum `Some` at 6:6", - "unused struct `Thing` at 17:8", + "Error: Unused type alias `A`. (2:6)", + "Error: Unused enum variant `A`. (7:5)", + "Error: Unused enum variant `B`. (10:5)", + "Error: Unused enum variant `C`. (14:5)", + "Error: Unused enum `Some`. (6:6)", + "Error: Unused struct `Thing`. (17:8)", ] [not] @@ -304,47 +304,47 @@ hash = "54de8fd5de2b5e54d4e396d06821f9e3a131f737383fc66900f6a5af4923e498" [function_call] parser_errors = [] compiler_errors = [ - "expected 0 arguments, but found 1 at 3:11", - "expected 0 or 1 arguments, but found 2 at 7:11", - "the function does not support the spread operator at 8:25", - "expected 0 or 1 arguments, but found 2 at 9:11", - "the function does not support the spread operator at 9:28", - "expected 1 or 2 arguments, but found 0 at 11:11", - "expected 1 or 2 arguments, but found 3 at 14:11", - "expected 1 or 2 arguments, but found 4 at 15:11", - "the function does not support the spread operator at 16:25", - "the function does not support the spread operator at 17:28", - "expected 1 or 2 arguments, but found 3 at 18:11", - "the function does not support the spread operator at 18:31", - "expected 1 or 2 arguments, but found 4 at 19:11", - "the function does not support the spread operator at 19:34", - "the function does not support the spread operator at 20:28", - "expected type `Int[]`, but found `Int` at 26:28", - "expected type `Int[]`, but found `Int` at 27:31", - "expected at least 2 arguments, but found 0 at 31:11", - "expected type `Int[]`, but found `Int` at 36:31", - "expected 1 argument, but found 0 at 40:11", - "the function requires the spread operator on the last argument at 41:27", - "expected 1 argument, but found 2 at 42:11", - "the function requires the spread operator on the last argument at 42:27", - "expected 1 argument, but found 2 at 44:11", - "expected type `Int`, but found `Int[]` at 45:27", - "expected 1 argument, but found 2 at 46:11", - "expected type `Int`, but found `Int[]` at 46:30", - "expected 2 arguments, but found 0 at 48:11", - "expected 2 arguments, but found 1 at 49:11", - "the function requires the spread operator on the last argument at 50:30", - "expected 2 arguments, but found 1 at 51:11", - "expected 2 arguments, but found 1 at 53:11", - "expected type `Int`, but found `Int[]` at 54:30", + "Error: Expected 0 arguments, but found 1. (3:11)", + "Error: Expected either 0 or 1 arguments, but found 2. (7:11)", + "Error: This function does not support the spread operator on its last argument. (8:25)", + "Error: Expected either 0 or 1 arguments, but found 2. (9:11)", + "Error: This function does not support the spread operator on its last argument. (9:28)", + "Error: Expected either 1 or 2 arguments, but found 0. (11:11)", + "Error: Expected either 1 or 2 arguments, but found 3. (14:11)", + "Error: Expected either 1 or 2 arguments, but found 4. (15:11)", + "Error: This function does not support the spread operator on its last argument. (16:25)", + "Error: This function does not support the spread operator on its last argument. (17:28)", + "Error: Expected either 1 or 2 arguments, but found 3. (18:11)", + "Error: This function does not support the spread operator on its last argument. (18:31)", + "Error: Expected either 1 or 2 arguments, but found 4. (19:11)", + "Error: This function does not support the spread operator on its last argument. (19:34)", + "Error: This function does not support the spread operator on its last argument. (20:28)", + "Error: Expected type `Int[]`, but found `Int`. (26:28)", + "Error: Expected type `Int[]`, but found `Int`. (27:31)", + "Error: Expected at least 2 arguments, but found 0. (31:11)", + "Error: Expected type `Int[]`, but found `Int`. (36:31)", + "Error: Expected 1 argument, but found 0. (40:11)", + "Error: This function requires the spread operator on its last argument. (41:27)", + "Error: Expected 1 argument, but found 2. (42:11)", + "Error: This function requires the spread operator on its last argument. (42:27)", + "Error: Expected 1 argument, but found 2. (44:11)", + "Error: Expected type `Int`, but found `Int[]`. (45:27)", + "Error: Expected 1 argument, but found 2. (46:11)", + "Error: Expected type `Int`, but found `Int[]`. (46:30)", + "Error: Expected 2 arguments, but found 0. (48:11)", + "Error: Expected 2 arguments, but found 1. (49:11)", + "Error: This function requires the spread operator on its last argument. (50:30)", + "Error: Expected 2 arguments, but found 1. (51:11)", + "Error: Expected 2 arguments, but found 1. (53:11)", + "Error: Expected type `Int`, but found `Int[]`. (54:30)", ] [field_guards] -bytes = 161 -cost = 2133 +bytes = 153 +cost = 1943 input = "()" output = "()" -hash = "3878fc2ed6b703c7c3d00f9f5d8f170ec252ca439be6e5b5b516e1dce1adc0d7" +hash = "42d769a7d2365e83a02528d601e7ee728325cf2c2f89ea012e9190e63c26638a" [bound_guards] bytes = 43 @@ -371,11 +371,11 @@ error = "()" [unit_variant] parser_errors = [] compiler_errors = [ - "unused let binding `non_unit` at 7:9", - "unused let binding `unit` at 8:9", - "unused enum variant `NonUnit` at 2:5", - "unused enum variant `Unit` at 3:5", - "unused enum `Enum` at 1:6", + "Error: Unused let binding `non_unit`. (7:9)", + "Error: Unused let binding `unit`. (8:9)", + "Error: Unused enum variant `NonUnit`. (2:5)", + "Error: Unused enum variant `Unit`. (3:5)", + "Error: Unused enum `Enum`. (1:6)", ] [royalty_split] @@ -388,16 +388,16 @@ hash = "83edaa46ef48ec8bbc8be46b4dd0485ee870fed36f7e51fad3f714cda94bd14c" [list_types] parser_errors = [] compiler_errors = [ - "unused let binding `empty` at 2:9", - "unused let binding `empty_hinted` at 3:9", + "Error: Unused let binding `empty`. (2:9)", + "Error: Unused let binding `empty_hinted`. (3:9)", ] [pair_types] bytes = 57 cost = 3035 input = "()" -output = "80" -hash = "91cecc8bd72dfaf559214ff324f9df7f45c652a96b8f5ae5e3b7fae0dd851014" +output = "88" +hash = "aba8a83e1a912b2a232cbed31ac0686b717bec369377051d98caf9b5c00a4818" [nested_scopes] bytes = 421 @@ -417,23 +417,23 @@ hash = "770bb2c0b2582924adf403e62374f8424a2ed510eef70b5f450eccab238a4911" [implicit_return_if_stmt] parser_errors = [] compiler_errors = [ - "implicit return is not allowed in if statements, use an explicit return statement at 2:19", - "block missing return value at 10:19", - "implicit return is not allowed in if statements, use an explicit return statement at 10:19", - "block missing return value at 9:22", - "block missing return value at 16:19", - "implicit return is not allowed in if statements, use an explicit return statement at 16:19", - "unused function `another` at 9:5", - "unused function `yet_another` at 15:5", - "unused function `raise_ok` at 24:5", + "Error: Implicit returns are not allowed in if statements. Either use an explicit return statement at the end of the block, or raise an error. (2:19)", + "Error: Blocks must either return an expression or raise an error. (10:19)", + "Error: Implicit returns are not allowed in if statements. Either use an explicit return statement at the end of the block, or raise an error. (10:19)", + "Error: Blocks must either return an expression or raise an error. (9:22)", + "Error: Blocks must either return an expression or raise an error. (16:19)", + "Error: Implicit returns are not allowed in if statements. Either use an explicit return statement at the end of the block, or raise an error. (16:19)", + "Error: Unused function `another`. (9:5)", + "Error: Unused function `yet_another`. (15:5)", + "Error: Unused function `raise_ok`. (24:5)", ] [recursive_type_alias] parser_errors = [] compiler_errors = [ - "type aliases cannot reference themselves at 4:1", - "type aliases cannot reference themselves at 7:1", - "unused type alias `E` at 6:6", + "Error: Cycle detected when resolving type alias `D`. Type aliases cannot reference themselves. (4:6)", + "Error: Cycle detected when resolving type alias `F`. Type aliases cannot reference themselves. (7:6)", + "Error: Unused type alias `E`. (6:6)", ] [and_guard]