Use smart-constructor for UnexpectedLabeledArg errors.

Reduce duplications and keep the formatting of the error inside the error module.
This commit is contained in:
KtorZ 2022-12-23 00:24:57 +01:00
parent dca633da48
commit ce0c6e0d0f
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
4 changed files with 72 additions and 61 deletions

View File

@ -10,10 +10,9 @@ use crate::{
ast::{
Annotation, CallArg, DataType, Definition, Function, ModuleConstant, Pattern,
RecordConstructor, RecordConstructorArg, Span, TypeAlias, TypedDefinition,
UnqualifiedImport, UntypedDefinition, UntypedPattern, Use, PIPE_VARIABLE,
UnqualifiedImport, UntypedDefinition, Use, PIPE_VARIABLE,
},
builtins::{self, function, generic_var, tuple, unbound_var},
format::Formatter,
tipo::fields::FieldMap,
IdGenerator,
};
@ -1584,57 +1583,13 @@ 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> {
pub(super) fn assert_no_labeled_arguments<A>(args: &[CallArg<A>]) -> Option<(Span, String)> {
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),
});
return Some((arg.location, label.to_string()));
}
}
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,
});
}
}
Ok(())
None
}
pub(super) fn collapse_links(t: Arc<Type>) -> Arc<Type> {

View File

@ -5,7 +5,8 @@ use ordinal::Ordinal;
use miette::Diagnostic;
use crate::{
ast::{BinOp, Span, TodoKind},
ast::{BinOp, CallArg, Span, TodoKind, UntypedPattern},
format::Formatter,
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)]

View File

@ -111,7 +111,9 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Some(field_map) => field_map.reorder(&mut args, location)?,
// 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
@ -1350,7 +1352,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Some(field_map) => field_map.reorder(&mut args, location)?,
// 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(

View File

@ -9,9 +9,7 @@ use std::{
use itertools::Itertools;
use super::{
environment::{
assert_no_labeled_arguments_in_pattern, collapse_links, EntityKind, Environment,
},
environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment},
error::Error,
hydrator::Hydrator,
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
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,
&pattern_args,
&module,
with_spread,
)?,
))
})
.unwrap_or(Ok(()))?,
}
let constructor_typ = cons.tipo.clone();