mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 06:55:15 +00:00
Multiple record builder error
This commit is contained in:
parent
20f8133b6c
commit
6670fbb1ab
11 changed files with 115 additions and 4 deletions
|
@ -1359,6 +1359,14 @@ pub fn canonicalize_expr<'a>(
|
||||||
|
|
||||||
(RuntimeError(problem), Output::default())
|
(RuntimeError(problem), Output::default())
|
||||||
}
|
}
|
||||||
|
ast::Expr::MultipleRecordBuilders(sub_expr) => {
|
||||||
|
use roc_problem::can::RuntimeError::*;
|
||||||
|
|
||||||
|
let problem = MultipleRecordBuilders(sub_expr.region);
|
||||||
|
env.problem(Problem::RuntimeError(problem.clone()));
|
||||||
|
|
||||||
|
(RuntimeError(problem), Output::default())
|
||||||
|
}
|
||||||
&ast::Expr::NonBase10Int {
|
&ast::Expr::NonBase10Int {
|
||||||
string,
|
string,
|
||||||
base,
|
base,
|
||||||
|
|
|
@ -137,6 +137,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
| MalformedIdent(_, _)
|
| MalformedIdent(_, _)
|
||||||
| MalformedClosure
|
| MalformedClosure
|
||||||
| PrecedenceConflict { .. }
|
| PrecedenceConflict { .. }
|
||||||
|
| MultipleRecordBuilders { .. }
|
||||||
| Tag(_)
|
| Tag(_)
|
||||||
| OpaqueRef(_)
|
| OpaqueRef(_)
|
||||||
| IngestedFile(_, _)
|
| IngestedFile(_, _)
|
||||||
|
@ -253,7 +254,7 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RecordBuilder(_) => {
|
RecordBuilder(_) => {
|
||||||
todo!("Compiler error: Record builders must be applied to functions");
|
todo!("Compiler error: Record builders must be applied to functions")
|
||||||
}
|
}
|
||||||
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) => {
|
||||||
|
@ -274,7 +275,10 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
||||||
match current {
|
match current {
|
||||||
RecordBuilder(fields) => {
|
RecordBuilder(fields) => {
|
||||||
if builder_apply_exprs.is_some() {
|
if builder_apply_exprs.is_some() {
|
||||||
todo!("Compiler error: A function application can only be passed one record builder")
|
return arena.alloc(Loc {
|
||||||
|
value: MultipleRecordBuilders(loc_expr),
|
||||||
|
region: loc_expr.region,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let builder_arg = record_builder_arg(arena, loc_arg.region, fields);
|
let builder_arg = record_builder_arg(arena, loc_arg.region, fields);
|
||||||
|
|
|
@ -773,6 +773,32 @@ mod test_can {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_record_builders_error() {
|
||||||
|
let src = indoc!(
|
||||||
|
r#"
|
||||||
|
succeed
|
||||||
|
{ a <- apply "a" }
|
||||||
|
{ b <- apply "b" }
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
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::MultipleRecordBuilders { .. })
|
||||||
|
)));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
loc_expr.value,
|
||||||
|
Expr::RuntimeError(roc_problem::can::RuntimeError::MultipleRecordBuilders { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
|
@ -78,7 +78,8 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
UnaryOp(loc_subexpr, _)
|
UnaryOp(loc_subexpr, _)
|
||||||
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
|
| PrecedenceConflict(roc_parse::ast::PrecedenceConflict {
|
||||||
expr: loc_subexpr, ..
|
expr: loc_subexpr, ..
|
||||||
}) => loc_subexpr.is_multiline(),
|
})
|
||||||
|
| MultipleRecordBuilders(loc_subexpr) => loc_subexpr.is_multiline(),
|
||||||
|
|
||||||
ParensAround(subexpr) => subexpr.is_multiline(),
|
ParensAround(subexpr) => subexpr.is_multiline(),
|
||||||
|
|
||||||
|
@ -498,6 +499,9 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
}
|
}
|
||||||
MalformedClosure => {}
|
MalformedClosure => {}
|
||||||
PrecedenceConflict { .. } => {}
|
PrecedenceConflict { .. } => {}
|
||||||
|
MultipleRecordBuilders(sub_expr) => {
|
||||||
|
sub_expr.format_with_options(buf, parens, newlines, indent)
|
||||||
|
}
|
||||||
IngestedFile(_, _) => {}
|
IngestedFile(_, _) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -747,6 +747,9 @@ 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(arena.alloc(a.remove_spaces(arena)))
|
||||||
|
}
|
||||||
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),
|
||||||
|
|
|
@ -327,6 +327,7 @@ pub enum Expr<'a> {
|
||||||
// Both operators were non-associative, e.g. (True == False == False).
|
// Both operators were non-associative, e.g. (True == False == False).
|
||||||
// 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>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -1533,7 +1534,8 @@ impl<'a> Malformed for Expr<'a> {
|
||||||
|
|
||||||
MalformedIdent(_, _) |
|
MalformedIdent(_, _) |
|
||||||
MalformedClosure |
|
MalformedClosure |
|
||||||
PrecedenceConflict(_) => true,
|
PrecedenceConflict(_) |
|
||||||
|
MultipleRecordBuilders(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1928,6 +1928,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
| Expr::Dbg(_, _)
|
| Expr::Dbg(_, _)
|
||||||
| Expr::MalformedClosure
|
| Expr::MalformedClosure
|
||||||
| Expr::PrecedenceConflict { .. }
|
| Expr::PrecedenceConflict { .. }
|
||||||
|
| Expr::MultipleRecordBuilders { .. }
|
||||||
| Expr::RecordUpdate { .. }
|
| Expr::RecordUpdate { .. }
|
||||||
| Expr::UnaryOp(_, _)
|
| Expr::UnaryOp(_, _)
|
||||||
| Expr::Crash => return Err(()),
|
| Expr::Crash => return Err(()),
|
||||||
|
|
|
@ -361,6 +361,7 @@ impl Problem {
|
||||||
| Problem::RuntimeError(RuntimeError::EmptySingleQuote(region))
|
| Problem::RuntimeError(RuntimeError::EmptySingleQuote(region))
|
||||||
| 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::InvalidAliasRigid { region, .. }
|
| Problem::InvalidAliasRigid { region, .. }
|
||||||
| Problem::InvalidInterpolation(region)
|
| Problem::InvalidInterpolation(region)
|
||||||
| Problem::InvalidHexadecimal(region)
|
| Problem::InvalidHexadecimal(region)
|
||||||
|
@ -588,6 +589,8 @@ pub enum RuntimeError {
|
||||||
MultipleCharsInSingleQuote(Region),
|
MultipleCharsInSingleQuote(Region),
|
||||||
|
|
||||||
DegenerateBranch(Region),
|
DegenerateBranch(Region),
|
||||||
|
|
||||||
|
MultipleRecordBuilders(Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
|
|
|
@ -1999,6 +1999,25 @@ mod test_fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_format_multiple_record_builders() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
succeed { a <- get "a" }
|
||||||
|
{ b <- get "b" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
succeed
|
||||||
|
{ a <- get "a" }
|
||||||
|
{ b <- get "b" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn final_comments_in_records() {
|
fn final_comments_in_records() {
|
||||||
expr_formats_same(indoc!(
|
expr_formats_same(indoc!(
|
||||||
|
|
|
@ -2133,6 +2133,20 @@ fn pretty_runtime_error<'b>(
|
||||||
|
|
||||||
title = "DEGENERATE BRANCH";
|
title = "DEGENERATE BRANCH";
|
||||||
}
|
}
|
||||||
|
RuntimeError::MultipleRecordBuilders(region) => {
|
||||||
|
let tip = alloc
|
||||||
|
.tip()
|
||||||
|
.append(alloc.reflow("You can combine them or apply them separately."));
|
||||||
|
|
||||||
|
doc = alloc.stack([
|
||||||
|
alloc.reflow("This function is applied to multiple record builders:"),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.note("Functions can only take at most one record builder!"),
|
||||||
|
tip,
|
||||||
|
]);
|
||||||
|
|
||||||
|
title = "MULTIPLE RECORD BUILDERS";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(doc, title)
|
(doc, title)
|
||||||
|
|
|
@ -10177,6 +10177,33 @@ I recommend using camelCase. It's the standard style in Roc code!
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Record Builders
|
||||||
|
|
||||||
|
test_report!(
|
||||||
|
multiple_record_builders,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
succeed
|
||||||
|
{ a <- apply "a" }
|
||||||
|
{ b <- apply "b" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
── MULTIPLE RECORD BUILDERS ────────────────────────────── /code/proj/Main.roc ─
|
||||||
|
|
||||||
|
This function is applied to multiple record builders:
|
||||||
|
|
||||||
|
4│> succeed
|
||||||
|
5│> { a <- apply "a" }
|
||||||
|
6│> { b <- apply "b" }
|
||||||
|
|
||||||
|
Note: Functions can only take at most one record builder!
|
||||||
|
|
||||||
|
Tip: You can combine them or apply them separately.
|
||||||
|
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
|
||||||
test_report!(
|
test_report!(
|
||||||
destructure_assignment_introduces_no_variables_nested,
|
destructure_assignment_introduces_no_variables_nested,
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue