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,
|
||||
name: &'a str,
|
||||
args: &'a [CallArg<UntypedPattern>],
|
||||
|
@ -1478,7 +1478,7 @@ impl<'comments> Formatter<'comments> {
|
|||
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 doc = match pattern {
|
||||
Pattern::Int { value, .. } => value.to_doc(),
|
||||
|
|
|
@ -10,9 +10,10 @@ use crate::{
|
|||
ast::{
|
||||
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, Pattern,
|
||||
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
|
||||
UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE,
|
||||
UnqualifiedImport, UntypedDefinition, UntypedPattern, Use, PIPE_VARIABLE,
|
||||
},
|
||||
builtins::{self, function, generic_var, tuple, unbound_var},
|
||||
format::Formatter,
|
||||
tipo::fields::FieldMap,
|
||||
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> {
|
||||
for arg in args {
|
||||
if let Some(label) = &arg.label {
|
||||
return Err(Error::UnexpectedLabeledArg {
|
||||
location: arg.location,
|
||||
label: label.to_string(),
|
||||
hint: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,11 +144,14 @@ pub enum Error {
|
|||
#[error("{name} is a reserved module name\n")]
|
||||
ReservedModuleName { name: String },
|
||||
|
||||
#[error("Unexpected labeled argument\n\n{label}\n")]
|
||||
#[error("Unexpected labeled argument '{label}'\n")]
|
||||
#[diagnostic()]
|
||||
UnexpectedLabeledArg {
|
||||
#[label]
|
||||
location: Span,
|
||||
label: String,
|
||||
#[help]
|
||||
hint: Option<String>,
|
||||
},
|
||||
|
||||
#[error("Unexpected type hole\n")]
|
||||
|
@ -229,8 +232,8 @@ pub enum Error {
|
|||
#[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:
|
||||
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 {{
|
||||
|
|
|
@ -9,7 +9,9 @@ use std::{
|
|||
use itertools::Itertools;
|
||||
|
||||
use super::{
|
||||
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
|
||||
environment::{
|
||||
assert_no_labeled_arguments_in_pattern, collapse_links, EntityKind, Environment,
|
||||
},
|
||||
error::Error,
|
||||
hydrator::Hydrator,
|
||||
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
|
||||
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();
|
||||
|
|
Loading…
Reference in New Issue