From 3c03b7004cc3e2ee6539eceb06951808d6c26c49 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 14 Apr 2022 09:31:26 -0400 Subject: [PATCH 1/6] Test for outdented list parse --- .../pass/outdented_list.expr.result-ast | 43 +++++++++++++++++++ .../snapshots/pass/outdented_list.expr.roc | 4 ++ 2 files changed, 47 insertions(+) create mode 100644 compiler/parse/tests/snapshots/pass/outdented_list.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/outdented_list.expr.roc diff --git a/compiler/parse/tests/snapshots/pass/outdented_list.expr.result-ast b/compiler/parse/tests/snapshots/pass/outdented_list.expr.result-ast new file mode 100644 index 0000000000..d7db49650a --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_list.expr.result-ast @@ -0,0 +1,43 @@ +Defs( + [ + @0-17 Value( + Body( + @0-1 Identifier( + "a", + ), + @4-17 List( + Collection { + items: [ + @8-9 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + @11-12 Num( + "2", + ), + @14-15 Num( + "3", + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + ), + ], + @18-19 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/outdented_list.expr.roc b/compiler/parse/tests/snapshots/pass/outdented_list.expr.roc new file mode 100644 index 0000000000..d5ae4d79bc --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_list.expr.roc @@ -0,0 +1,4 @@ +a = [ + 1, 2, 3 +] +a From 0c21821b044dc3d2fc0ac3b5e77d97890b6d9276 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 14 Apr 2022 09:31:55 -0400 Subject: [PATCH 2/6] Make sure outdented records parse --- compiler/parse/src/expr.rs | 9 ++-- .../pass/outdented_record.expr.result-ast | 51 +++++++++++++++++++ .../snapshots/pass/outdented_record.expr.roc | 4 ++ compiler/parse/tests/test_parse.rs | 2 + 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 compiler/parse/tests/snapshots/pass/outdented_record.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/outdented_record.expr.roc diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 1c6256bdfe..4d0654b31f 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -2,7 +2,9 @@ use crate::ast::{ AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Has, Pattern, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; -use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e}; +use crate::blankspace::{ + space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, +}; use crate::ident::{lowercase_ident, parse_ident, Ident}; use crate::keyword; use crate::parser::{ @@ -2591,14 +2593,15 @@ fn record_help<'a>( and!( trailing_sep_by0( word1(b',', ERecord::End), - space0_around_ee( + space0_before_optional_after( loc!(record_field_help(min_indent)), min_indent, ERecord::IndentEnd, ERecord::IndentEnd ), ), - space0_e(min_indent, ERecord::IndentEnd) + // Allow outdented closing braces + space0_e(0, ERecord::IndentEnd) ), word1(b'}', ERecord::End) ) diff --git a/compiler/parse/tests/snapshots/pass/outdented_record.expr.result-ast b/compiler/parse/tests/snapshots/pass/outdented_record.expr.result-ast new file mode 100644 index 0000000000..119d0dc5be --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_record.expr.result-ast @@ -0,0 +1,51 @@ +Defs( + [ + @0-23 Value( + Body( + @0-1 Identifier( + "x", + ), + @4-23 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @8-23 Record( + Collection { + items: [ + @12-21 SpaceBefore( + RequiredValue( + @12-15 "bar", + [], + @17-21 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ], + Space, + ), + ), + ), + ], + @24-25 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/outdented_record.expr.roc b/compiler/parse/tests/snapshots/pass/outdented_record.expr.roc new file mode 100644 index 0000000000..b344f9df89 --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_record.expr.roc @@ -0,0 +1,4 @@ +x = foo { + bar: blah +} +x diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 522c89c7c6..7b78187826 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -218,6 +218,8 @@ mod test_parse { pass/opaque_reference_pattern.expr, pass/opaque_reference_pattern_with_arguments.expr, pass/ops_with_newlines.expr, + pass/outdented_list.expr, + pass/outdented_record.expr, pass/packed_singleton_list.expr, pass/parenthetical_apply.expr, pass/parenthetical_basic_field.expr, From 362ca1d914bc0cba5750009e2c3149f2f63993ae Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 14 Apr 2022 10:44:07 -0400 Subject: [PATCH 3/6] Fix parse tests --- .../outdented_app_with_record.expr.result-ast | 62 +++++++++++++++++++ .../pass/outdented_app_with_record.expr.roc | 4 ++ compiler/parse/tests/test_parse.rs | 1 + 3 files changed, 67 insertions(+) create mode 100644 compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.roc diff --git a/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.result-ast b/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.result-ast new file mode 100644 index 0000000000..4df43e0d86 --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.result-ast @@ -0,0 +1,62 @@ +Defs( + [ + @0-29 Value( + Body( + @0-1 Identifier( + "x", + ), + @4-29 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @9-28 ParensAround( + Apply( + @9-12 Var { + module_name: "", + ident: "baz", + }, + [ + @13-28 Record( + Collection { + items: [ + @17-26 SpaceBefore( + RequiredValue( + @17-20 "bar", + [], + @22-26 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ], + Space, + ), + ), + ], + Space, + ), + ), + ), + ], + @30-31 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.roc b/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.roc new file mode 100644 index 0000000000..2f87899d3e --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/outdented_app_with_record.expr.roc @@ -0,0 +1,4 @@ +x = foo (baz { + bar: blah +}) +x diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 7b78187826..759276523c 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -220,6 +220,7 @@ mod test_parse { pass/ops_with_newlines.expr, pass/outdented_list.expr, pass/outdented_record.expr, + pass/outdented_app_with_record.expr, pass/packed_singleton_list.expr, pass/parenthetical_apply.expr, pass/parenthetical_basic_field.expr, From dc2d9ceeac1dda2ee9bae9e84b72b41ae51ffc4a Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 14 Apr 2022 10:43:53 -0400 Subject: [PATCH 4/6] Detect outdents too far --- compiler/parse/src/expr.rs | 84 ++++++++++++++++++++------- compiler/parse/src/module.rs | 26 +++++++++ compiler/parse/src/parser.rs | 18 ++++-- compiler/parse/src/pattern.rs | 2 + compiler/parse/src/type_annotation.rs | 4 ++ reporting/src/error/parse.rs | 84 ++++++++++++++++++++++----- reporting/tests/test_reporting.rs | 61 +++++++++++++++++++ 7 files changed, 238 insertions(+), 41 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 4d0654b31f..fbc57d27f8 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -190,6 +190,7 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> { /// pattern later fn parse_loc_term_or_underscore<'a>( min_indent: u32, + outdent_col: u32, options: ExprParseOptions, arena: &'a Bump, state: State<'a>, @@ -201,8 +202,11 @@ fn parse_loc_term_or_underscore<'a>( loc!(specialize(EExpr::Number, positive_number_literal_help())), loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))), loc!(underscore_expression()), - loc!(record_literal_help(min_indent)), - loc!(specialize(EExpr::List, list_literal_help(min_indent))), + loc!(record_literal_help(min_indent, outdent_col)), + loc!(specialize( + EExpr::List, + list_literal_help(min_indent, outdent_col) + )), loc!(map_with_arena!( assign_or_destructure_identifier(), ident_to_expr @@ -213,6 +217,7 @@ fn parse_loc_term_or_underscore<'a>( fn parse_loc_term<'a>( min_indent: u32, + outdent_col: u32, options: ExprParseOptions, arena: &'a Bump, state: State<'a>, @@ -223,8 +228,11 @@ fn parse_loc_term<'a>( loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())), loc!(specialize(EExpr::Number, positive_number_literal_help())), loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))), - loc!(record_literal_help(min_indent)), - loc!(specialize(EExpr::List, list_literal_help(min_indent))), + loc!(record_literal_help(min_indent, outdent_col)), + loc!(specialize( + EExpr::List, + list_literal_help(min_indent, outdent_col) + )), loc!(map_with_arena!( assign_or_destructure_identifier(), ident_to_expr @@ -252,6 +260,7 @@ fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> { fn loc_possibly_negative_or_negated_term<'a>( min_indent: u32, + outdent_col: u32, options: ExprParseOptions, ) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ @@ -259,7 +268,11 @@ fn loc_possibly_negative_or_negated_term<'a>( let initial = state.clone(); let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term( - min_indent, options, a, s + min_indent, + outdent_col, + options, + a, + s )) .parse(arena, state)?; @@ -271,13 +284,15 @@ fn loc_possibly_negative_or_negated_term<'a>( loc!(specialize(EExpr::Number, number_literal_help())), loc!(map_with_arena!( and!(loc!(word1(b'!', EExpr::Start)), |a, s| { - parse_loc_term(min_indent, options, a, s) + parse_loc_term(min_indent, outdent_col, options, a, s) }), |arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| { Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not)) } )), - |arena, state| { parse_loc_term_or_underscore(min_indent, options, arena, state) } + |arena, state| { + parse_loc_term_or_underscore(min_indent, outdent_col, options, arena, state) + } ] } @@ -336,8 +351,8 @@ fn parse_expr_operator_chain<'a>( arena: &'a Bump, state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - let (_, expr, state) = - loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state)?; + let (_, expr, state) = loc_possibly_negative_or_negated_term(min_indent, start_column, options) + .parse(arena, state)?; let initial = state.clone(); let end = state.pos(); @@ -1333,7 +1348,8 @@ fn parse_expr_operator<'a>( BinOp::Minus if expr_state.end != op_start && op_end == new_start => { // negative terms - let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?; + let (_, negated_expr, state) = + parse_loc_term(min_indent, start_column, options, arena, state)?; let new_end = state.pos(); let arg = numeric_negate_expression( @@ -1467,7 +1483,9 @@ fn parse_expr_operator<'a>( _ => unreachable!(), }, ), - _ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) { + _ => match loc_possibly_negative_or_negated_term(min_indent, start_column, options) + .parse(arena, state) + { Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), Ok((_, mut new_expr, state)) => { let new_end = state.pos(); @@ -1526,7 +1544,7 @@ fn parse_expr_end<'a>( ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let parser = skip_first!( crate::blankspace::check_indent(min_indent, EExpr::IndentEnd), - move |a, s| parse_loc_term(min_indent, options, a, s) + move |a, s| parse_loc_term(min_indent, start_column, options, a, s) ); match parser.parse(arena, state.clone()) { @@ -2467,7 +2485,10 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> { } } -fn list_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EList<'a>> { +fn list_literal_help<'a>( + min_indent: u32, + outdent_col: u32, +) -> impl Parser<'a, Expr<'a>, EList<'a>> { move |arena, state| { let (_, elements, state) = collection_trailing_sep_e!( word1(b'[', EList::Open), @@ -2478,8 +2499,10 @@ fn list_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EList<'a> word1(b',', EList::End), word1(b']', EList::End), min_indent, + outdent_col, EList::Open, EList::IndentEnd, + EList::OutdentEnd, Expr::SpaceBefore ) .parse(arena, state)?; @@ -2555,6 +2578,7 @@ fn record_updateable_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> fn record_help<'a>( min_indent: u32, + outdent_col: u32, ) -> impl Parser< 'a, ( @@ -2589,8 +2613,8 @@ fn record_help<'a>( // `{ }` can be successfully parsed as an empty record, and then // changed by the formatter back into `{}`. zero_or_more!(word1(b' ', ERecord::End)), - skip_second!( - and!( + |arena, state| { + let (_, (parsed_elems, comments), state) = and!( trailing_sep_by0( word1(b',', ERecord::End), space0_before_optional_after( @@ -2600,19 +2624,35 @@ fn record_help<'a>( ERecord::IndentEnd ), ), - // Allow outdented closing braces - space0_e(0, ERecord::IndentEnd) - ), - word1(b'}', ERecord::End) - ) + space0_e(0, ERecord::OutdentEnd) + ) + .parse(arena, state)?; + + let closing_brace_col = state.column(); + let closing_brace_pos = state.pos(); + + let (_, _, state) = word1(b'}', ERecord::End).parse(arena, state)?; + + if closing_brace_col < outdent_col { + return Err((MadeProgress, ERecord::OutdentEnd(closing_brace_pos), state)); + } + + Ok((MadeProgress, (parsed_elems, comments), state)) + } )) ) ) } -fn record_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { +fn record_literal_help<'a>( + min_indent: u32, + outdent_col: u32, +) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { then( - loc!(specialize(EExpr::Record, record_help(min_indent))), + loc!(specialize( + EExpr::Record, + record_help(min_indent, outdent_col) + )), move |arena, state, _, loc_record| { let (opt_update, loc_assigned_fields_with_comments) = loc_record.value; diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index b1e43773f5..c2a6366dd3 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -416,6 +416,7 @@ fn provides_without_to<'a>() -> impl Parser< EProvides<'a>, > { let min_indent = 1; + let outdent_col = 0; and!( spaces_around_keyword( min_indent, @@ -431,8 +432,10 @@ fn provides_without_to<'a>() -> impl Parser< word1(b',', EProvides::ListEnd), word1(b']', EProvides::ListEnd), min_indent, + outdent_col, EProvides::Open, EProvides::IndentListEnd, + EProvides::IndentListEnd, Spaced::SpaceBefore ), // Optionally @@ -445,6 +448,7 @@ fn provides_without_to<'a>() -> impl Parser< fn provides_types<'a>( ) -> impl Parser<'a, Collection<'a, Loc>>>, EProvides<'a>> { let min_indent = 1; + let outdent_col = 0; skip_first!( // We only support spaces here, not newlines, because this is not intended @@ -464,8 +468,10 @@ fn provides_types<'a>( word1(b',', EProvides::ListEnd), word1(b'}', EProvides::ListEnd), min_indent, + outdent_col, EProvides::Open, EProvides::IndentListEnd, + EProvides::IndentListEnd, Spaced::SpaceBefore ) ) @@ -545,8 +551,10 @@ fn requires_rigids<'a>( word1(b',', ERequires::ListEnd), word1(b'}', ERequires::ListEnd), min_indent, + 0, ERequires::Open, ERequires::IndentListEnd, + ERequires::IndentListEnd, Spaced::SpaceBefore ) } @@ -577,6 +585,7 @@ fn exposes_values<'a>() -> impl Parser< EExposes, > { let min_indent = 1; + let outdent_col = 0; and!( spaces_around_keyword( @@ -592,8 +601,10 @@ fn exposes_values<'a>() -> impl Parser< word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, + outdent_col, EExposes::Open, EExposes::IndentListEnd, + EExposes::IndentListEnd, Spaced::SpaceBefore ) ) @@ -628,6 +639,7 @@ fn exposes_modules<'a>() -> impl Parser< EExposes, > { let min_indent = 1; + let outdent_col = 0; and!( spaces_around_keyword( @@ -643,8 +655,10 @@ fn exposes_modules<'a>() -> impl Parser< word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, + outdent_col, EExposes::Open, EExposes::IndentListEnd, + EExposes::IndentListEnd, Spaced::SpaceBefore ) ) @@ -674,6 +688,7 @@ struct Packages<'a> { #[inline(always)] fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { let min_indent = 1; + let outdent_col = 0; map!( and!( @@ -690,8 +705,10 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { word1(b',', EPackages::ListEnd), word1(b'}', EPackages::ListEnd), min_indent, + outdent_col, EPackages::Open, EPackages::IndentListEnd, + EPackages::IndentListEnd, Spaced::SpaceBefore ) ), @@ -741,6 +758,7 @@ fn generates_with<'a>() -> impl Parser< EGeneratesWith, > { let min_indent = 1; + let outdent_col = 0; and!( spaces_around_keyword( @@ -756,8 +774,10 @@ fn generates_with<'a>() -> impl Parser< word1(b',', EGeneratesWith::ListEnd), word1(b']', EGeneratesWith::ListEnd), min_indent, + outdent_col, EGeneratesWith::Open, EGeneratesWith::IndentListEnd, + EGeneratesWith::IndentListEnd, Spaced::SpaceBefore ) ) @@ -773,6 +793,7 @@ fn imports<'a>() -> impl Parser< EImports, > { let min_indent = 1; + let outdent_col = 0; and!( spaces_around_keyword( @@ -788,8 +809,10 @@ fn imports<'a>() -> impl Parser< word1(b',', EImports::ListEnd), word1(b']', EImports::ListEnd), min_indent, + outdent_col, EImports::Open, EImports::IndentListEnd, + EImports::IndentListEnd, Spaced::SpaceBefore ) ) @@ -849,6 +872,7 @@ where #[inline(always)] fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> { let min_indent = 1; + let outdent_col = 0; type Temp<'a> = ( (Option<&'a str>, ModuleName<'a>), @@ -875,8 +899,10 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports word1(b',', EImports::SetEnd), word1(b'}', EImports::SetEnd), min_indent, + outdent_col, EImports::Open, EImports::IndentSetEnd, + EImports::IndentSetEnd, Spaced::SpaceBefore ) )) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 3c25922ba9..6edeaa3cdd 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -407,6 +407,7 @@ pub enum ERecord<'a> { IndentBar(Position), IndentAmpersand(Position), IndentEnd(Position), + OutdentEnd(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -448,6 +449,7 @@ pub enum EList<'a> { IndentOpen(Position), IndentEnd(Position), + OutdentEnd(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1211,7 +1213,7 @@ macro_rules! collection { #[macro_export] macro_rules! collection_trailing_sep_e { - ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $indent_problem:expr, $space_before:expr) => { + ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $outdent_col:expr, $open_problem:expr, $indent_problem:expr, $outdent_problem:expr, $space_before:expr) => { skip_first!( $opening_brace, |arena, state| { @@ -1230,12 +1232,13 @@ macro_rules! collection_trailing_sep_e { ) ), $crate::blankspace::space0_e( - // we use min_indent=0 because we want to parse incorrectly indented closing braces - // and later fix these up in the formatter. - 0 /* min_indent */, + 0, $indent_problem) ).parse(arena, state)?; + let closing_brace_col = state.column(); + let closing_brace_pos = state.pos(); + let (_,_, state) = if parsed_elems.is_empty() { one_of_with_error![$open_problem; $closing_brace].parse(arena, state)? @@ -1243,6 +1246,13 @@ macro_rules! collection_trailing_sep_e { $closing_brace.parse(arena, state)? }; + #[allow(unused_comparisons)] // sometimes $outdent_col is 0 + if closing_brace_col < $outdent_col { + // We successfully parsed the collection but the closing brace was outdented + // further than expected. + return Err((MadeProgress, $outdent_problem(closing_brace_pos), state)); + } + if !spaces.is_empty() { if let Some(first) = parsed_elems.first_mut() { first.value = $space_before(arena.alloc(first.value), spaces) diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index 09e676075f..c2ec96fcf1 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -365,8 +365,10 @@ fn record_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PRec // word1_check_indent!(b'}', PRecord::End, min_indent, PRecord::IndentEnd), word1(b'}', PRecord::End), min_indent, + 0, PRecord::Open, PRecord::IndentEnd, + PRecord::IndentEnd, Pattern::SpaceBefore ) .parse(arena, state)?; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 45b34b7157..02e52f774c 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -31,8 +31,10 @@ fn tag_union_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ET word1(b',', ETypeTagUnion::End), word1(b']', ETypeTagUnion::End), min_indent, + 0, ETypeTagUnion::Open, ETypeTagUnion::IndentEnd, + ETypeTagUnion::IndentEnd, Tag::SpaceBefore ) .parse(arena, state)?; @@ -323,8 +325,10 @@ fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType // word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd), word1(b'}', ETypeRecord::End), min_indent, + 0, ETypeRecord::Open, ETypeRecord::IndentEnd, + ETypeRecord::IndentEnd, AssignedField::SpaceBefore ) .parse(arena, state)?; diff --git a/reporting/src/error/parse.rs b/reporting/src/error/parse.rs index 83b878a6c0..cb5a3e0b47 100644 --- a/reporting/src/error/parse.rs +++ b/reporting/src/error/parse.rs @@ -1,4 +1,4 @@ -use roc_parse::parser::{ENumber, FileError, SyntaxError}; +use roc_parse::parser::{ENumber, ERecord, FileError, SyntaxError}; use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Position, Region}; use std::path::PathBuf; @@ -516,23 +516,42 @@ fn to_expr_report<'a>( } } - EExpr::Record(_erecord, pos) => { - let surroundings = Region::new(start, *pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); + EExpr::Record(erecord, pos) => match erecord { + &ERecord::OutdentEnd(pos) => { + let surroundings = Region::new(start, pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); - let doc = alloc.stack(vec![ - alloc.reflow(r"I am partway through parsing an record, but I got stuck here:"), - alloc.region_with_subregion(lines.convert_region(surroundings), region), - alloc.concat(vec![alloc.reflow("TODO provide more context.")]), - ]); + let doc = alloc.stack(vec![ + alloc.reflow(r"I found the end of this record, but it's outdented too far:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region), + alloc.reflow(r"Did you mean to indent it further?"), + ]); - Report { - filename, - doc, - title: "RECORD PARSE PROBLEM".to_string(), - severity: Severity::RuntimeError, + Report { + filename, + doc, + title: "RECORD END OUDENTED TOO FAR".to_string(), + severity: Severity::RuntimeError, + } } - } + _ => { + let surroundings = Region::new(start, *pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing an record, but I got stuck here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region), + alloc.concat(vec![alloc.reflow("TODO provide more context.")]), + ]); + + Report { + filename, + doc, + title: "RECORD PARSE PROBLEM".to_string(), + severity: Severity::RuntimeError, + } + } + }, EExpr::Space(error, pos) => to_space_report(alloc, lines, filename, error, *pos), @@ -542,6 +561,23 @@ fn to_expr_report<'a>( EExpr::Ability(err, pos) => to_ability_def_report(alloc, lines, filename, err, *pos), + EExpr::IndentEnd(pos) => { + let surroundings = Region::new(start, *pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); + + let doc = alloc.stack(vec![ + alloc.reflow("Indentation unexpectedly ended here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region), + ]); + + Report { + filename, + doc, + title: "INDENTATION ENDED EARLY".to_string(), + // In an ideal world, this is recoverable and we keep parsing. + severity: Severity::Warning, + } + } _ => todo!("unhandled parse error: {:?}", parse_problem), } } @@ -1116,6 +1152,24 @@ fn to_list_report<'a>( severity: Severity::RuntimeError, } } + + EList::OutdentEnd(pos) => { + let surroundings = Region::new(start, pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I found the end of this list, but it's outdented too far:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region), + alloc.reflow(r"Did you mean to indent it further?"), + ]); + + Report { + filename, + doc, + title: "LIST END OUDENTED TOO FAR".to_string(), + severity: Severity::RuntimeError, + } + } } } diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 99cbc3d710..8eb035dea2 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -9780,4 +9780,65 @@ I need all branches in an `if` to have the same type! ), ) } + + #[test] + fn list_outdented_too_far() { + report_problem_as( + indoc!( + r#" + w = + a = [ + 1, 2, 3 + ] + a + w + "# + ), + indoc!( + r#" + ── LIST END OUDENTED TOO FAR ─────────────────────────────────────────────────── + + I found the end of this list, but it's outdented too far: + + 2│ a = [ + 3│ 1, 2, 3 + 4│ ] + ^ + + Did you mean to indent it further? + "# + ), + ) + } + + #[test] + fn record_outdented_too_far() { + report_problem_as( + indoc!( + r#" + w = + r = { + sweet: "disposition" + } + r + w + "# + ), + indoc!( + r#" + ── RECORD END OUDENTED TOO FAR ───────────────────────────────────────────────── + + I found the end of this record, but it's outdented too far: + + 1│ w = + 2│ r = { + 3│ sweet: "disposition" + 4│ } + ^ + + Did you mean to indent it further? + "# + ), + ) + } } From 233ea239793f6577ac3849bf4bf149e495a4c399 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Thu, 14 Apr 2022 10:45:12 -0400 Subject: [PATCH 5/6] Revert "Detect outdents too far" This reverts commit 1c2ca2e3f5ae12d114cc72970035caf937d6e413. --- compiler/parse/src/expr.rs | 84 +++++++-------------------- compiler/parse/src/module.rs | 26 --------- compiler/parse/src/parser.rs | 18 ++---- compiler/parse/src/pattern.rs | 2 - compiler/parse/src/type_annotation.rs | 4 -- compiler/parse/tests/test_parse.rs | 1 - reporting/src/error/parse.rs | 84 +++++---------------------- reporting/tests/test_reporting.rs | 61 ------------------- 8 files changed, 41 insertions(+), 239 deletions(-) diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index fbc57d27f8..4d0654b31f 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -190,7 +190,6 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> { /// pattern later fn parse_loc_term_or_underscore<'a>( min_indent: u32, - outdent_col: u32, options: ExprParseOptions, arena: &'a Bump, state: State<'a>, @@ -202,11 +201,8 @@ fn parse_loc_term_or_underscore<'a>( loc!(specialize(EExpr::Number, positive_number_literal_help())), loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))), loc!(underscore_expression()), - loc!(record_literal_help(min_indent, outdent_col)), - loc!(specialize( - EExpr::List, - list_literal_help(min_indent, outdent_col) - )), + loc!(record_literal_help(min_indent)), + loc!(specialize(EExpr::List, list_literal_help(min_indent))), loc!(map_with_arena!( assign_or_destructure_identifier(), ident_to_expr @@ -217,7 +213,6 @@ fn parse_loc_term_or_underscore<'a>( fn parse_loc_term<'a>( min_indent: u32, - outdent_col: u32, options: ExprParseOptions, arena: &'a Bump, state: State<'a>, @@ -228,11 +223,8 @@ fn parse_loc_term<'a>( loc!(specialize(EExpr::SingleQuote, single_quote_literal_help())), loc!(specialize(EExpr::Number, positive_number_literal_help())), loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))), - loc!(record_literal_help(min_indent, outdent_col)), - loc!(specialize( - EExpr::List, - list_literal_help(min_indent, outdent_col) - )), + loc!(record_literal_help(min_indent)), + loc!(specialize(EExpr::List, list_literal_help(min_indent))), loc!(map_with_arena!( assign_or_destructure_identifier(), ident_to_expr @@ -260,7 +252,6 @@ fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> { fn loc_possibly_negative_or_negated_term<'a>( min_indent: u32, - outdent_col: u32, options: ExprParseOptions, ) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ @@ -268,11 +259,7 @@ fn loc_possibly_negative_or_negated_term<'a>( let initial = state.clone(); let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term( - min_indent, - outdent_col, - options, - a, - s + min_indent, options, a, s )) .parse(arena, state)?; @@ -284,15 +271,13 @@ fn loc_possibly_negative_or_negated_term<'a>( loc!(specialize(EExpr::Number, number_literal_help())), loc!(map_with_arena!( and!(loc!(word1(b'!', EExpr::Start)), |a, s| { - parse_loc_term(min_indent, outdent_col, options, a, s) + parse_loc_term(min_indent, options, a, s) }), |arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| { Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not)) } )), - |arena, state| { - parse_loc_term_or_underscore(min_indent, outdent_col, options, arena, state) - } + |arena, state| { parse_loc_term_or_underscore(min_indent, options, arena, state) } ] } @@ -351,8 +336,8 @@ fn parse_expr_operator_chain<'a>( arena: &'a Bump, state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - let (_, expr, state) = loc_possibly_negative_or_negated_term(min_indent, start_column, options) - .parse(arena, state)?; + let (_, expr, state) = + loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state)?; let initial = state.clone(); let end = state.pos(); @@ -1348,8 +1333,7 @@ fn parse_expr_operator<'a>( BinOp::Minus if expr_state.end != op_start && op_end == new_start => { // negative terms - let (_, negated_expr, state) = - parse_loc_term(min_indent, start_column, options, arena, state)?; + let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?; let new_end = state.pos(); let arg = numeric_negate_expression( @@ -1483,9 +1467,7 @@ fn parse_expr_operator<'a>( _ => unreachable!(), }, ), - _ => match loc_possibly_negative_or_negated_term(min_indent, start_column, options) - .parse(arena, state) - { + _ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) { Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), Ok((_, mut new_expr, state)) => { let new_end = state.pos(); @@ -1544,7 +1526,7 @@ fn parse_expr_end<'a>( ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let parser = skip_first!( crate::blankspace::check_indent(min_indent, EExpr::IndentEnd), - move |a, s| parse_loc_term(min_indent, start_column, options, a, s) + move |a, s| parse_loc_term(min_indent, options, a, s) ); match parser.parse(arena, state.clone()) { @@ -2485,10 +2467,7 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> { } } -fn list_literal_help<'a>( - min_indent: u32, - outdent_col: u32, -) -> impl Parser<'a, Expr<'a>, EList<'a>> { +fn list_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EList<'a>> { move |arena, state| { let (_, elements, state) = collection_trailing_sep_e!( word1(b'[', EList::Open), @@ -2499,10 +2478,8 @@ fn list_literal_help<'a>( word1(b',', EList::End), word1(b']', EList::End), min_indent, - outdent_col, EList::Open, EList::IndentEnd, - EList::OutdentEnd, Expr::SpaceBefore ) .parse(arena, state)?; @@ -2578,7 +2555,6 @@ fn record_updateable_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> fn record_help<'a>( min_indent: u32, - outdent_col: u32, ) -> impl Parser< 'a, ( @@ -2613,8 +2589,8 @@ fn record_help<'a>( // `{ }` can be successfully parsed as an empty record, and then // changed by the formatter back into `{}`. zero_or_more!(word1(b' ', ERecord::End)), - |arena, state| { - let (_, (parsed_elems, comments), state) = and!( + skip_second!( + and!( trailing_sep_by0( word1(b',', ERecord::End), space0_before_optional_after( @@ -2624,35 +2600,19 @@ fn record_help<'a>( ERecord::IndentEnd ), ), - space0_e(0, ERecord::OutdentEnd) - ) - .parse(arena, state)?; - - let closing_brace_col = state.column(); - let closing_brace_pos = state.pos(); - - let (_, _, state) = word1(b'}', ERecord::End).parse(arena, state)?; - - if closing_brace_col < outdent_col { - return Err((MadeProgress, ERecord::OutdentEnd(closing_brace_pos), state)); - } - - Ok((MadeProgress, (parsed_elems, comments), state)) - } + // Allow outdented closing braces + space0_e(0, ERecord::IndentEnd) + ), + word1(b'}', ERecord::End) + ) )) ) ) } -fn record_literal_help<'a>( - min_indent: u32, - outdent_col: u32, -) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { +fn record_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { then( - loc!(specialize( - EExpr::Record, - record_help(min_indent, outdent_col) - )), + loc!(specialize(EExpr::Record, record_help(min_indent))), move |arena, state, _, loc_record| { let (opt_update, loc_assigned_fields_with_comments) = loc_record.value; diff --git a/compiler/parse/src/module.rs b/compiler/parse/src/module.rs index c2a6366dd3..b1e43773f5 100644 --- a/compiler/parse/src/module.rs +++ b/compiler/parse/src/module.rs @@ -416,7 +416,6 @@ fn provides_without_to<'a>() -> impl Parser< EProvides<'a>, > { let min_indent = 1; - let outdent_col = 0; and!( spaces_around_keyword( min_indent, @@ -432,10 +431,8 @@ fn provides_without_to<'a>() -> impl Parser< word1(b',', EProvides::ListEnd), word1(b']', EProvides::ListEnd), min_indent, - outdent_col, EProvides::Open, EProvides::IndentListEnd, - EProvides::IndentListEnd, Spaced::SpaceBefore ), // Optionally @@ -448,7 +445,6 @@ fn provides_without_to<'a>() -> impl Parser< fn provides_types<'a>( ) -> impl Parser<'a, Collection<'a, Loc>>>, EProvides<'a>> { let min_indent = 1; - let outdent_col = 0; skip_first!( // We only support spaces here, not newlines, because this is not intended @@ -468,10 +464,8 @@ fn provides_types<'a>( word1(b',', EProvides::ListEnd), word1(b'}', EProvides::ListEnd), min_indent, - outdent_col, EProvides::Open, EProvides::IndentListEnd, - EProvides::IndentListEnd, Spaced::SpaceBefore ) ) @@ -551,10 +545,8 @@ fn requires_rigids<'a>( word1(b',', ERequires::ListEnd), word1(b'}', ERequires::ListEnd), min_indent, - 0, ERequires::Open, ERequires::IndentListEnd, - ERequires::IndentListEnd, Spaced::SpaceBefore ) } @@ -585,7 +577,6 @@ fn exposes_values<'a>() -> impl Parser< EExposes, > { let min_indent = 1; - let outdent_col = 0; and!( spaces_around_keyword( @@ -601,10 +592,8 @@ fn exposes_values<'a>() -> impl Parser< word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, - outdent_col, EExposes::Open, EExposes::IndentListEnd, - EExposes::IndentListEnd, Spaced::SpaceBefore ) ) @@ -639,7 +628,6 @@ fn exposes_modules<'a>() -> impl Parser< EExposes, > { let min_indent = 1; - let outdent_col = 0; and!( spaces_around_keyword( @@ -655,10 +643,8 @@ fn exposes_modules<'a>() -> impl Parser< word1(b',', EExposes::ListEnd), word1(b']', EExposes::ListEnd), min_indent, - outdent_col, EExposes::Open, EExposes::IndentListEnd, - EExposes::IndentListEnd, Spaced::SpaceBefore ) ) @@ -688,7 +674,6 @@ struct Packages<'a> { #[inline(always)] fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { let min_indent = 1; - let outdent_col = 0; map!( and!( @@ -705,10 +690,8 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { word1(b',', EPackages::ListEnd), word1(b'}', EPackages::ListEnd), min_indent, - outdent_col, EPackages::Open, EPackages::IndentListEnd, - EPackages::IndentListEnd, Spaced::SpaceBefore ) ), @@ -758,7 +741,6 @@ fn generates_with<'a>() -> impl Parser< EGeneratesWith, > { let min_indent = 1; - let outdent_col = 0; and!( spaces_around_keyword( @@ -774,10 +756,8 @@ fn generates_with<'a>() -> impl Parser< word1(b',', EGeneratesWith::ListEnd), word1(b']', EGeneratesWith::ListEnd), min_indent, - outdent_col, EGeneratesWith::Open, EGeneratesWith::IndentListEnd, - EGeneratesWith::IndentListEnd, Spaced::SpaceBefore ) ) @@ -793,7 +773,6 @@ fn imports<'a>() -> impl Parser< EImports, > { let min_indent = 1; - let outdent_col = 0; and!( spaces_around_keyword( @@ -809,10 +788,8 @@ fn imports<'a>() -> impl Parser< word1(b',', EImports::ListEnd), word1(b']', EImports::ListEnd), min_indent, - outdent_col, EImports::Open, EImports::IndentListEnd, - EImports::IndentListEnd, Spaced::SpaceBefore ) ) @@ -872,7 +849,6 @@ where #[inline(always)] fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> { let min_indent = 1; - let outdent_col = 0; type Temp<'a> = ( (Option<&'a str>, ModuleName<'a>), @@ -899,10 +875,8 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports word1(b',', EImports::SetEnd), word1(b'}', EImports::SetEnd), min_indent, - outdent_col, EImports::Open, EImports::IndentSetEnd, - EImports::IndentSetEnd, Spaced::SpaceBefore ) )) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 6edeaa3cdd..3c25922ba9 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -407,7 +407,6 @@ pub enum ERecord<'a> { IndentBar(Position), IndentAmpersand(Position), IndentEnd(Position), - OutdentEnd(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -449,7 +448,6 @@ pub enum EList<'a> { IndentOpen(Position), IndentEnd(Position), - OutdentEnd(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -1213,7 +1211,7 @@ macro_rules! collection { #[macro_export] macro_rules! collection_trailing_sep_e { - ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $outdent_col:expr, $open_problem:expr, $indent_problem:expr, $outdent_problem:expr, $space_before:expr) => { + ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $indent_problem:expr, $space_before:expr) => { skip_first!( $opening_brace, |arena, state| { @@ -1232,13 +1230,12 @@ macro_rules! collection_trailing_sep_e { ) ), $crate::blankspace::space0_e( - 0, + // we use min_indent=0 because we want to parse incorrectly indented closing braces + // and later fix these up in the formatter. + 0 /* min_indent */, $indent_problem) ).parse(arena, state)?; - let closing_brace_col = state.column(); - let closing_brace_pos = state.pos(); - let (_,_, state) = if parsed_elems.is_empty() { one_of_with_error![$open_problem; $closing_brace].parse(arena, state)? @@ -1246,13 +1243,6 @@ macro_rules! collection_trailing_sep_e { $closing_brace.parse(arena, state)? }; - #[allow(unused_comparisons)] // sometimes $outdent_col is 0 - if closing_brace_col < $outdent_col { - // We successfully parsed the collection but the closing brace was outdented - // further than expected. - return Err((MadeProgress, $outdent_problem(closing_brace_pos), state)); - } - if !spaces.is_empty() { if let Some(first) = parsed_elems.first_mut() { first.value = $space_before(arena.alloc(first.value), spaces) diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index c2ec96fcf1..09e676075f 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -365,10 +365,8 @@ fn record_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PRec // word1_check_indent!(b'}', PRecord::End, min_indent, PRecord::IndentEnd), word1(b'}', PRecord::End), min_indent, - 0, PRecord::Open, PRecord::IndentEnd, - PRecord::IndentEnd, Pattern::SpaceBefore ) .parse(arena, state)?; diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 02e52f774c..45b34b7157 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -31,10 +31,8 @@ fn tag_union_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ET word1(b',', ETypeTagUnion::End), word1(b']', ETypeTagUnion::End), min_indent, - 0, ETypeTagUnion::Open, ETypeTagUnion::IndentEnd, - ETypeTagUnion::IndentEnd, Tag::SpaceBefore ) .parse(arena, state)?; @@ -325,10 +323,8 @@ fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType // word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd), word1(b'}', ETypeRecord::End), min_indent, - 0, ETypeRecord::Open, ETypeRecord::IndentEnd, - ETypeRecord::IndentEnd, AssignedField::SpaceBefore ) .parse(arena, state)?; diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 759276523c..7b78187826 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -220,7 +220,6 @@ mod test_parse { pass/ops_with_newlines.expr, pass/outdented_list.expr, pass/outdented_record.expr, - pass/outdented_app_with_record.expr, pass/packed_singleton_list.expr, pass/parenthetical_apply.expr, pass/parenthetical_basic_field.expr, diff --git a/reporting/src/error/parse.rs b/reporting/src/error/parse.rs index cb5a3e0b47..83b878a6c0 100644 --- a/reporting/src/error/parse.rs +++ b/reporting/src/error/parse.rs @@ -1,4 +1,4 @@ -use roc_parse::parser::{ENumber, ERecord, FileError, SyntaxError}; +use roc_parse::parser::{ENumber, FileError, SyntaxError}; use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Position, Region}; use std::path::PathBuf; @@ -516,42 +516,23 @@ fn to_expr_report<'a>( } } - EExpr::Record(erecord, pos) => match erecord { - &ERecord::OutdentEnd(pos) => { - let surroundings = Region::new(start, pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); + EExpr::Record(_erecord, pos) => { + let surroundings = Region::new(start, *pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); - let doc = alloc.stack(vec![ - alloc.reflow(r"I found the end of this record, but it's outdented too far:"), - alloc.region_with_subregion(lines.convert_region(surroundings), region), - alloc.reflow(r"Did you mean to indent it further?"), - ]); + let doc = alloc.stack(vec![ + alloc.reflow(r"I am partway through parsing an record, but I got stuck here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region), + alloc.concat(vec![alloc.reflow("TODO provide more context.")]), + ]); - Report { - filename, - doc, - title: "RECORD END OUDENTED TOO FAR".to_string(), - severity: Severity::RuntimeError, - } + Report { + filename, + doc, + title: "RECORD PARSE PROBLEM".to_string(), + severity: Severity::RuntimeError, } - _ => { - let surroundings = Region::new(start, *pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); - - let doc = alloc.stack(vec![ - alloc.reflow(r"I am partway through parsing an record, but I got stuck here:"), - alloc.region_with_subregion(lines.convert_region(surroundings), region), - alloc.concat(vec![alloc.reflow("TODO provide more context.")]), - ]); - - Report { - filename, - doc, - title: "RECORD PARSE PROBLEM".to_string(), - severity: Severity::RuntimeError, - } - } - }, + } EExpr::Space(error, pos) => to_space_report(alloc, lines, filename, error, *pos), @@ -561,23 +542,6 @@ fn to_expr_report<'a>( EExpr::Ability(err, pos) => to_ability_def_report(alloc, lines, filename, err, *pos), - EExpr::IndentEnd(pos) => { - let surroundings = Region::new(start, *pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); - - let doc = alloc.stack(vec![ - alloc.reflow("Indentation unexpectedly ended here:"), - alloc.region_with_subregion(lines.convert_region(surroundings), region), - ]); - - Report { - filename, - doc, - title: "INDENTATION ENDED EARLY".to_string(), - // In an ideal world, this is recoverable and we keep parsing. - severity: Severity::Warning, - } - } _ => todo!("unhandled parse error: {:?}", parse_problem), } } @@ -1152,24 +1116,6 @@ fn to_list_report<'a>( severity: Severity::RuntimeError, } } - - EList::OutdentEnd(pos) => { - let surroundings = Region::new(start, pos); - let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); - - let doc = alloc.stack(vec![ - alloc.reflow(r"I found the end of this list, but it's outdented too far:"), - alloc.region_with_subregion(lines.convert_region(surroundings), region), - alloc.reflow(r"Did you mean to indent it further?"), - ]); - - Report { - filename, - doc, - title: "LIST END OUDENTED TOO FAR".to_string(), - severity: Severity::RuntimeError, - } - } } } diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 8eb035dea2..99cbc3d710 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -9780,65 +9780,4 @@ I need all branches in an `if` to have the same type! ), ) } - - #[test] - fn list_outdented_too_far() { - report_problem_as( - indoc!( - r#" - w = - a = [ - 1, 2, 3 - ] - a - w - "# - ), - indoc!( - r#" - ── LIST END OUDENTED TOO FAR ─────────────────────────────────────────────────── - - I found the end of this list, but it's outdented too far: - - 2│ a = [ - 3│ 1, 2, 3 - 4│ ] - ^ - - Did you mean to indent it further? - "# - ), - ) - } - - #[test] - fn record_outdented_too_far() { - report_problem_as( - indoc!( - r#" - w = - r = { - sweet: "disposition" - } - r - w - "# - ), - indoc!( - r#" - ── RECORD END OUDENTED TOO FAR ───────────────────────────────────────────────── - - I found the end of this record, but it's outdented too far: - - 1│ w = - 2│ r = { - 3│ sweet: "disposition" - 4│ } - ^ - - Did you mean to indent it further? - "# - ), - ) - } } From 5652e72f994832766b7a9029a5c0c8d05ecbd019 Mon Sep 17 00:00:00 2001 From: Ayaz <20735482+ayazhafiz@users.noreply.github.com> Date: Thu, 14 Apr 2022 12:50:28 -0400 Subject: [PATCH 6/6] Missing test --- compiler/parse/tests/test_parse.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 7b78187826..759276523c 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -220,6 +220,7 @@ mod test_parse { pass/ops_with_newlines.expr, pass/outdented_list.expr, pass/outdented_record.expr, + pass/outdented_app_with_record.expr, pass/packed_singleton_list.expr, pass/parenthetical_apply.expr, pass/parenthetical_basic_field.expr,