Refactor 'UnknownVariable' and 'UnknownTypeConstructor' as smart-constructor.

This commit is contained in:
KtorZ 2022-12-23 00:05:48 +01:00
parent aa2a235790
commit dca633da48
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
4 changed files with 76 additions and 50 deletions

View File

@ -317,19 +317,7 @@ impl<'a> Environment<'a> {
) -> Result<&ValueConstructor, Error> { ) -> Result<&ValueConstructor, Error> {
match module { match module {
None => self.scope.get(name).ok_or_else(|| { None => self.scope.get(name).ok_or_else(|| {
if name.chars().into_iter().next().unwrap().is_uppercase() { Error::unknown_variable_or_type(location, name, self.local_value_names())
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) => { Some(m) => {

View File

@ -225,46 +225,24 @@ pub enum Error {
types: Vec<String>, types: Vec<String>,
}, },
#[error("Unknown variable\n\n {name}\n")] #[error("Unknown variable '{name}'\n")]
#[diagnostic()]
UnknownVariable { UnknownVariable {
#[label] #[label]
location: Span, location: Span,
name: String, name: String,
variables: Vec<String>, #[help]
hint: String,
}, },
#[error("Unknown data-type constructor '{name}'\n")] #[error("Unknown data-type constructor '{name}'\n")]
#[diagnostic(help( #[diagnostic()]
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 { UnknownTypeConstructor {
#[label] #[label]
location: Span, location: Span,
name: String, name: String,
variables: Vec<String>, #[help]
hint: String,
}, },
#[error("Unnecessary spread operator\n")] #[error("Unnecessary spread operator\n")]
@ -455,6 +433,62 @@ impl Error {
hint, hint,
} }
} }
pub fn unknown_variable_or_type(location: Span, name: &str, variables: Vec<String>) -> Self {
let hint = variables
.iter()
.map(|s| (s, levenshtein::distance(name, s)))
.min_by(|(_, a), (_, b)| a.cmp(b))
.and_then(|(suggestion, distance)| {
if distance <= 3 {
Some(format!("Did you mean '{suggestion}'?"))
} else {
None
}
});
if name.chars().into_iter().next().unwrap().is_uppercase() {
let hint = hint.unwrap_or_else(|| {
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 -> // ...
}}
}}"#
.to_string()
});
Self::UnknownTypeConstructor {
name: name.to_string(),
hint,
location,
}
} else {
let hint = hint.unwrap_or_else(|| "Did you forget to import it?".to_string());
Self::UnknownVariable {
name: name.to_string(),
location,
hint,
}
}
}
} }
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)] #[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]

View File

@ -1781,10 +1781,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
self.environment self.environment
.get_variable(name) .get_variable(name)
.cloned() .cloned()
.ok_or_else(|| Error::UnknownVariable { .ok_or_else(|| {
location: *location, Error::unknown_variable_or_type(
name: name.to_string(), *location,
variables: self.environment.local_value_names(), name,
self.environment.local_value_names(),
)
})?; })?;
// Note whether we are using an ungeneralised function so that we can // Note whether we are using an ungeneralised function so that we can

View File

@ -242,10 +242,12 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
.environment .environment
.get_variable(&name) .get_variable(&name)
.cloned() .cloned()
.ok_or_else(|| Error::UnknownVariable { .ok_or_else(|| {
Error::unknown_variable_or_type(
location, location,
name: name.to_string(), &name,
variables: self.environment.local_value_names(), self.environment.local_value_names(),
)
})?; })?;
self.environment.increment_usage(&name); self.environment.increment_usage(&name);