Make 'UnexpectedLabelArg' errors more helpful
## Before ``` × Checking ╰─▶ Unexpected labeled argument t ╭─[/Users/mati/Devel/OpenSource/time_lock_aiken/validators/time_lock.ak:13:1] 13 │ let now = when context.transaction.validity_range.lower_bound.bound_type is { 14 │ Finite { t } -> t · ─ 15 │ NegativeInfinity -> 0 ╰──── ``` ## After ``` × Type-checking ╰─▶ Unexpected labeled argument 't' ╭─[../stdlib/validators/tmp.ak:10:1] 10 │ let now = when context.transaction.validity_range.lower_bound.bound_type is { 11 │ interval.Finite { t } -> t · ─ 12 │ interval.NegativeInfinity -> 0 ╰──── help: The constructor 'Finite' does not have any labeled field. Its fields must therefore be matched only by position. Perhaps, try the following: ╰─▶ interval.Finite(t) ```
This commit is contained in:
parent
0682781460
commit
666761efef
|
@ -796,7 +796,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_constructor<'a>(
|
pub fn pattern_constructor<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
args: &'a [CallArg<UntypedPattern>],
|
args: &'a [CallArg<UntypedPattern>],
|
||||||
|
@ -1478,7 +1478,7 @@ impl<'comments> Formatter<'comments> {
|
||||||
list(elements_document, elements.len(), tail)
|
list(elements_document, elements.len(), tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern<'a>(&mut self, pattern: &'a UntypedPattern) -> Document<'a> {
|
pub fn pattern<'a>(&mut self, pattern: &'a UntypedPattern) -> Document<'a> {
|
||||||
let comments = self.pop_comments(pattern.location().start);
|
let comments = self.pop_comments(pattern.location().start);
|
||||||
let doc = match pattern {
|
let doc = match pattern {
|
||||||
Pattern::Int { value, .. } => value.to_doc(),
|
Pattern::Int { value, .. } => value.to_doc(),
|
||||||
|
|
|
@ -10,9 +10,10 @@ use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, Pattern,
|
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, Pattern,
|
||||||
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
|
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
|
||||||
UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE,
|
UnqualifiedImport, UntypedDefinition, UntypedPattern, Use, PIPE_VARIABLE,
|
||||||
},
|
},
|
||||||
builtins::{self, function, generic_var, tuple, unbound_var},
|
builtins::{self, function, generic_var, tuple, unbound_var},
|
||||||
|
format::Formatter,
|
||||||
tipo::fields::FieldMap,
|
tipo::fields::FieldMap,
|
||||||
IdGenerator,
|
IdGenerator,
|
||||||
};
|
};
|
||||||
|
@ -1603,12 +1604,53 @@ fn assert_unique_const_name<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn assert_no_labeled_arguments_in_pattern<'a>(
|
||||||
|
name: &str,
|
||||||
|
args: &[CallArg<UntypedPattern>],
|
||||||
|
module: &'a Option<String>,
|
||||||
|
with_spread: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
for arg in args {
|
||||||
|
if let Some(label) = &arg.label {
|
||||||
|
let fixed_args = args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| CallArg {
|
||||||
|
label: None,
|
||||||
|
location: arg.location,
|
||||||
|
value: arg.value.clone(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let suggestion = Formatter::new()
|
||||||
|
.pattern_constructor(name, &fixed_args, module, with_spread, false)
|
||||||
|
.to_pretty_string(70);
|
||||||
|
|
||||||
|
let hint = format!(
|
||||||
|
r#"The constructor '{name}' does not have any labeled field. Its fields
|
||||||
|
must therefore be matched only by position.
|
||||||
|
|
||||||
|
Perhaps, try the following:
|
||||||
|
|
||||||
|
╰─▶ {suggestion}"#
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(Error::UnexpectedLabeledArg {
|
||||||
|
location: arg.location,
|
||||||
|
label: label.to_string(),
|
||||||
|
hint: Some(hint),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Result<(), Error> {
|
pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Result<(), Error> {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if let Some(label) = &arg.label {
|
if let Some(label) = &arg.label {
|
||||||
return Err(Error::UnexpectedLabeledArg {
|
return Err(Error::UnexpectedLabeledArg {
|
||||||
location: arg.location,
|
location: arg.location,
|
||||||
label: label.to_string(),
|
label: label.to_string(),
|
||||||
|
hint: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,11 +144,14 @@ pub enum Error {
|
||||||
#[error("{name} is a reserved module name\n")]
|
#[error("{name} is a reserved module name\n")]
|
||||||
ReservedModuleName { name: String },
|
ReservedModuleName { name: String },
|
||||||
|
|
||||||
#[error("Unexpected labeled argument\n\n{label}\n")]
|
#[error("Unexpected labeled argument '{label}'\n")]
|
||||||
|
#[diagnostic()]
|
||||||
UnexpectedLabeledArg {
|
UnexpectedLabeledArg {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
label: String,
|
label: String,
|
||||||
|
#[help]
|
||||||
|
hint: Option<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("Unexpected type hole\n")]
|
#[error("Unexpected type hole\n")]
|
||||||
|
@ -229,8 +232,8 @@ pub enum Error {
|
||||||
#[diagnostic(help(
|
#[diagnostic(help(
|
||||||
r#"Did you forget to import it?
|
r#"Did you forget to import it?
|
||||||
|
|
||||||
Data-type constructors are not automatically imported, even if their type is
|
Data-type constructors are not automatically imported, even if their type
|
||||||
imported. So, if a module `aiken/pet` defines the following type:
|
is imported. So, if a module `aiken/pet` defines the following type:
|
||||||
|
|
||||||
┍━ aiken/pet.ak ━━━━━━━━
|
┍━ aiken/pet.ak ━━━━━━━━
|
||||||
│ pub type Pet {{
|
│ pub type Pet {{
|
||||||
|
|
|
@ -9,7 +9,9 @@ use std::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
|
environment::{
|
||||||
|
assert_no_labeled_arguments_in_pattern, collapse_links, EntityKind, Environment,
|
||||||
|
},
|
||||||
error::Error,
|
error::Error,
|
||||||
hydrator::Hydrator,
|
hydrator::Hydrator,
|
||||||
PatternConstructor, Type, ValueConstructor, ValueConstructorVariant,
|
PatternConstructor, Type, ValueConstructor, ValueConstructorVariant,
|
||||||
|
@ -483,7 +485,12 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fun has no field map and so we error if arguments have been labelled
|
// The fun has no field map and so we error if arguments have been labelled
|
||||||
None => assert_no_labeled_arguments(&pattern_args)?,
|
None => assert_no_labeled_arguments_in_pattern(
|
||||||
|
&name,
|
||||||
|
&pattern_args,
|
||||||
|
&module,
|
||||||
|
with_spread,
|
||||||
|
)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
let constructor_typ = cons.tipo.clone();
|
let constructor_typ = cons.tipo.clone();
|
||||||
|
|
Loading…
Reference in New Issue