mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-01 10:52:18 +00:00
Add parsing error reporting for list patterns
This commit is contained in:
parent
9bb523ce97
commit
89867a4dc5
4 changed files with 247 additions and 10 deletions
|
@ -566,8 +566,6 @@ pub enum PList<'a> {
|
||||||
Space(BadInputError, Position),
|
Space(BadInputError, Position),
|
||||||
|
|
||||||
IndentOpen(Position),
|
IndentOpen(Position),
|
||||||
IndentColon(Position),
|
|
||||||
IndentOptional(Position),
|
|
||||||
IndentEnd(Position),
|
IndentEnd(Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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::ident::{lowercase_ident, parse_ident, Ident};
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
backtrackable, optional, specialize, specialize_ref, then, word1, word2, EPattern, PInParens,
|
backtrackable, optional, specialize, specialize_ref, then, word1, word2, word3, EPattern,
|
||||||
PList, PRecord, ParseResult, Parser,
|
PInParens, PList, PRecord, ParseResult, Parser,
|
||||||
};
|
};
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
use bumpalo::collections::string::String;
|
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,
|
EPattern::Record,
|
||||||
crate::pattern::record_pattern_help(min_indent)
|
crate::pattern::record_pattern_help(min_indent)
|
||||||
)),
|
)),
|
||||||
|
loc!(specialize(EPattern::List, list_pattern_help(min_indent))),
|
||||||
loc!(number_pattern_help()),
|
loc!(number_pattern_help()),
|
||||||
loc!(string_pattern_help()),
|
loc!(string_pattern_help()),
|
||||||
loc!(single_quote_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>> {
|
fn list_element_pattern<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
|
three_list_rest_pattern_error(),
|
||||||
list_rest_pattern(),
|
list_rest_pattern(),
|
||||||
specialize_ref(PList::Pattern, loc_pattern_help(min_indent)),
|
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>> {
|
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)
|
loc_word.map(|_| Pattern::ListRest)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Position, Region};
|
||||||
use std::path::PathBuf;
|
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")
|
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> {
|
fn note_for_tag_union_type_indent<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
alloc.note("I may be confused by indentation")
|
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>(
|
fn to_syntax_report<'a>(
|
||||||
alloc: &'a RocDocAllocator<'a>,
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
lines: &LineInfo,
|
lines: &LineInfo,
|
||||||
|
@ -1595,6 +1608,7 @@ fn to_pattern_report<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EPattern::Record(record, pos) => to_precord_report(alloc, lines, filename, record, *pos),
|
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) => {
|
EPattern::PInParens(inparens, pos) => {
|
||||||
to_pattern_in_parens_report(alloc, lines, filename, 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>(
|
fn to_pattern_in_parens_report<'a>(
|
||||||
alloc: &'a RocDocAllocator<'a>,
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
lines: &LineInfo,
|
lines: &LineInfo,
|
||||||
|
@ -1941,9 +2104,9 @@ fn to_pattern_in_parens_report<'a>(
|
||||||
|
|
||||||
PInParens::IndentEnd(pos) => {
|
PInParens::IndentEnd(pos) => {
|
||||||
match next_line_starts_with_close_parenthesis(alloc.src_lines, lines.convert_pos(pos)) {
|
match next_line_starts_with_close_parenthesis(alloc.src_lines, lines.convert_pos(pos)) {
|
||||||
Some(curly_pos) => {
|
Some(close_pos) => {
|
||||||
let surroundings = LineColumnRegion::new(lines.convert_pos(start), curly_pos);
|
let surroundings = LineColumnRegion::new(lines.convert_pos(start), close_pos);
|
||||||
let region = LineColumnRegion::from_pos(curly_pos);
|
let region = LineColumnRegion::from_pos(close_pos);
|
||||||
|
|
||||||
let doc = alloc.stack([
|
let doc = alloc.stack([
|
||||||
alloc.reflow(
|
alloc.reflow(
|
||||||
|
|
|
@ -11665,4 +11665,70 @@ All branches in an `if` must have the same type!
|
||||||
clause of `x`.
|
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?
|
||||||
|
"###
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue