Make ListStart branch less brittle; Add ListEnd branch

This commit is contained in:
Jared Ramirez 2024-12-29 13:21:09 -08:00
parent 69c36af43e
commit c28d6b9411
No known key found for this signature in database
GPG key ID: 41158983F521D68C
2 changed files with 120 additions and 69 deletions

View file

@ -6432,6 +6432,82 @@ All branches in an `if` must have the same type!
)
}
#[test]
fn exposes_start() {
report_header_problem_as(
indoc!(
r"
module foobar []
"
),
indoc!(
r#"
WEIRD EXPOSES in /code/proj/Main.roc
I am partway through parsing a header, but I got stuck here:
1 module foobar []
^
I was expecting an `exposes` list like
[Animal, default, tame]
"#
),
)
}
#[test]
fn exposes_missing_comma() {
report_header_problem_as(
indoc!(
r"
module [value func]
"
),
indoc!(
r#"
WEIRD EXPOSES in /code/proj/Main.roc
I am partway through parsing an `exposes` list, but I got stuck here:
1 module [value func]
^
I was expecting a type name, value name or function name next, like
[Animal, default, tame]
"#
),
)
}
#[test]
fn exposes_end() {
report_header_problem_as(
indoc!(
r"
module [value
"
),
indoc!(
r#"
WEIRD EXPOSES in /code/proj/Main.roc
I am partway through parsing an `exposes` list, but I got stuck here:
1 module [value
2
^
I was expecting a type name, value name or function name next, like
[Animal, default, tame]
"#
),
)
}
#[test]
fn invalid_app_name() {
report_header_problem_as(
@ -6458,39 +6534,6 @@ All branches in an `if` must have the same type!
)
}
#[test]
fn unneeded_module_name() {
report_header_problem_as(
indoc!(
r"
module foobar []
"
),
indoc!(
r#"
WEIRD MODULE NAME in /code/proj/Main.roc
I am partway through parsing a header, but I got stuck here:
1 module foobar []
^
I am expecting a list of exports like
module [func, value]
or module params like
module { echo } -> [func, value]
If you're trying to specify a module name, recall that unlike
application names, module names are not specified in the header.
Instead, they are derived from the name of the module's filename.
"#
),
)
}
test_report!(
apply_unary_negative,
indoc!(

View file

@ -4049,20 +4049,24 @@ fn to_exposes_report<'a>(
let severity = Severity::RuntimeError;
match *parse_problem {
EExposes::ListEnd(pos) | // TODO: give this its own error message
EExposes::Identifier(pos) => {
// TODO: It would be nice to give ListEnd it's own error message.
// However with how parsing error are currently reported, ListEnd and
// Identifier are tightly coupled and Identifier is basically never
// "thrown". Splitting these up to make more specific error messages
// would require tweaking how parsing works.
EExposes::ListEnd(pos) | EExposes::Identifier(pos) => {
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 an `exposes` list, but I got stuck here:"),
alloc.concat([
alloc.reflow("I am partway through parsing an "),
alloc.keyword("exposes"),
alloc.reflow(" list, but I got stuck here:"),
]),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([alloc.reflow(
"I was expecting a type name, value name or function name next, like",
)]),
alloc
.parser_suggestion("[Animal, default, tame]")
.indent(4),
alloc.reflow("I was expecting a type name, value name or function name next, like"),
alloc.parser_suggestion("[Animal, default, tame]").indent(4),
]);
Report {
@ -4085,9 +4089,7 @@ fn to_exposes_report<'a>(
alloc.keyword("exposes"),
alloc.reflow(" keyword next, like"),
]),
alloc
.parser_suggestion("[Animal, default, tame]")
.indent(4),
alloc.parser_suggestion("[Animal, default, tame]").indent(4),
]);
Report {
@ -4100,7 +4102,20 @@ fn to_exposes_report<'a>(
EExposes::Space(error, pos) => to_space_report(alloc, lines, filename, &error, pos),
EExposes::ListStart(pos@Position { offset: 7 }) => {
// TODO: Depending on the type of header we're parsing, it would be
// great to add more context to this error message. For example, if
// we're parsing a `module` then saying something like:
//
// I was expecting an exposes list like
//
// ...
//
// or module params like
// ...
//
// would be great. But currently we don't capture the type of header in
// the parse problem data type, so this isn't possible
EExposes::ListStart(pos) => {
let surroundings = Region::new(start, pos);
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
@ -4108,40 +4123,33 @@ fn to_exposes_report<'a>(
alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"),
alloc.region_with_subregion(lines.convert_region(surroundings), region, severity),
alloc.concat([
alloc.reflow("I am expecting a list of exports like"),
]),
alloc
.parser_suggestion("module [func, value]")
.indent(4),
alloc.concat([
alloc.reflow("or module params like"),
]),
alloc
.parser_suggestion("module { echo } -> [func, value]")
.indent(4),
alloc.concat([
alloc.reflow("If you're trying to specify a module name, recall that unlike "),
alloc.reflow("application names, module names are not specified in the header. "),
alloc.reflow("Instead, they are derived from the name of the module's filename."),
alloc.reflow("I was expecting an "),
alloc.keyword("exposes"),
alloc.reflow(" list like"),
]),
alloc.parser_suggestion("[Animal, default, tame]").indent(4),
]);
Report {
filename,
doc,
title: "WEIRD MODULE NAME".to_string(),
title: "WEIRD EXPOSES".to_string(),
severity,
}
},
}
// If you're adding or changing syntax, please handle the case with a
// good error message above instead of adding more unhandled cases below.
EExposes::Open(pos) |
EExposes::IndentExposes(pos) |
EExposes::IndentListStart(pos) |
EExposes::ListStart(pos) =>
to_unhandled_parse_error_report(alloc, lines, filename, format!("{:?}", parse_problem), pos, start)
EExposes::Open(pos) | EExposes::IndentExposes(pos) | EExposes::IndentListStart(pos) => {
to_unhandled_parse_error_report(
alloc,
lines,
filename,
format!("{:?}", parse_problem),
pos,
start,
)
}
}
}