Add parsing error reporting for list patterns

This commit is contained in:
Ayaz Hafiz 2022-10-26 18:05:52 -05:00
parent 9bb523ce97
commit 89867a4dc5
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
4 changed files with 247 additions and 10 deletions

View file

@ -566,8 +566,6 @@ pub enum PList<'a> {
Space(BadInputError, Position),
IndentOpen(Position),
IndentColon(Position),
IndentOptional(Position),
IndentEnd(Position),
}

View file

@ -3,8 +3,8 @@ use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::ident::{lowercase_ident, parse_ident, Ident};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, optional, specialize, specialize_ref, then, word1, word2, EPattern, PInParens,
PList, PRecord, ParseResult, Parser,
backtrackable, optional, specialize, specialize_ref, then, word1, word2, word3, EPattern,
PInParens, PList, PRecord, ParseResult, Parser,
};
use crate::state::State;
use bumpalo::collections::string::String;
@ -59,10 +59,10 @@ pub fn loc_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
EPattern::Record,
crate::pattern::record_pattern_help(min_indent)
)),
loc!(specialize(EPattern::List, list_pattern_help(min_indent))),
loc!(number_pattern_help()),
loc!(string_pattern_help()),
loc!(single_quote_pattern_help()),
loc!(specialize(EPattern::List, list_pattern_help(min_indent))),
)
}
@ -227,13 +227,23 @@ fn list_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PList<
fn list_element_pattern<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
one_of!(
three_list_rest_pattern_error(),
list_rest_pattern(),
specialize_ref(PList::Pattern, loc_pattern_help(min_indent)),
)
}
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
then(
loc!(word3(b'.', b'.', b'.', PList::Rest)),
|_arena, state, _progress, word| {
Err((MadeProgress, PList::Rest(word.region.start()), state))
},
)
}
fn list_rest_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
map!(loc!(word2(b'.', b'.', PList::Rest)), |loc_word: Loc<_>| {
map!(loc!(word2(b'.', b'.', PList::Open)), |loc_word: Loc<_>| {
loc_word.map(|_| Pattern::ListRest)
})
}

View file

@ -1,4 +1,4 @@
use roc_parse::parser::{ENumber, FileError, SyntaxError};
use roc_parse::parser::{ENumber, FileError, PList, SyntaxError};
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Position, Region};
use std::path::PathBuf;
@ -23,6 +23,10 @@ fn note_for_record_pattern_indent<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocB
alloc.note("I may be confused by indentation")
}
fn note_for_list_pattern_indent<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
alloc.note("I may be confused by indentation")
}
fn note_for_tag_union_type_indent<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
alloc.note("I may be confused by indentation")
}
@ -46,6 +50,15 @@ fn record_patterns_look_like<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilde
])
}
fn list_patterns_look_like<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
alloc.concat([
alloc.reflow(r"Record pattern look like "),
alloc.parser_suggestion("[1, 2, ..]"),
alloc.reflow(" or "),
alloc.parser_suggestion("[first, .., last]"),
])
}
fn to_syntax_report<'a>(
alloc: &'a RocDocAllocator<'a>,
lines: &LineInfo,
@ -1595,6 +1608,7 @@ fn to_pattern_report<'a>(
}
}
EPattern::Record(record, pos) => to_precord_report(alloc, lines, filename, record, *pos),
EPattern::List(list, pos) => to_plist_report(alloc, lines, filename, list, *pos),
EPattern::PInParens(inparens, pos) => {
to_pattern_in_parens_report(alloc, lines, filename, inparens, *pos)
}
@ -1855,6 +1869,155 @@ fn to_precord_report<'a>(
}
}
fn to_plist_report<'a>(
alloc: &'a RocDocAllocator<'a>,
lines: &LineInfo,
filename: PathBuf,
parse_problem: &PList<'a>,
start: Position,
) -> Report<'a> {
match *parse_problem {
PList::Open(pos) => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
let doc = alloc.stack([
alloc.reflow(r"I just started parsing a list pattern, but I got stuck here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
list_patterns_look_like(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED LIST PATTERN".to_string(),
severity: Severity::RuntimeError,
}
}
PList::End(pos) => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
let doc = alloc.stack([
alloc.reflow("I am partway through parsing a list pattern, but I got stuck here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.concat([
alloc.reflow(
r"I was expecting to see a closing square brace before this, so try adding a ",
),
alloc.parser_suggestion("]"),
alloc.reflow(" and see if that helps?"),
])]);
Report {
filename,
doc,
title: "UNFINISHED LIST PATTERN".to_string(),
severity: Severity::RuntimeError,
}
}
PList::Rest(pos) => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
let doc = alloc.stack([
alloc.reflow("It looks like you may trying to write a list rest pattern, but it's not the form I expect:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.concat([
alloc.reflow(
r"List rest patterns, which match zero or more elements in a list, are denoted with ",
),
alloc.parser_suggestion(".."),
alloc.reflow(" - is that what you meant?"),
])]);
Report {
filename,
doc,
title: "INCORRECT REST PATTERN".to_string(),
severity: Severity::RuntimeError,
}
}
PList::Pattern(pattern, pos) => to_pattern_report(alloc, lines, filename, pattern, pos),
PList::IndentOpen(pos) => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
let doc = alloc.stack([
alloc.reflow(r"I just started parsing a list pattern, but I got stuck here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
record_patterns_look_like(alloc),
note_for_list_pattern_indent(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED LIST PATTERN".to_string(),
severity: Severity::RuntimeError,
}
}
PList::IndentEnd(pos) => {
match next_line_starts_with_close_square_bracket(
alloc.src_lines,
lines.convert_pos(pos),
) {
Some(curly_pos) => {
let surroundings = LineColumnRegion::new(lines.convert_pos(start), curly_pos);
let region = LineColumnRegion::from_pos(curly_pos);
let doc = alloc.stack([
alloc.reflow(
"I am partway through parsing a list pattern, but I got stuck here:",
),
alloc.region_with_subregion(surroundings, region),
alloc.concat([
alloc.reflow("I need this square brace to be indented more. Try adding more spaces before it!"),
]),
]);
Report {
filename,
doc,
title: "NEED MORE INDENTATION".to_string(),
severity: Severity::RuntimeError,
}
}
None => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
let doc = alloc.stack([
alloc.reflow(
r"I am partway through parsing a list pattern, but I got stuck here:",
),
alloc.region_with_subregion(lines.convert_region(surroundings), region),
alloc.concat([
alloc.reflow("I was expecting to see a closing square "),
alloc.reflow("brace before this, so try adding a "),
alloc.parser_suggestion("]"),
alloc.reflow(" and see if that helps?"),
]),
note_for_list_pattern_indent(alloc),
]);
Report {
filename,
doc,
title: "UNFINISHED LIST PATTERN".to_string(),
severity: Severity::RuntimeError,
}
}
}
}
PList::Space(error, pos) => to_space_report(alloc, lines, filename, &error, pos),
}
}
fn to_pattern_in_parens_report<'a>(
alloc: &'a RocDocAllocator<'a>,
lines: &LineInfo,
@ -1941,9 +2104,9 @@ fn to_pattern_in_parens_report<'a>(
PInParens::IndentEnd(pos) => {
match next_line_starts_with_close_parenthesis(alloc.src_lines, lines.convert_pos(pos)) {
Some(curly_pos) => {
let surroundings = LineColumnRegion::new(lines.convert_pos(start), curly_pos);
let region = LineColumnRegion::from_pos(curly_pos);
Some(close_pos) => {
let surroundings = LineColumnRegion::new(lines.convert_pos(start), close_pos);
let region = LineColumnRegion::from_pos(close_pos);
let doc = alloc.stack([
alloc.reflow(

View file

@ -11665,4 +11665,70 @@ All branches in an `if` must have the same type!
clause of `x`.
"###
);
test_report!(
list_pattern_not_terminated,
indoc!(
r#"
when [] is
[1, 2, -> ""
"#
),
@r###"
UNFINISHED LIST PATTERN tmp/list_pattern_not_terminated/Test.roc
I am partway through parsing a list pattern, but I got stuck here:
5 [1, 2, -> ""
^
I was expecting to see a closing square brace before this, so try
adding a ] and see if that helps?
"###
);
test_report!(
list_pattern_weird_indent,
indoc!(
r#"
when [] is
[1, 2,
3] -> ""
"#
),
@r###"
UNFINISHED LIST PATTERN tmp/list_pattern_weird_indent/Test.roc
I am partway through parsing a list pattern, but I got stuck here:
5 [1, 2,
6 3] -> ""
^
I was expecting to see a closing square brace before this, so try
adding a ] and see if that helps?
"###
);
test_report!(
list_pattern_weird_rest_pattern,
indoc!(
r#"
when [] is
[...] -> ""
"#
),
@r###"
INCORRECT REST PATTERN tmp/list_pattern_weird_rest_pattern/Test.roc
It looks like you may trying to write a list rest pattern, but it's
not the form I expect:
5 [...] -> ""
^
List rest patterns, which match zero or more elements in a list, are
denoted with .. - is that what you meant?
"###
);
}