mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
improve messages for tag names
This commit is contained in:
parent
19d3e43f09
commit
b204154fec
4 changed files with 172 additions and 85 deletions
|
@ -391,18 +391,14 @@ pub enum Type<'a> {
|
||||||
pub enum TRecord<'a> {
|
pub enum TRecord<'a> {
|
||||||
End(Row, Col),
|
End(Row, Col),
|
||||||
Open(Row, Col),
|
Open(Row, Col),
|
||||||
///
|
|
||||||
Field(Row, Col),
|
Field(Row, Col),
|
||||||
Colon(Row, Col),
|
Colon(Row, Col),
|
||||||
Optional(Row, Col),
|
Optional(Row, Col),
|
||||||
Type(&'a Type<'a>, Row, Col),
|
Type(&'a Type<'a>, Row, Col),
|
||||||
|
|
||||||
// TODO REMOVE in favor of Type
|
|
||||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
|
||||||
|
|
||||||
///
|
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
///
|
|
||||||
IndentOpen(Row, Col),
|
IndentOpen(Row, Col),
|
||||||
IndentColon(Row, Col),
|
IndentColon(Row, Col),
|
||||||
IndentOptional(Row, Col),
|
IndentOptional(Row, Col),
|
||||||
|
@ -413,15 +409,11 @@ pub enum TRecord<'a> {
|
||||||
pub enum TTagUnion<'a> {
|
pub enum TTagUnion<'a> {
|
||||||
End(Row, Col),
|
End(Row, Col),
|
||||||
Open(Row, Col),
|
Open(Row, Col),
|
||||||
///
|
|
||||||
Type(&'a Type<'a>, Row, Col),
|
Type(&'a Type<'a>, Row, Col),
|
||||||
|
|
||||||
// TODO REMOVE in favor of Type
|
|
||||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
|
||||||
|
|
||||||
///
|
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
///
|
|
||||||
IndentOpen(Row, Col),
|
IndentOpen(Row, Col),
|
||||||
IndentEnd(Row, Col),
|
IndentEnd(Row, Col),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::ast::{AssignedField, CommentOrNewline, Tag, TypeAnnotation};
|
use crate::ast::{AssignedField, CommentOrNewline, Tag, TypeAnnotation};
|
||||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||||
use crate::expr::{global_tag, private_tag};
|
|
||||||
use crate::ident::join_module_parts;
|
use crate::ident::join_module_parts;
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, backtrackable, not_e, optional, peek_utf8_char_e, specialize, specialize_ref, word1,
|
allocated, backtrackable, not_e, optional, peek_utf8_char_e, specialize, specialize_ref, word1,
|
||||||
word2, BadInputError, Either, ParseResult, Parser,
|
word2, BadInputError, ParseResult, Parser,
|
||||||
Progress::{self, *},
|
Progress::{self, *},
|
||||||
State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type,
|
State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type,
|
||||||
};
|
};
|
||||||
|
@ -36,11 +35,9 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TT
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
// This could be an open tag union, e.g. `[ Foo, Bar ]a`
|
// This could be an open tag union, e.g. `[ Foo, Bar ]a`
|
||||||
let (_, ext, state) = optional(allocated(specialize_ref(
|
let (_, ext, state) =
|
||||||
TTagUnion::Syntax,
|
optional(allocated(specialize_ref(TTagUnion::Type, term(min_indent))))
|
||||||
term(min_indent),
|
.parse(arena, state)?;
|
||||||
)))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
let result = TypeAnnotation::TagUnion {
|
let result = TypeAnnotation::TagUnion {
|
||||||
tags: tags.into_bump_slice(),
|
tags: tags.into_bump_slice(),
|
||||||
|
@ -52,13 +49,7 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
||||||
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError<'a>> {
|
|
||||||
specialize(|x, _, _| SyntaxError::Type(x), term_help(min_indent))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
fn term_help<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
and!(
|
and!(
|
||||||
one_of!(
|
one_of!(
|
||||||
|
@ -78,7 +69,7 @@ fn term_help<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>
|
||||||
crate::parser::keyword_e(keyword::AS, Type::TEnd(0, 0))
|
crate::parser::keyword_e(keyword::AS, Type::TEnd(0, 0))
|
||||||
),
|
),
|
||||||
space0_before_e(
|
space0_before_e(
|
||||||
term_help(min_indent),
|
term(min_indent),
|
||||||
min_indent,
|
min_indent,
|
||||||
Type::TSpace,
|
Type::TSpace,
|
||||||
Type::TAsIndentStart
|
Type::TAsIndentStart
|
||||||
|
@ -116,18 +107,7 @@ fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loc_applied_arg<'a>(
|
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError<'a>> {
|
|
||||||
specialize(
|
|
||||||
|x, _, _| SyntaxError::Type(x),
|
|
||||||
loc_applied_arg_help(min_indent),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_applied_arg_help<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
|
||||||
use crate::ast::Spaceable;
|
use crate::ast::Spaceable;
|
||||||
|
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
|
@ -161,12 +141,6 @@ fn loc_applied_arg_help<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loc_applied_args<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, SyntaxError<'a>> {
|
|
||||||
zero_or_more!(loc_applied_arg(min_indent))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_type_in_parens<'a>(
|
fn loc_type_in_parens<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
||||||
|
@ -187,30 +161,132 @@ fn loc_type_in_parens<'a>(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, TTagUnion<'a>> {
|
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, TTagUnion<'a>> {
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| {
|
||||||
let (_, either_name, state) = specialize_ref(
|
let (_, name, state) = loc!(parse_tag_name(TTagUnion::End)).parse(arena, state)?;
|
||||||
TTagUnion::Syntax,
|
|
||||||
either!(loc!(private_tag()), loc!(global_tag())),
|
|
||||||
)
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
let (_, args, state) =
|
let (_, args, state) =
|
||||||
specialize_ref(TTagUnion::Syntax, loc_applied_args(min_indent)).parse(arena, state)?;
|
specialize_ref(TTagUnion::Type, loc_applied_args_e(min_indent)).parse(arena, state)?;
|
||||||
|
|
||||||
let result = match either_name {
|
let result = if name.value.starts_with('@') {
|
||||||
Either::First(name) => Tag::Private {
|
Tag::Private {
|
||||||
name,
|
name,
|
||||||
args: args.into_bump_slice(),
|
args: args.into_bump_slice(),
|
||||||
},
|
}
|
||||||
Either::Second(name) => Tag::Global {
|
} else {
|
||||||
|
Tag::Global {
|
||||||
name,
|
name,
|
||||||
args: args.into_bump_slice(),
|
args: args.into_bump_slice(),
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((MadeProgress, result, state))
|
Ok((MadeProgress, result, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::parser::{Col, Row};
|
||||||
|
fn parse_tag_name<'a, F, E>(to_problem: F) -> impl Parser<'a, &'a str, E>
|
||||||
|
where
|
||||||
|
F: Fn(Row, Col) -> E,
|
||||||
|
E: 'a,
|
||||||
|
{
|
||||||
|
use encode_unicode::CharExt;
|
||||||
|
|
||||||
|
move |arena, mut state: State<'a>| {
|
||||||
|
let mut buf;
|
||||||
|
|
||||||
|
match char::from_utf8_slice_start(state.bytes) {
|
||||||
|
Ok((first_letter, bytes_parsed)) => match first_letter {
|
||||||
|
'@' => {
|
||||||
|
debug_assert_eq!(bytes_parsed, 1);
|
||||||
|
|
||||||
|
// parsing a private tag name
|
||||||
|
match char::from_utf8_slice_start(&state.bytes[1..]) {
|
||||||
|
Ok((second_letter, bytes_parsed_2)) if second_letter.is_uppercase() => {
|
||||||
|
let total_parsed = bytes_parsed + bytes_parsed_2;
|
||||||
|
|
||||||
|
buf = String::with_capacity_in(total_parsed, arena);
|
||||||
|
|
||||||
|
buf.push('@');
|
||||||
|
buf.push(second_letter);
|
||||||
|
|
||||||
|
state = state
|
||||||
|
.advance_without_indenting(arena, total_parsed)
|
||||||
|
.map_err(|(progress, _, state)| {
|
||||||
|
(progress, to_problem(state.line, state.column), state)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// important for error messages
|
||||||
|
state = state
|
||||||
|
.advance_without_indenting(arena, bytes_parsed)
|
||||||
|
.map_err(|(progress, _, state)| {
|
||||||
|
(progress, to_problem(state.line, state.column), state)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let row = state.line;
|
||||||
|
let col = state.column;
|
||||||
|
return state.fail(arena, MadeProgress, to_problem(row, col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ if first_letter.is_uppercase() => {
|
||||||
|
buf = String::with_capacity_in(1, arena);
|
||||||
|
|
||||||
|
buf.push(first_letter);
|
||||||
|
|
||||||
|
state = state
|
||||||
|
.advance_without_indenting(arena, bytes_parsed)
|
||||||
|
.map_err(|(progress, _, state)| {
|
||||||
|
(progress, to_problem(state.line, state.column), state)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let row = state.line;
|
||||||
|
let col = state.column;
|
||||||
|
return state.fail(arena, NoProgress, to_problem(row, col));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
let row = state.line;
|
||||||
|
let col = state.column;
|
||||||
|
return state.fail(arena, NoProgress, to_problem(row, col));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while !state.bytes.is_empty() {
|
||||||
|
match char::from_utf8_slice_start(state.bytes) {
|
||||||
|
Ok((ch, bytes_parsed)) => {
|
||||||
|
// After the first character, only these are allowed:
|
||||||
|
//
|
||||||
|
// * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers
|
||||||
|
// * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric()
|
||||||
|
// * A ':' indicating the end of the field
|
||||||
|
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||||
|
buf.push(ch);
|
||||||
|
|
||||||
|
state = state
|
||||||
|
.advance_without_indenting(arena, bytes_parsed)
|
||||||
|
.map_err(|(progress, _, state)| {
|
||||||
|
(progress, to_problem(state.line, state.column), state)
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
// This is the end of the field. We're done!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let row = state.line;
|
||||||
|
let col = state.column;
|
||||||
|
return state.fail(arena, MadeProgress, to_problem(row, col));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((MadeProgress, buf.into_bump_str(), state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn record_type_field<'a>(
|
fn record_type_field<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, TRecord<'a>> {
|
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, TRecord<'a>> {
|
||||||
|
@ -231,7 +307,7 @@ fn record_type_field<'a>(
|
||||||
debug_assert_eq!(progress, MadeProgress);
|
debug_assert_eq!(progress, MadeProgress);
|
||||||
|
|
||||||
let (_, spaces, state) =
|
let (_, spaces, state) =
|
||||||
debug!(space0_e(min_indent, TRecord::Space, TRecord::IndentEnd)).parse(arena, state)?;
|
space0_e(min_indent, TRecord::Space, TRecord::IndentEnd).parse(arena, state)?;
|
||||||
|
|
||||||
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
||||||
// (This is true in both literals and types.)
|
// (This is true in both literals and types.)
|
||||||
|
@ -241,7 +317,7 @@ fn record_type_field<'a>(
|
||||||
))
|
))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
let val_parser = specialize_ref(TRecord::Syntax, term(min_indent));
|
let val_parser = specialize_ref(TRecord::Type, term(min_indent));
|
||||||
|
|
||||||
match opt_loc_val {
|
match opt_loc_val {
|
||||||
Some(First(_)) => {
|
Some(First(_)) => {
|
||||||
|
@ -289,11 +365,6 @@ fn record_type_field<'a>(
|
||||||
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TRecord<'a>> {
|
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TRecord<'a>> {
|
||||||
use crate::type_annotation::TypeAnnotation::*;
|
use crate::type_annotation::TypeAnnotation::*;
|
||||||
|
|
||||||
let field_term = move |a, s| match term(min_indent).parse(a, s) {
|
|
||||||
Ok(t) => Ok(t),
|
|
||||||
Err((p, error, s)) => Err((p, TRecord::Syntax(a.alloc(error), s.line, s.column), s)),
|
|
||||||
};
|
|
||||||
|
|
||||||
move |arena, state| {
|
move |arena, state| {
|
||||||
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
|
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
|
||||||
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
|
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
|
||||||
|
@ -309,6 +380,7 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TReco
|
||||||
)
|
)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
let field_term = specialize_ref(TRecord::Type, term(min_indent));
|
||||||
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
|
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
|
||||||
|
|
||||||
let result = Record {
|
let result = Record {
|
||||||
|
@ -349,13 +421,13 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, Type
|
||||||
fn loc_applied_args_e<'a>(
|
fn loc_applied_args_e<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, Type<'a>> {
|
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, Type<'a>> {
|
||||||
zero_or_more!(loc_applied_arg_help(min_indent))
|
zero_or_more!(loc_applied_arg(min_indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, Type<'a>> {
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| {
|
||||||
let (p1, first, state) = space0_before_e(
|
let (p1, first, state) = space0_before_e(
|
||||||
term_help(min_indent),
|
term(min_indent),
|
||||||
min_indent,
|
min_indent,
|
||||||
Type::TSpace,
|
Type::TSpace,
|
||||||
Type::TIndentStart,
|
Type::TIndentStart,
|
||||||
|
@ -366,7 +438,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
||||||
word1(b',', Type::TFunctionArgument),
|
word1(b',', Type::TFunctionArgument),
|
||||||
one_of![
|
one_of![
|
||||||
space0_around_e(
|
space0_around_e(
|
||||||
term_help(min_indent),
|
term(min_indent),
|
||||||
min_indent,
|
min_indent,
|
||||||
Type::TSpace,
|
Type::TSpace,
|
||||||
Type::TIndentStart
|
Type::TIndentStart
|
||||||
|
@ -390,7 +462,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
||||||
|
|
||||||
if is_function.is_some() {
|
if is_function.is_some() {
|
||||||
let (p4, return_type, state) = space0_before_e(
|
let (p4, return_type, state) = space0_before_e(
|
||||||
term_help(min_indent),
|
term(min_indent),
|
||||||
min_indent,
|
min_indent,
|
||||||
Type::TSpace,
|
Type::TSpace,
|
||||||
Type::TIndentStart,
|
Type::TIndentStart,
|
||||||
|
|
|
@ -31,7 +31,7 @@ fn note_for_tag_union_type_indent<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocB
|
||||||
fn hint_for_tag_name<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
fn hint_for_tag_name<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.hint("Tag names "),
|
alloc.hint("Tag names "),
|
||||||
alloc.reflow("Tag names start with an uppercase letter, like "),
|
alloc.reflow("start with an uppercase letter, like "),
|
||||||
alloc.parser_suggestion("Err"),
|
alloc.parser_suggestion("Err"),
|
||||||
alloc.text(" or "),
|
alloc.text(" or "),
|
||||||
alloc.parser_suggestion("Green"),
|
alloc.parser_suggestion("Green"),
|
||||||
|
@ -39,6 +39,17 @@ fn hint_for_tag_name<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hint_for_private_tag_name<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.hint("Private tag names "),
|
||||||
|
alloc.reflow("start with a `@` symbol followed by an uppercase letter, like "),
|
||||||
|
alloc.parser_suggestion("@UID"),
|
||||||
|
alloc.text(" or "),
|
||||||
|
alloc.parser_suggestion("@SecretKey"),
|
||||||
|
alloc.text("."),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
fn to_syntax_report<'a>(
|
fn to_syntax_report<'a>(
|
||||||
alloc: &'a RocDocAllocator<'a>,
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
|
@ -372,8 +383,6 @@ fn to_trecord_report<'a>(
|
||||||
|
|
||||||
TRecord::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
|
TRecord::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
|
||||||
|
|
||||||
TRecord::Syntax(error, row, col) => to_syntax_report(alloc, filename, error, row, col),
|
|
||||||
|
|
||||||
TRecord::IndentOpen(row, col) => {
|
TRecord::IndentOpen(row, col) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_row_col(row, col);
|
||||||
|
@ -555,6 +564,22 @@ fn to_ttag_union_report<'a>(
|
||||||
title: "WEIRD TAG NAME".to_string(),
|
title: "WEIRD TAG NAME".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Next::Other(Some('@')) => {
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.reflow(
|
||||||
|
r"I am partway through parsing a tag union type, but I got stuck here:",
|
||||||
|
),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
alloc.reflow(r"I was expecting to see a private tag name."),
|
||||||
|
hint_for_private_tag_name(alloc),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "WEIRD TAG NAME".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc.reflow(r"I am partway through parsing a tag union type, but I got stuck here:"),
|
alloc.reflow(r"I am partway through parsing a tag union type, but I got stuck here:"),
|
||||||
|
@ -579,8 +604,6 @@ fn to_ttag_union_report<'a>(
|
||||||
|
|
||||||
TTagUnion::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
|
TTagUnion::Type(tipe, row, col) => to_type_report(alloc, filename, tipe, row, col),
|
||||||
|
|
||||||
TTagUnion::Syntax(error, row, col) => to_syntax_report(alloc, filename, error, row, col),
|
|
||||||
|
|
||||||
TTagUnion::IndentOpen(row, col) => {
|
TTagUnion::IndentOpen(row, col) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_row_col(row, col);
|
||||||
|
|
|
@ -4173,8 +4173,7 @@ mod test_reporting {
|
||||||
|
|
||||||
I was expecting to see a tag name.
|
I was expecting to see a tag name.
|
||||||
|
|
||||||
Hint: Tag names Tag names start with an uppercase letter, like
|
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||||||
Err or Green.
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -4199,8 +4198,7 @@ mod test_reporting {
|
||||||
|
|
||||||
I was expecting to see a tag name.
|
I was expecting to see a tag name.
|
||||||
|
|
||||||
Hint: Tag names Tag names start with an uppercase letter, like
|
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||||||
Err or Green.
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -4677,13 +4675,12 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn foobar() {
|
fn invalid_private_tag_name() {
|
||||||
// TODO fix error on new row
|
// TODO could do better by pointing out we're parsing a function type
|
||||||
// we should make whitespace only consumed when it puts us in a validly-indented position
|
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
f : I64 ->
|
f : [ @Foo Bool, @100 I64 ]
|
||||||
f = 0
|
f = 0
|
||||||
|
|
||||||
f
|
f
|
||||||
|
@ -4691,14 +4688,17 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── UNFINISHED TYPE ─────────────────────────────────────────────────────────────
|
── WEIRD TAG NAME ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I just started parsing a type, but I got stuck here:
|
I am partway through parsing a tag union type, but I got stuck here:
|
||||||
|
|
||||||
1│ f : I64 ->
|
1│ f : [ @Foo Bool, @100 I64 ]
|
||||||
^
|
^
|
||||||
|
|
||||||
Note: I may be confused by indentation
|
I was expecting to see a private tag name.
|
||||||
|
|
||||||
|
Hint: Private tag names start with a `@` symbol followed by an
|
||||||
|
uppercase letter, like @UID or @SecretKey.
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue