Unapplied record builder error

This commit is contained in:
Agustin Zubiaga 2023-05-08 20:16:38 -03:00
parent 6670fbb1ab
commit d2a57112fd
10 changed files with 82 additions and 11 deletions

View file

@ -1367,6 +1367,14 @@ pub fn canonicalize_expr<'a>(
(RuntimeError(problem), Output::default()) (RuntimeError(problem), Output::default())
} }
ast::Expr::UnappliedRecordBuilder(sub_expr) => {
use roc_problem::can::RuntimeError::*;
let problem = UnappliedRecordBuilder(sub_expr.region);
env.problem(Problem::RuntimeError(problem.clone()));
(RuntimeError(problem), Output::default())
}
&ast::Expr::NonBase10Int { &ast::Expr::NonBase10Int {
string, string,
base, base,

View file

@ -138,6 +138,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| MalformedClosure | MalformedClosure
| PrecedenceConflict { .. } | PrecedenceConflict { .. }
| MultipleRecordBuilders { .. } | MultipleRecordBuilders { .. }
| UnappliedRecordBuilder { .. }
| Tag(_) | Tag(_)
| OpaqueRef(_) | OpaqueRef(_)
| IngestedFile(_, _) | IngestedFile(_, _)
@ -253,9 +254,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
} }
} }
} }
RecordBuilder(_) => { RecordBuilder(_) => arena.alloc(Loc {
todo!("Compiler error: Record builders must be applied to functions") value: UnappliedRecordBuilder(loc_expr),
} region: loc_expr.region,
}),
BinOps(lefts, right) => desugar_bin_ops(arena, loc_expr.region, lefts, right), BinOps(lefts, right) => desugar_bin_ops(arena, loc_expr.region, lefts, right),
Defs(defs, loc_ret) => { Defs(defs, loc_ret) => {
let mut defs = (*defs).clone(); let mut defs = (*defs).clone();

View file

@ -799,6 +799,30 @@ mod test_can {
)); ));
} }
#[test]
fn hanging_record_builder() {
let src = indoc!(
r#"
{ a <- apply "a" }
"#
);
let arena = Bump::new();
let CanExprOut {
problems, loc_expr, ..
} = can_expr_with(&arena, test_home(), src);
assert_eq!(problems.len(), 1);
assert!(problems.iter().all(|problem| matches!(
problem,
Problem::RuntimeError(roc_problem::can::RuntimeError::UnappliedRecordBuilder { .. })
)));
assert!(matches!(
loc_expr.value,
Expr::RuntimeError(roc_problem::can::RuntimeError::UnappliedRecordBuilder { .. })
));
}
// TAIL CALLS // TAIL CALLS
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive { fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
match expr { match expr {

View file

@ -79,7 +79,8 @@ impl<'a> Formattable for Expr<'a> {
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict { | PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
expr: loc_subexpr, .. expr: loc_subexpr, ..
}) })
| MultipleRecordBuilders(loc_subexpr) => loc_subexpr.is_multiline(), | MultipleRecordBuilders(loc_subexpr)
| UnappliedRecordBuilder(loc_subexpr) => loc_subexpr.is_multiline(),
ParensAround(subexpr) => subexpr.is_multiline(), ParensAround(subexpr) => subexpr.is_multiline(),
@ -499,9 +500,8 @@ impl<'a> Formattable for Expr<'a> {
} }
MalformedClosure => {} MalformedClosure => {}
PrecedenceConflict { .. } => {} PrecedenceConflict { .. } => {}
MultipleRecordBuilders(sub_expr) => { MultipleRecordBuilders { .. } => {}
sub_expr.format_with_options(buf, parens, newlines, indent) UnappliedRecordBuilder { .. } => {}
}
IngestedFile(_, _) => {} IngestedFile(_, _) => {}
} }
} }

View file

@ -747,9 +747,8 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)),
Expr::MalformedClosure => Expr::MalformedClosure, Expr::MalformedClosure => Expr::MalformedClosure,
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
Expr::MultipleRecordBuilders(a) => { Expr::MultipleRecordBuilders(a) => Expr::MultipleRecordBuilders(a),
Expr::MultipleRecordBuilders(arena.alloc(a.remove_spaces(arena))) Expr::UnappliedRecordBuilder(a) => Expr::UnappliedRecordBuilder(a),
}
Expr::SpaceBefore(a, _) => a.remove_spaces(arena), Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
Expr::SpaceAfter(a, _) => a.remove_spaces(arena), Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
Expr::SingleQuote(a) => Expr::Num(a), Expr::SingleQuote(a) => Expr::Num(a),

