Merge pull request #6732 from roc-lang/import-parse-reporting

Friendly reporting of import syntax errors
This commit is contained in:
Ayaz 2024-05-07 21:29:58 -05:00 committed by GitHub
commit 010aed88f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 517 additions and 116 deletions

View file

@ -9,8 +9,7 @@ use crate::blankspace::{
space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before,
};
use crate::ident::{
integer_ident, lowercase_ident, parse_ident, unqualified_ident, uppercase_ident, Accessor,
Ident, Suffix,
integer_ident, lowercase_ident, parse_ident, unqualified_ident, Accessor, Ident, Suffix,
};
use crate::module::module_name_help;
use crate::parser::{
@ -656,8 +655,9 @@ pub fn parse_single_def<'a>(
min_indent,
) {
Err((NoProgress, _)) => {
match loc!(import()).parse(arena, state.clone(), min_indent) {
Err((_, _)) => {
let pos_before_import = state.pos();
match import().parse(arena, state.clone(), min_indent) {
Err((NoProgress, _)) => {
match parse_expect.parse(arena, state.clone(), min_indent) {
Err((_, _)) => {
// a hacky way to get expression-based error messages. TODO fix this
@ -684,12 +684,16 @@ pub fn parse_single_def<'a>(
),
}
}
Ok((_, loc_import, state)) => Ok((
Err((MadeProgress, err)) => {
Err((MadeProgress, EExpr::Import(err, pos_before_import)))
}
Ok((_, (loc_import, spaces_after), state)) => Ok((
MadeProgress,
Some(SingleDef {
type_or_value: Either::Second(loc_import.value),
region: loc_import.region,
spaces_before: spaces_before_current,
spaces_after,
}),
state,
)),
@ -753,6 +757,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::First(type_def),
region: def_region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -813,6 +818,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::First(type_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -836,6 +842,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::First(type_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -849,6 +856,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -887,6 +895,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::First(type_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -911,6 +920,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::First(type_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -924,6 +934,7 @@ pub fn parse_single_def<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
));
@ -957,10 +968,23 @@ pub fn parse_single_def<'a>(
}
}
fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
skip_first!(
parser::keyword(keyword::IMPORT, EImport::Import),
increment_min_indent(one_of!(import_body(), import_ingested_file_body()))
fn import<'a>() -> impl Parser<'a, (Loc<ValueDef<'a>>, &'a [CommentOrNewline<'a>]), EImport<'a>> {
then(
and!(
loc!(skip_first!(
parser::keyword(keyword::IMPORT, EImport::Import),
increment_min_indent(one_of!(import_body(), import_ingested_file_body()))
)),
space0_e(EImport::EndNewline)
),
|_arena, state, progress, (import, spaces_after)| {
if !spaces_after.is_empty() || state.has_reached_end() {
Ok((progress, (import, spaces_after), state))
} else {
// We require EOF, comment, or newline after import
Err((progress, EImport::EndNewline(state.pos())))
}
},
)
}
@ -969,8 +993,8 @@ fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
record!(ModuleImport {
before_name: space0_e(EImport::IndentStart),
name: loc!(imported_module_name()),
alias: optional(backtrackable(import_as())),
exposed: optional(backtrackable(import_exposing()))
alias: optional(import_as()),
exposed: optional(import_exposing())
}),
ValueDef::ModuleImport
)
@ -997,10 +1021,20 @@ fn import_as<'a>(
EImport::IndentAs,
EImport::IndentAlias
),
item: loc!(map!(
specialize_err(|_, pos| EImport::Alias(pos), uppercase_ident()),
ImportAlias::new
))
item: then(
specialize_err(|_, pos| EImport::Alias(pos), loc!(unqualified_ident())),
|_arena, state, _progress, loc_ident| {
match loc_ident.value.chars().next() {
Some(first) if first.is_uppercase() => Ok((
MadeProgress,
loc_ident.map(|ident| ImportAlias::new(ident)),
state,
)),
Some(_) => Err((MadeProgress, EImport::LowercaseAlias(loc_ident.region))),
None => Err((MadeProgress, EImport::Alias(state.pos()))),
}
}
)
})
}
@ -1133,6 +1167,7 @@ pub fn parse_single_def_assignment<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_rest_of_def,
));
@ -1154,6 +1189,7 @@ pub fn parse_single_def_assignment<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_first_expression,
));
@ -1169,6 +1205,7 @@ pub fn parse_single_def_assignment<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_first_expression,
))
@ -1216,6 +1253,7 @@ fn parse_statement_inside_def<'a>(
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state,
))
@ -1299,10 +1337,16 @@ fn parse_defs_end<'a>(
Ok((_, Some(single_def), next_state)) => {
let region = single_def.region;
let spaces_before_current = single_def.spaces_before;
let spaces_after_current = single_def.spaces_after;
match single_def.type_or_value {
Either::First(type_def) => {
defs.push_type_def(type_def, region, spaces_before_current, &[]);
defs.push_type_def(
type_def,
region,
spaces_before_current,
spaces_after_current,
);
}
Either::Second(value_def) => {
// If we got a ValueDef::Body, check if a type annotation preceded it.
@ -1364,7 +1408,12 @@ fn parse_defs_end<'a>(
if !joined {
// the previous and current def can't be joined up
defs.push_value_def(value_def, region, spaces_before_current, &[]);
defs.push_value_def(
value_def,
region,
spaces_before_current,
spaces_after_current,
);
}
}
}
@ -1386,6 +1435,7 @@ pub struct SingleDef<'a> {
pub type_or_value: Either<TypeDef<'a>, ValueDef<'a>>,
pub region: Region,
pub spaces_before: &'a [CommentOrNewline<'a>],
pub spaces_after: &'a [CommentOrNewline<'a>],
}
fn parse_defs_expr<'a>(
@ -2873,11 +2923,11 @@ fn dbg_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<
fn import_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, import_def, state) =
loc!(specialize_err(EExpr::Import, import())).parse(arena, state, min_indent)?;
let (_, (import_def, spaces_after), state) =
specialize_err(EExpr::Import, import()).parse(arena, state, min_indent)?;
let mut defs = Defs::default();
defs.push_value_def(import_def.value, import_def.region, &[], &[]);
defs.push_value_def(import_def.value, import_def.region, &[], spaces_after);
parse_defs_expr(options, min_indent, defs, arena, state)
}

View file

@ -534,6 +534,7 @@ pub enum EImport<'a> {
As(Position),
IndentAlias(Position),
Alias(Position),
LowercaseAlias(Region),
IndentExposing(Position),
Exposing(Position),
ExposingListStart(Position),
@ -548,6 +549,7 @@ pub enum EImport<'a> {
IndentAnnotation(Position),
Annotation(EType<'a>, Position),
Space(BadInputError, Position),
EndNewline(Position),
}
#[derive(Debug, Clone, PartialEq, Eq)]