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::{ 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> {

View File

@ -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)]

View File

@ -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(

View File

@ -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();