View file

@ -328,6 +328,7 @@ pub enum Expr<'a> {
// We should tell the author to disambiguate by grouping them with parens. // We should tell the author to disambiguate by grouping them with parens.
PrecedenceConflict(&'a PrecedenceConflict<'a>), PrecedenceConflict(&'a PrecedenceConflict<'a>),
MultipleRecordBuilders(&'a Loc<Expr<'a>>), MultipleRecordBuilders(&'a Loc<Expr<'a>>),
UnappliedRecordBuilder(&'a Loc<Expr<'a>>),
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -1535,7 +1536,8 @@ impl<'a> Malformed for Expr<'a> {
MalformedIdent(_, _) | MalformedIdent(_, _) |
MalformedClosure | MalformedClosure |
PrecedenceConflict(_) | PrecedenceConflict(_) |
MultipleRecordBuilders(_) => true, MultipleRecordBuilders(_) |
UnappliedRecordBuilder(_) => true,
} }
} }
} }

View file

@ -1929,6 +1929,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::MalformedClosure | Expr::MalformedClosure
| Expr::PrecedenceConflict { .. } | Expr::PrecedenceConflict { .. }
| Expr::MultipleRecordBuilders { .. } | Expr::MultipleRecordBuilders { .. }
| Expr::UnappliedRecordBuilder { .. }
| Expr::RecordUpdate { .. } | Expr::RecordUpdate { .. }
| Expr::UnaryOp(_, _) | Expr::UnaryOp(_, _)
| Expr::Crash => return Err(()), | Expr::Crash => return Err(()),

View file

@ -362,6 +362,7 @@ impl Problem {
| Problem::RuntimeError(RuntimeError::MultipleCharsInSingleQuote(region)) | Problem::RuntimeError(RuntimeError::MultipleCharsInSingleQuote(region))
| Problem::RuntimeError(RuntimeError::DegenerateBranch(region)) | Problem::RuntimeError(RuntimeError::DegenerateBranch(region))
| Problem::RuntimeError(RuntimeError::MultipleRecordBuilders(region)) | Problem::RuntimeError(RuntimeError::MultipleRecordBuilders(region))
| Problem::RuntimeError(RuntimeError::UnappliedRecordBuilder(region))
| Problem::InvalidAliasRigid { region, .. } | Problem::InvalidAliasRigid { region, .. }
| Problem::InvalidInterpolation(region) | Problem::InvalidInterpolation(region)
| Problem::InvalidHexadecimal(region) | Problem::InvalidHexadecimal(region)
@ -591,6 +592,7 @@ pub enum RuntimeError {
DegenerateBranch(Region), DegenerateBranch(Region),
MultipleRecordBuilders(Region), MultipleRecordBuilders(Region),
UnappliedRecordBuilder(Region),
} }
impl RuntimeError { impl RuntimeError {

View file

@ -2147,6 +2147,18 @@ fn pretty_runtime_error<'b>(
title = "MULTIPLE RECORD BUILDERS"; title = "MULTIPLE RECORD BUILDERS";
} }
RuntimeError::UnappliedRecordBuilder(region) => {
doc = alloc.stack([
alloc.reflow("This record builder was not applied to a function:"),
alloc.region(lines.convert_region(region)),
alloc.reflow("However, we need a function to construct the record."),
alloc.note(
"Functions must be applied directly. The pipe operator (|>) cannot be used.",
),
]);
title = "UNAPPLIED RECORD BUILDER";
}
} }
(doc, title) (doc, title)

View file

@ -10204,6 +10204,27 @@ I recommend using camelCase. It's the standard style in Roc code!
"### "###
); );
test_report!(
unapplied_record_builder,
indoc!(
r#"
{ a <- apply "a" }
"#
),
@r###"
UNAPPLIED RECORD BUILDER /code/proj/Main.roc
This record builder was not applied to a function:
4 { a <- apply "a" }
^^^^^^^^^^^^^^^^^^
However, we need a function to construct the record.
Note: Functions must be applied directly. The pipe operator (|>) cannot be used.
"###
);
test_report!( test_report!(
destructure_assignment_introduces_no_variables_nested, destructure_assignment_introduces_no_variables_nested,
indoc!( indoc!(