fiddling with the when parser

This commit is contained in:
Folkert 2021-03-24 22:55:01 +01:00
parent dd8bdcb806
commit 2a0c5c669b
9 changed files with 538 additions and 130 deletions

View File

@ -28,7 +28,7 @@ mod test_fmt {
assert_eq!(buf, expected) assert_eq!(buf, expected)
} }
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", input, error) Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
}; };
} }
@ -1833,23 +1833,23 @@ mod test_fmt {
indoc!( indoc!(
r#" r#"
when b is when b is
1 | 2 | 1 | 2 |
3 3
-> ->
4 4
5 | 6 | 7 -> 5 | 6 | 7 ->
8 8
9 9
| 10 -> 11 | 10 -> 11
12 | 13 -> 12 | 13 ->
when c is when c is
14 | 15 -> 16 14 | 15 -> 16
17 17
| 18 -> 19 | 18 -> 19
20 -> 21 20 -> 21
"# "#
), ),

View File

@ -125,7 +125,7 @@ where
E: 'a, E: 'a,
{ {
move |_, state: State<'a>| { move |_, state: State<'a>| {
if state.column > min_indent { if state.column >= min_indent {
Ok((NoProgress, (), state)) Ok((NoProgress, (), state))
} else { } else {
Err((NoProgress, indent_problem(state.line, state.column), state)) Err((NoProgress, indent_problem(state.line, state.column), state))

View File

@ -16,16 +16,33 @@ use roc_region::all::{Located, Position, Region};
use crate::parser::Progress::{self, *}; use crate::parser::Progress::{self, *};
fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|_arena, state: State<'a>| {
if state.has_reached_end() {
Ok((NoProgress, (), state))
} else {
Err((
NoProgress,
EExpr::BadExprEnd(state.line, state.column),
state,
))
}
}
}
pub fn test_parse_expr<'a>( pub fn test_parse_expr<'a>(
min_indent: u16, min_indent: u16,
arena: &'a bumpalo::Bump, arena: &'a bumpalo::Bump,
state: State<'a>, state: State<'a>,
) -> Result<Located<Expr<'a>>, EExpr<'a>> { ) -> Result<Located<Expr<'a>>, EExpr<'a>> {
let parser = space0_before_e( let parser = skip_second!(
move |a, s| parse_loc_expr(min_indent, a, s), space0_before_e(
min_indent, move |a, s| parse_loc_expr(min_indent, a, s),
EExpr::Space, min_indent,
EExpr::IndentStart, EExpr::Space,
EExpr::IndentStart,
),
expr_end()
); );
match parser.parse(arena, state) { match parser.parse(arena, state) {
@ -1593,15 +1610,13 @@ mod when {
// 1. Parse the first branch and get its indentation level. (It must be >= min_indent.) // 1. Parse the first branch and get its indentation level. (It must be >= min_indent.)
// 2. Parse the other branches. Their indentation levels must be == the first branch's. // 2. Parse the other branches. Their indentation levels must be == the first branch's.
let (_, (loc_first_patterns, loc_first_guard), state) = let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), state) =
branch_alternatives(min_indent).parse(arena, state)?; branch_alternatives(min_indent, None).parse(arena, state)?;
let loc_first_pattern = loc_first_patterns.first().unwrap(); let original_indent = pattern_indent_level;
let original_indent = loc_first_pattern.region.start_col;
let indented_more = original_indent + 1;
// Parse the first "->" and the expression after it. // Parse the first "->" and the expression after it.
let (_, loc_first_expr, mut state) = let (_, loc_first_expr, mut state) =
branch_result(indented_more).parse(arena, state)?; branch_result(original_indent + 1).parse(arena, state)?;
// Record this as the first branch, then optionally parse additional branches. // Record this as the first branch, then optionally parse additional branches.
branches.push(arena.alloc(WhenBranch { branches.push(arena.alloc(WhenBranch {
@ -1613,19 +1628,21 @@ mod when {
let branch_parser = map!( let branch_parser = map!(
and!( and!(
then( then(
branch_alternatives(min_indent), branch_alternatives(min_indent, Some(pattern_indent_level)),
move |_arena, state, _, (loc_patterns, loc_guard)| { move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
match alternatives_indented_correctly(&loc_patterns, original_indent) { if pattern_indent_level == indent_col {
Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)), Ok((MadeProgress, (loc_patterns, loc_guard), state))
Err(indent) => Err(( } else {
let indent = pattern_indent_level - indent_col;
Err((
MadeProgress, MadeProgress,
When::PatternAlignment(indent, state.line, state.column), When::PatternAlignment(indent, state.line, state.column),
state, state,
)), ))
} }
}, },
), ),
branch_result(indented_more) branch_result(original_indent + 1)
), ),
|((patterns, guard), expr)| { |((patterns, guard), expr)| {
let patterns: Vec<'a, _> = patterns; let patterns: Vec<'a, _> = patterns;
@ -1662,33 +1679,17 @@ mod when {
/// Parsing alternative patterns in when branches. /// Parsing alternative patterns in when branches.
fn branch_alternatives<'a>( fn branch_alternatives<'a>(
min_indent: u16, min_indent: u16,
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>), When<'a>> { pattern_indent_level: Option<u16>,
) -> impl Parser<
'a,
(
(Col, Vec<'a, Located<Pattern<'a>>>),
Option<Located<Expr<'a>>>,
),
When<'a>,
> {
and!( and!(
sep_by1(word1(b'|', When::Bar), |arena, state| { branch_alternatives_help(min_indent, pattern_indent_level),
let (_, spaces, state) =
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
.parse(arena, state)?;
let (_, loc_pattern, state) = space0_after_e(
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
min_indent,
When::Space,
When::IndentPattern,
)
.parse(arena, state)?;
Ok((
MadeProgress,
if spaces.is_empty() {
loc_pattern
} else {
arena
.alloc(loc_pattern.value)
.with_spaces_before(spaces, loc_pattern.region)
},
state,
))
}),
one_of![ one_of![
map!( map!(
skip_first!( skip_first!(
@ -1696,7 +1697,7 @@ mod when {
// TODO we should require space before the expression but not after // TODO we should require space before the expression but not after
space0_around_ee( space0_around_ee(
specialize_ref(When::IfGuard, move |arena, state| { specialize_ref(When::IfGuard, move |arena, state| {
parse_loc_expr(min_indent, arena, state) parse_loc_expr(min_indent + 1, arena, state)
}), }),
min_indent, min_indent,
When::Space, When::Space,
@ -1711,22 +1712,103 @@ mod when {
) )
} }
/// Check if alternatives of a when branch are indented correctly. fn branch_single_alternative<'a>(
fn alternatives_indented_correctly<'a>( min_indent: u16,
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>, ) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
original_indent: u16, move |arena, state| {
) -> Result<(), u16> { let (_, spaces, state) =
let (first, rest) = loc_patterns.split_first().unwrap(); backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
let first_indented_correctly = first.region.start_col == original_indent; .parse(arena, state)?;
if first_indented_correctly {
for when_pattern in rest.iter() { let (_, loc_pattern, state) = space0_after_e(
if when_pattern.region.start_col < original_indent { specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
return Err(original_indent - when_pattern.region.start_col); min_indent,
When::Space,
When::IndentPattern,
)
.parse(arena, state)?;
Ok((
MadeProgress,
if spaces.is_empty() {
loc_pattern
} else {
arena
.alloc(loc_pattern.value)
.with_spaces_before(spaces, loc_pattern.region)
},
state,
))
}
}
fn branch_alternatives_help<'a>(
min_indent: u16,
pattern_indent_level: Option<u16>,
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), When<'a>> {
move |arena, state: State<'a>| {
let initial = state;
// put no restrictions on the indent after the spaces; we'll check it manually
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
Ok((_, spaces, state)) => {
match pattern_indent_level {
Some(wanted) if state.column > wanted => {
// this branch is indented too much
Err((
MadeProgress,
When::IndentPattern(state.line, state.column),
state,
))
}
Some(wanted) if state.column < wanted => {
let indent = wanted - state.column;
Err((
NoProgress,
When::PatternAlignment(indent, state.line, state.column),
initial,
))
}
_ => {
let pattern_indent =
min_indent.max(pattern_indent_level.unwrap_or(min_indent));
// the region is not reliable for the indent col in the case of
// parentheses around patterns
let pattern_indent_col = state.column;
let parser = sep_by1(
word1(b'|', When::Bar),
branch_single_alternative(pattern_indent + 1),
);
match parser.parse(arena, state) {
Err((MadeProgress, fail, state)) => {
Err((MadeProgress, fail, state))
}
Err((NoProgress, fail, _)) => {
// roll back space parsing if the pattern made no progress
Err((NoProgress, fail, initial))
}
Ok((_, mut loc_patterns, state)) => {
// tag spaces onto the first parsed pattern
if !spaces.is_empty() {
if let Some(first) = loc_patterns.get_mut(0) {
*first = arena
.alloc(first.value)
.with_spaces_before(spaces, first.region);
}
}
Ok((MadeProgress, (pattern_indent_col, loc_patterns), state))
}
}
}
}
} }
} }
Ok(())
} else {
Err(original_indent - first.region.start_col)
} }
} }

View File

@ -379,6 +379,7 @@ pub type Col = u16;
pub enum EExpr<'a> { pub enum EExpr<'a> {
Start(Row, Col), Start(Row, Col),
End(Row, Col), End(Row, Col),
BadExprEnd(Row, Col),
Space(BadInputError, Row, Col), Space(BadInputError, Row, Col),
Dot(Row, Col), Dot(Row, Col),
@ -926,8 +927,8 @@ where
state = next_state; state = next_state;
buf.push(next_output); buf.push(next_output);
} }
Err((element_progress, fail, state)) => { Err((_, fail, state)) => {
return Err((element_progress, fail, state)); return Err((MadeProgress, fail, state));
} }
} }
} }

View File

@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>(
EPattern::Record, EPattern::Record,
crate::pattern::record_pattern_help(min_indent) crate::pattern::record_pattern_help(min_indent)
)), )),
loc!(number_pattern_help()),
loc!(string_pattern_help()), loc!(string_pattern_help()),
loc!(number_pattern_help())
) )
} }

View File

@ -2550,6 +2550,201 @@ mod test_parse {
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
#[test]
fn when_with_negative_numbers() {
let arena = Bump::new();
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
let expr1 = Num("2");
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
let branch1 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("-3")), newlines);
let loc_pattern2 = Located::new(2, 2, 1, 3, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(2, 2, 7, 8, expr2);
let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2]),
value: loc_expr2,
guard: None,
});
let branches = &[branch1, branch2];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
1 -> 2
-3 -> 4
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_with_function_application() {
let arena = Bump::new();
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
let num_2 = Num("2");
let num_neg = Located::new(
1,
1,
9,
16,
Expr::Var {
module_name: "Num",
ident: "neg",
},
);
let expr0 = Located::new(2, 2, 5, 6, Expr::SpaceBefore(&num_2, &[Newline]));
let expr1 = Expr::Apply(
&num_neg,
&*arena.alloc([&*arena.alloc(expr0)]),
CalledVia::Space,
);
let loc_expr1 = Located::new(1, 2, 9, 6, expr1);
let branch1 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
});
let newlines = &[Newline];
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let loc_pattern2 = Located::new(3, 3, 4, 5, pattern2);
let expr2 = Num("4");
let loc_expr2 = Located::new(3, 3, 9, 10, expr2);
let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2]),
value: loc_expr2,
guard: None,
});
let branches = &[branch1, branch2];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
1 -> Num.neg
2
_ -> 4
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test]
fn when_if_guard() {
let arena = Bump::new();
let branch1 = {
let newlines = &[Newline];
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
let num_1 = Num("1");
let expr1 = Located::new(
2,
2,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branch2 = {
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), &[Newline, Newline]);
let loc_pattern1 = Located::new(4, 4, 4, 5, pattern1);
let num_1 = Num("2");
let expr1 = Located::new(
5,
5,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branch3 = {
let pattern1 =
Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), &[Newline, Newline]);
let loc_pattern1 = Located::new(7, 7, 4, 6, pattern1);
let num_1 = Num("3");
let expr1 = Located::new(
8,
8,
8,
9,
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
);
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
&*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern1]),
value: loc_expr1,
guard: None,
})
};
let branches = &[branch1, branch2, branch3];
let var = Var {
module_name: "",
ident: "x",
};
let loc_cond = Located::new(0, 0, 5, 6, var);
let expected = Expr::When(arena.alloc(loc_cond), branches);
let actual = parse_expr_with(
&arena,
indoc!(
r#"
when x is
_ ->
1
_ ->
2
Ok ->
3
"#
),
);
assert_eq!(Ok(expected), actual);
}
#[test] #[test]
fn when_with_records() { fn when_with_records() {
let arena = Bump::new(); let arena = Bump::new();
@ -2620,9 +2815,9 @@ mod test_parse {
let pattern2_alt = let pattern2_alt =
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines); Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2); let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt); let loc_pattern2_alt = Located::new(3, 3, 2, 7, pattern2_alt);
let expr2 = Num("2"); let expr2 = Num("2");
let loc_expr2 = Located::new(3, 3, 10, 11, expr2); let loc_expr2 = Located::new(3, 3, 11, 12, expr2);
let branch2 = &*arena.alloc(WhenBranch { let branch2 = &*arena.alloc(WhenBranch {
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]), patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
value: loc_expr2, value: loc_expr2,
@ -2642,7 +2837,7 @@ mod test_parse {
when x is when x is
"blah" | "blop" -> 1 "blah" | "blop" -> 1
"foo" | "foo" |
"bar" -> 2 "bar" -> 2
"# "#
), ),
); );

