Refactor 'UnknownVariable' and 'UnknownTypeConstructor' as smart-constructor.
This commit is contained in:
parent
aa2a235790
commit
dca633da48
|
@ -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) => {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue