Use smart-constructor for UnexpectedLabeledArg errors.
Reduce duplications and keep the formatting of the error inside the error module.
This commit is contained in:
parent
dca633da48
commit
ce0c6e0d0f
|
@ -10,10 +10,9 @@ 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, UntypedPattern, Use, PIPE_VARIABLE,
|
UnqualifiedImport, UntypedDefinition, 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,
|
||||||
};
|
};
|
||||||
|
@ -1584,57 +1583,13 @@ fn assert_unique_const_name<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn assert_no_labeled_arguments_in_pattern<'a>(
|
pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Option<(Span, String)> {
|
||||||
name: &str,
|
|
||||||
args: &[CallArg<UntypedPattern>],
|
|
||||||
module: &'a Option<String>,
|
|
||||||
with_spread: bool,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if let Some(label) = &arg.label {
|
if let Some(label) = &arg.label {
|
||||||
let fixed_args = args
|
return Some((arg.location, label.to_string()));
|
||||||
.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(())
|
None
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn collapse_links(t: Arc<Type>) -> Arc<Type> {
|
pub(super) fn collapse_links(t: Arc<Type>) -> Arc<Type> {
|
||||||
|
|
|
@ -5,7 +5,8 @@ use ordinal::Ordinal;
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{BinOp, Span, TodoKind},
|
ast::{BinOp, CallArg, Span, TodoKind, UntypedPattern},
|
||||||
|
format::Formatter,
|
||||||
levenshtein,
|
levenshtein,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -489,6 +490,51 @@ with the module's name.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unexpected_labeled_arg<'a>(location: Span, label: String) -> Self {
|
||||||
|
Self::UnexpectedLabeledArg {
|
||||||
|
location,
|
||||||
|
label,
|
||||||
|
hint: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unexpected_labeled_arg_in_pattern(
|
||||||
|
location: Span,
|
||||||
|
label: String,
|
||||||
|
name: &str,
|
||||||
|
args: &[CallArg<UntypedPattern>],
|
||||||
|
module: &Option<String>,
|
||||||
|
with_spread: bool,
|
||||||
|
) -> Self {
|
||||||
|
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}"#
|
||||||
|
);
|
||||||
|
|
||||||
|
Self::UnexpectedLabeledArg {
|
||||||
|
location,
|
||||||
|
label: label.to_string(),
|
||||||
|
hint: Some(hint),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
|
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
|
||||||
|
|
|
@ -111,7 +111,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
Some(field_map) => field_map.reorder(&mut args, location)?,
|
Some(field_map) => field_map.reorder(&mut args, location)?,
|
||||||
|
|
||||||
// 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(&args)?,
|
None => assert_no_labeled_arguments(&args)
|
||||||
|
.map(|(location, label)| Err(Error::unexpected_labeled_arg(location, label)))
|
||||||
|
.unwrap_or(Ok(()))?,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the type of the fun, ensuring it actually is a function
|
// Extract the type of the fun, ensuring it actually is a function
|
||||||
|
@ -1350,7 +1352,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
Some(field_map) => field_map.reorder(&mut args, location)?,
|
Some(field_map) => field_map.reorder(&mut args, location)?,
|
||||||
|
|
||||||
// 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(&args)?,
|
None => assert_no_labeled_arguments(&args)
|
||||||
|
.map(|(location, label)| {
|
||||||
|
Err(Error::unexpected_labeled_arg(location, label))
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(()))?,
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut args_types, return_type) = self.environment.match_fun_type(
|
let (mut args_types, return_type) = self.environment.match_fun_type(
|
||||||
|
|
|
@ -9,9 +9,7 @@ use std::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
environment::{
|
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, 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,
|
||||||
|
@ -487,12 +485,18 @@ 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_in_pattern(
|
None => assert_no_labeled_arguments(&pattern_args)
|
||||||
|
.map(|(location, label)| {
|
||||||
|
Err(Error::unexpected_labeled_arg_in_pattern(
|
||||||
|
location,
|
||||||
|
label,
|
||||||
&name,
|
&name,
|
||||||
&pattern_args,
|
&pattern_args,
|
||||||
&module,
|
&module,
|
||||||
with_spread,
|
with_spread,
|
||||||
)?,
|
))
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(()))?,
|
||||||
}
|
}
|
||||||
|
|
||||||
let constructor_typ = cons.tipo.clone();
|
let constructor_typ = cons.tipo.clone();
|
||||||
|
|
Loading…
Reference in New Issue