View File

@ -458,6 +458,27 @@ fn to_expr_report<'a>(
*col, *col,
), ),
EExpr::BadExprEnd(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
let doc = alloc.stack(vec![
alloc.reflow(r"I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Whatever I am running into is confusing me al lot! "),
alloc.reflow("Normally I can give fairly specific hints, "),
alloc.reflow("but something is really tripping me up this time."),
]),
]);
Report {
filename,
doc,
title: "SYNTAX PROBLEM".to_string(),
}
}
EExpr::Colon(row, col) => { EExpr::Colon(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col); let region = Region::from_row_col(*row, *col);
@ -982,7 +1003,7 @@ fn to_list_report<'a>(
), ),
List::Open(row, col) | List::End(row, col) => { List::Open(row, col) | List::End(row, col) => {
match dbg!(what_is_next(alloc.src_lines, row, col)) { match what_is_next(alloc.src_lines, row, col) {
Next::Other(Some(',')) => { Next::Other(Some(',')) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col); let region = Region::from_row_col(row, col);
@ -1388,24 +1409,56 @@ fn to_unfinished_when_report<'a>(
start_col: Col, start_col: Col,
message: RocDocBuilder<'a>, message: RocDocBuilder<'a>,
) -> Report<'a> { ) -> Report<'a> {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col); match what_is_next(alloc.src_lines, row, col) {
let region = Region::from_row_col(row, col); Next::Token("->") => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_rows_cols(row, col, row, col + 2);
let doc = alloc.stack(vec![ let doc = alloc.stack(vec![
alloc.concat(vec![ alloc.concat(vec![
alloc.reflow(r"I was partway through parsing a "), alloc.reflow(r"I am parsing a "),
alloc.keyword("when"), alloc.keyword("when"),
alloc.reflow(r" expression, but I got stuck here:"), alloc.reflow(r" expression right now, but this arrow is confusing me:"),
]), ]),
alloc.region_with_subregion(surroundings, region), alloc.region_with_subregion(surroundings, region),
message, alloc.concat(vec![
note_for_when_error(alloc), alloc.reflow(r"It makes sense to see arrows around here, "),
]); alloc.reflow(r"so I suspect it is something earlier."),
alloc.reflow(
r"Maybe this pattern is indented a bit farther from the previous patterns?",
),
]),
note_for_when_error(alloc),
]);
Report { Report {
filename, filename,
doc, doc,
title: "UNFINISHED WHEN".to_string(), title: "UNEXPECTED ARROW".to_string(),
}
}
_ => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow(r"I was partway through parsing a "),
alloc.keyword("when"),
alloc.reflow(r" expression, but I got stuck here:"),
]),
alloc.region_with_subregion(surroundings, region),
message,
note_for_when_error(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED WHEN".to_string(),
}
}
} }
} }

