diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index 151dd27635..ebcf367152 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -28,7 +28,7 @@ mod test_fmt { 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!( r#" when b is - 1 | 2 | - 3 - -> + 1 | 2 | + 3 + -> - 4 - 5 | 6 | 7 -> + 4 + 5 | 6 | 7 -> - 8 - 9 - | 10 -> 11 + 8 + 9 + | 10 -> 11 - 12 | 13 -> - when c is - 14 | 15 -> 16 - 17 - | 18 -> 19 - 20 -> 21 + 12 | 13 -> + when c is + 14 | 15 -> 16 + 17 + | 18 -> 19 + 20 -> 21 "# ), diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index eb72f51cae..1a785f9671 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -125,7 +125,7 @@ where E: 'a, { move |_, state: State<'a>| { - if state.column > min_indent { + if state.column >= min_indent { Ok((NoProgress, (), state)) } else { Err((NoProgress, indent_problem(state.line, state.column), state)) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 0b163b5057..719775348e 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -16,16 +16,33 @@ use roc_region::all::{Located, Position, Region}; 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>( min_indent: u16, arena: &'a bumpalo::Bump, state: State<'a>, ) -> Result>, EExpr<'a>> { - let parser = space0_before_e( - move |a, s| parse_loc_expr(min_indent, a, s), - min_indent, - EExpr::Space, - EExpr::IndentStart, + let parser = skip_second!( + space0_before_e( + move |a, s| parse_loc_expr(min_indent, a, s), + min_indent, + EExpr::Space, + EExpr::IndentStart, + ), + expr_end() ); 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.) // 2. Parse the other branches. Their indentation levels must be == the first branch's. - let (_, (loc_first_patterns, loc_first_guard), state) = - branch_alternatives(min_indent).parse(arena, state)?; - let loc_first_pattern = loc_first_patterns.first().unwrap(); - let original_indent = loc_first_pattern.region.start_col; - let indented_more = original_indent + 1; + let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), state) = + branch_alternatives(min_indent, None).parse(arena, state)?; + let original_indent = pattern_indent_level; // Parse the first "->" and the expression after it. 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. branches.push(arena.alloc(WhenBranch { @@ -1613,19 +1628,21 @@ mod when { let branch_parser = map!( and!( then( - branch_alternatives(min_indent), - move |_arena, state, _, (loc_patterns, loc_guard)| { - match alternatives_indented_correctly(&loc_patterns, original_indent) { - Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)), - Err(indent) => Err(( + branch_alternatives(min_indent, Some(pattern_indent_level)), + move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| { + if pattern_indent_level == indent_col { + Ok((MadeProgress, (loc_patterns, loc_guard), state)) + } else { + let indent = pattern_indent_level - indent_col; + Err(( MadeProgress, When::PatternAlignment(indent, state.line, state.column), state, - )), + )) } }, ), - branch_result(indented_more) + branch_result(original_indent + 1) ), |((patterns, guard), expr)| { let patterns: Vec<'a, _> = patterns; @@ -1662,33 +1679,17 @@ mod when { /// Parsing alternative patterns in when branches. fn branch_alternatives<'a>( min_indent: u16, - ) -> impl Parser<'a, (Vec<'a, Located>>, Option>>), When<'a>> { + pattern_indent_level: Option, + ) -> impl Parser< + 'a, + ( + (Col, Vec<'a, Located>>), + Option>>, + ), + When<'a>, + > { and!( - sep_by1(word1(b'|', When::Bar), |arena, state| { - 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, - )) - }), + branch_alternatives_help(min_indent, pattern_indent_level), one_of![ map!( skip_first!( @@ -1696,7 +1697,7 @@ mod when { // TODO we should require space before the expression but not after space0_around_ee( specialize_ref(When::IfGuard, move |arena, state| { - parse_loc_expr(min_indent, arena, state) + parse_loc_expr(min_indent + 1, arena, state) }), min_indent, When::Space, @@ -1711,22 +1712,103 @@ mod when { ) } - /// Check if alternatives of a when branch are indented correctly. - fn alternatives_indented_correctly<'a>( - loc_patterns: &'a Vec<'a, Located>>, - original_indent: u16, - ) -> Result<(), u16> { - let (first, rest) = loc_patterns.split_first().unwrap(); - let first_indented_correctly = first.region.start_col == original_indent; - if first_indented_correctly { - for when_pattern in rest.iter() { - if when_pattern.region.start_col < original_indent { - return Err(original_indent - when_pattern.region.start_col); + fn branch_single_alternative<'a>( + min_indent: u16, + ) -> impl Parser<'a, Located>, When<'a>> { + move |arena, state| { + 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, + )) + } + } + + fn branch_alternatives_help<'a>( + min_indent: u16, + pattern_indent_level: Option, + ) -> impl Parser<'a, (Col, Vec<'a, Located>>), 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) } } diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 624555e07c..3c5200ca64 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -379,6 +379,7 @@ pub type Col = u16; pub enum EExpr<'a> { Start(Row, Col), End(Row, Col), + BadExprEnd(Row, Col), Space(BadInputError, Row, Col), Dot(Row, Col), @@ -926,8 +927,8 @@ where state = next_state; buf.push(next_output); } - Err((element_progress, fail, state)) => { - return Err((element_progress, fail, state)); + Err((_, fail, state)) => { + return Err((MadeProgress, fail, state)); } } } diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index 38a05bd3aa..1ae58f38a1 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>( EPattern::Record, crate::pattern::record_pattern_help(min_indent) )), + loc!(number_pattern_help()), loc!(string_pattern_help()), - loc!(number_pattern_help()) ) } diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 1aa5d0cba9..182b901499 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -2550,6 +2550,201 @@ mod test_parse { 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] fn when_with_records() { let arena = Bump::new(); @@ -2620,9 +2815,9 @@ mod test_parse { let pattern2_alt = Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines); 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 loc_expr2 = Located::new(3, 3, 10, 11, expr2); + let loc_expr2 = Located::new(3, 3, 11, 12, expr2); let branch2 = &*arena.alloc(WhenBranch { patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]), value: loc_expr2, @@ -2642,7 +2837,7 @@ mod test_parse { when x is "blah" | "blop" -> 1 "foo" | - "bar" -> 2 + "bar" -> 2 "# ), ); diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index e131745996..19e7d77b3a 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -458,6 +458,27 @@ fn to_expr_report<'a>( *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) => { let surroundings = Region::from_rows_cols(start_row, start_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) => { - match dbg!(what_is_next(alloc.src_lines, row, col)) { + match what_is_next(alloc.src_lines, row, col) { Next::Other(Some(',')) => { let surroundings = Region::from_rows_cols(start_row, start_col, row, col); let region = Region::from_row_col(row, col); @@ -1388,24 +1409,56 @@ fn to_unfinished_when_report<'a>( start_col: Col, message: RocDocBuilder<'a>, ) -> Report<'a> { - let surroundings = Region::from_rows_cols(start_row, start_col, row, col); - let region = Region::from_row_col(row, col); + match what_is_next(alloc.src_lines, 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![ - 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), - ]); + let doc = alloc.stack(vec![ + alloc.concat(vec![ + alloc.reflow(r"I am parsing a "), + alloc.keyword("when"), + alloc.reflow(r" expression right now, but this arrow is confusing me:"), + ]), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + 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 { - filename, - doc, - title: "UNFINISHED WHEN".to_string(), + Report { + filename, + doc, + 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(), + } + } } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index ee2af9e6bf..e2d0c9bd78 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4964,7 +4964,6 @@ mod test_reporting { #[test] fn empty_or_pattern() { - // this should get better with time report_problem_as( indoc!( r#" @@ -4978,29 +4977,16 @@ mod test_reporting { ), indoc!( r#" - ── MISSING EXPRESSION ────────────────────────────────────────────────────────── - - I am partway through parsing a definition, but I got stuck here: - - 1│ when Just 4 is + ── UNFINISHED PATTERN ────────────────────────────────────────────────────────── + + I just started parsing a pattern, but I got stuck here: + 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#" when 4 is 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!( r#" ── 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) - + + 3│ _ -> 2 + ^ + + I was expecting to see a pattern next + 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 + 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 indented a bit more than the corresponding pattern. That is important! "# diff --git a/docs/tests/test_docs.rs b/docs/tests/test_docs.rs index da99e07b08..1099d88b69 100644 --- a/docs/tests/test_docs.rs +++ b/docs/tests/test_docs.rs @@ -1,5 +1,5 @@ use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[cfg(test)] mod test_docs {