From 068278146048ae99ae3292de0a507bc9f3fda1e6 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Thu, 22 Dec 2022 19:31:43 +0100 Subject: [PATCH] Better errors when using unknown data-type constructor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Before ``` × Checking ╰─▶ Unknown variable Finite ╭─[../stdlib/validators/tmp.ak:10:1] 10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is { 11 │ Finite { t } -> t · ──────────── 12 │ NegativeInfinity -> 0 ╰──── ``` ## After ``` × Type-checking ╰─▶ Unknown data-type constructor 'Finite' ╭─[../stdlib/validators/tmp.ak:10:1] 10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is { 11 │ Finite { t } -> t · ──────────── 12 │ NegativeInfinity -> 0 ╰──── help: Did you forget to import it? Data-type constructors are not automatically imported, even if their type is imported. So, if a module `aiken/pet` defines the following type: ┍━ aiken/pet.ak ━━━━━━━━ │ pub type Pet { │ Cat │ Dog │ } You must import its constructors explicitly to use them, or prefix them with the module's name. ┍━ foo.ak ━━━━━━━━ │ use aiken/pet.{Pet, Dog} │ │ fn foo(pet : Pet) { │ when pet is { │ pet.Cat -> // ... │ Dog -> // ... │ } │ } ``` --- crates/aiken-lang/src/tipo/environment.rs | 18 +++++++++--- crates/aiken-lang/src/tipo/error.rs | 34 +++++++++++++++++++++++ crates/aiken-project/src/error.rs | 2 +- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/crates/aiken-lang/src/tipo/environment.rs b/crates/aiken-lang/src/tipo/environment.rs index 491d3953..d0992f4b 100644 --- a/crates/aiken-lang/src/tipo/environment.rs +++ b/crates/aiken-lang/src/tipo/environment.rs @@ -315,10 +315,20 @@ impl<'a> Environment<'a> { location: Span, ) -> Result<&ValueConstructor, Error> { match module { - None => self.scope.get(name).ok_or_else(|| Error::UnknownVariable { - name: name.to_string(), - variables: self.local_value_names(), - location, + None => self.scope.get(name).ok_or_else(|| { + if name.chars().into_iter().next().unwrap().is_uppercase() { + Error::UnknownTypeConstructor { + name: name.to_string(), + variables: self.local_value_names(), + location, + } + } else { + Error::UnknownVariable { + name: name.to_string(), + variables: self.local_value_names(), + location, + } + } }), Some(m) => { diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 24b21d80..19dd2189 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -225,6 +225,40 @@ pub enum Error { variables: Vec, }, + #[error("Unknown data-type constructor '{name}'\n")] + #[diagnostic(help( + r#"Did you forget to import it? + +Data-type constructors are not automatically imported, even if their type is +imported. So, if a module `aiken/pet` defines the following type: + + ┍━ aiken/pet.ak ━━━━━━━━ + │ pub type Pet {{ + │ Cat + │ Dog + │ }} + +You must import its constructors explicitly to use them, or prefix them +with the module's name. + + ┍━ foo.ak ━━━━━━━━ + │ use aiken/pet.{{Pet, Dog}} + │ + │ fn foo(pet : Pet) {{ + │ when pet is {{ + │ pet.Cat -> // ... + │ Dog -> // ... + │ }} + │ }} +"# + ))] + UnknownTypeConstructor { + #[label] + location: Span, + name: String, + variables: Vec, + }, + #[error("Unnecessary spread operator\n")] UnnecessarySpreadOperator { #[label] diff --git a/crates/aiken-project/src/error.rs b/crates/aiken-project/src/error.rs index 213a7e73..38455c2e 100644 --- a/crates/aiken-project/src/error.rs +++ b/crates/aiken-project/src/error.rs @@ -74,7 +74,7 @@ pub enum Error { error: Box, }, - #[error("Checking")] + #[error("Type-checking")] Type { path: PathBuf, src: String,