View File

@ -4964,7 +4964,6 @@ mod test_reporting {
#[test] #[test]
fn empty_or_pattern() { fn empty_or_pattern() {
// this should get better with time
report_problem_as( report_problem_as(
indoc!( indoc!(
r#" r#"
@ -4978,29 +4977,16 @@ mod test_reporting {
), ),
indoc!( indoc!(
r#" r#"
MISSING EXPRESSION UNFINISHED PATTERN
I am partway through parsing a definition, but I got stuck here: I just started parsing a pattern, but I got stuck here:
1 when Just 4 is
2 Just 4 | -> 2 Just 4 | ->
^ ^
I was expecting to see an expression like 42 or "hello". Note: I may be confused by indentation
"# "#
), ),
// indoc!(
// r#"
// ── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
//
// I just started parsing a pattern, but I got stuck here:
//
// 2│ Just 4 | ->
// ^
//
// Note: I may be confused by indentation
// "#
// ),
) )
} }
@ -5132,29 +5118,120 @@ mod test_reporting {
r#" r#"
when 4 is when 4 is
5 -> 2 5 -> 2
_ -> 2 2 -> 2
"#
),
indoc!(
r#"
SYNTAX PROBLEM
I got stuck here:
1 when 4 is
2 5 -> 2
^
Whatever I am running into is confusing me al lot! Normally I can give
fairly specific hints, but something is really tripping me up this
time.
"#
),
// TODO this formerly gave
//
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
//
// I was partway through parsing a `when` expression, but I got stuck here:
//
// 3│ _ -> 2
// ^
//
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
//
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
// we find an efficient solution that doesn't require parsing an extra pattern for
// every `when`, i.e. we want a good error message for the test case above, but for
// a valid `when`, we don't want to do extra work, e.g. here
//
// x
// when x is
// n -> n
//
// 4
//
// We don't want to parse the `4` and say it's an outdented pattern!
)
}
#[test]
fn when_over_indented_underscore() {
report_problem_as(
indoc!(
r#"
when 4 is
5 -> 2
_ -> 2
"# "#
), ),
indoc!( indoc!(
r#" r#"
UNFINISHED WHEN UNFINISHED WHEN
I was partway through parsing a `when` expression, but I got stuck here: I was partway through parsing a `when` expression, but I got stuck here:
3 _ -> 2 3 _ -> 2
^ ^
I suspect this is a pattern that is not indented enough? (by 2 spaces) I was expecting to see a pattern next
Note: Here is an example of a valid `when` expression for reference. Note: Here is an example of a valid `when` expression for reference.
when List.first plants is when List.first plants is
Ok n -> Ok n ->
n n
Err _ -> Err _ ->
200 200
Notice the indentation. All patterns are aligned, and each branch is
indented a bit more than the corresponding pattern. That is important!
"#
),
)
}
#[test]
fn when_over_indented_int() {
report_problem_as(
indoc!(
r#"
when 4 is
5 -> Num.neg
2 -> 2
"#
),
indoc!(
r#"
UNEXPECTED ARROW
I am parsing a `when` expression right now, but this arrow is confusing
me:
3 2 -> 2
^^
It makes sense to see arrows around here, so I suspect it is something
earlier.Maybe this pattern is indented a bit farther from the previous
patterns?
Note: Here is an example of a valid `when` expression for reference.
when List.first plants is
Ok n ->
n
Err _ ->
200
Notice the indentation. All patterns are aligned, and each branch is Notice the indentation. All patterns are aligned, and each branch is
indented a bit more than the corresponding pattern. That is important! indented a bit more than the corresponding pattern. That is important!
"# "#

View File

@ -1,5 +1,5 @@
use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry}; use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
#[cfg(test)] #[cfg(test)]
mod test_docs { mod test_docs {