mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-04 00:55:00 +00:00
Fix a bunch of bugs in parsing/formatting found by fuzzing
This commit is contained in:
parent
acd446f6bd
commit
3fee0d3e8f
43 changed files with 593 additions and 70 deletions
|
|
@ -76,8 +76,19 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
|
|
||||||
for var in *vars {
|
for var in *vars {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
||||||
|
let need_parens = matches!(var.value, Pattern::Apply(..));
|
||||||
|
|
||||||
|
if need_parens {
|
||||||
|
buf.push_str("(");
|
||||||
|
}
|
||||||
|
|
||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
|
||||||
|
if need_parens {
|
||||||
|
buf.push_str(")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push_str(" :");
|
buf.push_str(" :");
|
||||||
|
|
|
||||||
|
|
@ -51,25 +51,7 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
|
|
||||||
List(items) => items.iter().any(|loc_expr| loc_expr.is_multiline()),
|
List(items) => items.iter().any(|loc_expr| loc_expr.is_multiline()),
|
||||||
|
|
||||||
Str(literal) => {
|
Str(literal) => is_str_multiline(literal),
|
||||||
use roc_parse::ast::StrLiteral::*;
|
|
||||||
|
|
||||||
match literal {
|
|
||||||
PlainLine(string) => {
|
|
||||||
// When a PlainLine contains '\n' or '"', format as a block string
|
|
||||||
string.contains('"') || string.contains('\n')
|
|
||||||
}
|
|
||||||
Line(_) => {
|
|
||||||
// If this had any newlines, it'd have parsed as Block.
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Block(_) => {
|
|
||||||
// Block strings are always formatted on multiple lines,
|
|
||||||
// even if the string is only a single line.
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Apply(loc_expr, args, _) => {
|
Apply(loc_expr, args, _) => {
|
||||||
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
loc_expr.is_multiline() || args.iter().any(|loc_arg| loc_arg.is_multiline())
|
||||||
}
|
}
|
||||||
|
|
@ -271,8 +253,21 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
indent
|
indent
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let expr_needs_parens =
|
||||||
|
matches!(loc_expr.value.extract_spaces().item, Expr::Closure(..))
|
||||||
|
&& !loc_args.is_empty();
|
||||||
|
|
||||||
|
if expr_needs_parens {
|
||||||
|
buf.push('(');
|
||||||
|
}
|
||||||
|
|
||||||
loc_expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
loc_expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
|
||||||
|
|
||||||
|
if expr_needs_parens {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push(')');
|
||||||
|
}
|
||||||
|
|
||||||
for loc_arg in loc_args.iter() {
|
for loc_arg in loc_args.iter() {
|
||||||
if should_reflow_outdentable {
|
if should_reflow_outdentable {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
@ -432,7 +427,31 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub_expr.format_with_options(buf, Parens::InApply, newlines, indent);
|
let needs_newline = match &sub_expr.value {
|
||||||
|
SpaceBefore(..) => true,
|
||||||
|
Str(text) => is_str_multiline(text),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
let needs_parens =
|
||||||
|
needs_newline && matches!(unary_op.value, called_via::UnaryOp::Negate);
|
||||||
|
|
||||||
|
if needs_parens {
|
||||||
|
// Unary negation can't be followed by whitespace (which is what a newline is) - so
|
||||||
|
// we need to wrap the negated value in parens.
|
||||||
|
buf.push('(');
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner_indent = if needs_parens {
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
|
||||||
|
sub_expr.format_with_options(buf, Parens::InApply, newlines, inner_indent);
|
||||||
|
|
||||||
|
if needs_parens {
|
||||||
|
buf.push(')');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RecordAccessorFunction(key) => {
|
RecordAccessorFunction(key) => {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
|
@ -464,6 +483,26 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_str_multiline(literal: &StrLiteral) -> bool {
|
||||||
|
use roc_parse::ast::StrLiteral::*;
|
||||||
|
|
||||||
|
match literal {
|
||||||
|
PlainLine(string) => {
|
||||||
|
// When a PlainLine contains '\n' or '"', format as a block string
|
||||||
|
string.contains('"') || string.contains('\n')
|
||||||
|
}
|
||||||
|
Line(_) => {
|
||||||
|
// If this had any newlines, it'd have parsed as Block.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Block(_) => {
|
||||||
|
// Block strings are always formatted on multiple lines,
|
||||||
|
// even if the string is only a single line.
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn needs_unicode_escape(ch: char) -> bool {
|
fn needs_unicode_escape(ch: char) -> bool {
|
||||||
matches!(ch, '\u{0000}'..='\u{001f}' | '\u{007f}'..='\u{009f}')
|
matches!(ch, '\u{0000}'..='\u{001f}' | '\u{007f}'..='\u{009f}')
|
||||||
}
|
}
|
||||||
|
|
@ -585,11 +624,11 @@ pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u
|
||||||
buf.ensure_ends_with_newline();
|
buf.ensure_ends_with_newline();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str("\"\"\"");
|
buf.push_str("\"\"\"");
|
||||||
buf.newline();
|
buf.push_newline_literal();
|
||||||
for line in string.split('\n') {
|
for line in string.split('\n') {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str_allow_spaces(line);
|
buf.push_str_allow_spaces(line);
|
||||||
buf.newline();
|
buf.push_newline_literal();
|
||||||
}
|
}
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str("\"\"\"");
|
buf.push_str("\"\"\"");
|
||||||
|
|
@ -613,7 +652,7 @@ pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u
|
||||||
buf.ensure_ends_with_newline();
|
buf.ensure_ends_with_newline();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str("\"\"\"");
|
buf.push_str("\"\"\"");
|
||||||
buf.newline();
|
buf.push_newline_literal();
|
||||||
|
|
||||||
for segments in lines.iter() {
|
for segments in lines.iter() {
|
||||||
for seg in segments.iter() {
|
for seg in segments.iter() {
|
||||||
|
|
@ -622,11 +661,11 @@ pub fn fmt_str_literal<'buf>(buf: &mut Buf<'buf>, literal: StrLiteral, indent: u
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
format_str_segment(seg, buf, indent);
|
format_str_segment(seg, buf, indent);
|
||||||
} else {
|
} else {
|
||||||
buf.newline();
|
buf.push_newline_literal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.newline();
|
buf.push_newline_literal();
|
||||||
}
|
}
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str("\"\"\"");
|
buf.push_str("\"\"\"");
|
||||||
|
|
|
||||||
|
|
@ -106,12 +106,20 @@ impl<'a> Buf<'a> {
|
||||||
self.spaces_to_flush += count;
|
self.spaces_to_flush += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline(&mut self) {
|
/// Only for use in emitting newlines in block strings, which don't follow the rule of
|
||||||
|
/// having at most two newlines in a row.
|
||||||
|
pub fn push_newline_literal(&mut self) {
|
||||||
self.spaces_to_flush = 0;
|
self.spaces_to_flush = 0;
|
||||||
self.newlines_to_flush += 1;
|
self.newlines_to_flush += 1;
|
||||||
self.beginning_of_line = true;
|
self.beginning_of_line = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn newline(&mut self) {
|
||||||
|
self.spaces_to_flush = 0;
|
||||||
|
self.newlines_to_flush = std::cmp::min(self.newlines_to_flush + 1, 2);
|
||||||
|
self.beginning_of_line = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Ensures the current buffer ends in a newline, if it didn't already.
|
/// Ensures the current buffer ends in a newline, if it didn't already.
|
||||||
/// Doesn't add a newline if the buffer already ends in one.
|
/// Doesn't add a newline if the buffer already ends in one.
|
||||||
pub fn ensure_ends_with_newline(&mut self) {
|
pub fn ensure_ends_with_newline(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -745,6 +745,7 @@ fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
||||||
BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()),
|
BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()),
|
||||||
BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()),
|
BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()),
|
||||||
BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()),
|
BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()),
|
||||||
|
BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1106,7 +1106,10 @@ fn finish_parsing_alias_or_opaque<'a>(
|
||||||
let mut defs = Defs::default();
|
let mut defs = Defs::default();
|
||||||
|
|
||||||
let state = match &expr.value.extract_spaces().item {
|
let state = match &expr.value.extract_spaces().item {
|
||||||
Expr::Tag(name) => {
|
Expr::ParensAround(Expr::SpaceBefore(Expr::Tag(name), _))
|
||||||
|
| Expr::ParensAround(Expr::SpaceAfter(Expr::Tag(name), _))
|
||||||
|
| Expr::ParensAround(Expr::Tag(name))
|
||||||
|
| Expr::Tag(name) => {
|
||||||
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
|
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
|
|
@ -1796,20 +1799,47 @@ pub fn loc_expr<'a>(accept_multi_backpassing: bool) -> impl Parser<'a, Loc<Expr<
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn merge_spaces<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
a: &'a [CommentOrNewline<'a>],
|
||||||
|
b: &'a [CommentOrNewline<'a>],
|
||||||
|
) -> &'a [CommentOrNewline<'a>] {
|
||||||
|
if a.is_empty() {
|
||||||
|
b
|
||||||
|
} else if b.is_empty() {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
let mut merged = Vec::with_capacity_in(a.len() + b.len(), arena);
|
||||||
|
merged.extend_from_slice(a);
|
||||||
|
merged.extend_from_slice(b);
|
||||||
|
merged.into_bump_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||||
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
|
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
|
||||||
fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, ()> {
|
fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, ()> {
|
||||||
match expr {
|
let mut expr = expr.extract_spaces();
|
||||||
|
|
||||||
|
if let Expr::ParensAround(loc_expr) = &expr.item {
|
||||||
|
let expr_inner = loc_expr.extract_spaces();
|
||||||
|
|
||||||
|
expr.before = merge_spaces(arena, expr.before, expr_inner.before);
|
||||||
|
expr.after = merge_spaces(arena, expr_inner.after, expr.after);
|
||||||
|
expr.item = expr_inner.item;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pat = match expr.item {
|
||||||
Expr::Var { module_name, ident } => {
|
Expr::Var { module_name, ident } => {
|
||||||
if module_name.is_empty() {
|
if module_name.is_empty() {
|
||||||
Ok(Pattern::Identifier(ident))
|
Pattern::Identifier(ident)
|
||||||
} else {
|
} else {
|
||||||
Ok(Pattern::QualifiedIdentifier { module_name, ident })
|
Pattern::QualifiedIdentifier { module_name, ident }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
|
Expr::Underscore(opt_name) => Pattern::Underscore(opt_name),
|
||||||
Expr::Tag(value) => Ok(Pattern::Tag(value)),
|
Expr::Tag(value) => Pattern::Tag(value),
|
||||||
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
|
Expr::OpaqueRef(value) => Pattern::OpaqueRef(value),
|
||||||
Expr::Apply(loc_val, loc_args, _) => {
|
Expr::Apply(loc_val, loc_args, _) => {
|
||||||
let region = loc_val.region;
|
let region = loc_val.region;
|
||||||
let value = expr_to_pattern_help(arena, &loc_val.value)?;
|
let value = expr_to_pattern_help(arena, &loc_val.value)?;
|
||||||
|
|
@ -1826,19 +1856,10 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
|
|
||||||
let pattern = Pattern::Apply(val_pattern, arg_patterns.into_bump_slice());
|
let pattern = Pattern::Apply(val_pattern, arg_patterns.into_bump_slice());
|
||||||
|
|
||||||
Ok(pattern)
|
pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::SpaceBefore(sub_expr, spaces) => Ok(Pattern::SpaceBefore(
|
Expr::SpaceBefore(..) | Expr::SpaceAfter(..) | Expr::ParensAround(..) => unreachable!(),
|
||||||
arena.alloc(expr_to_pattern_help(arena, sub_expr)?),
|
|
||||||
spaces,
|
|
||||||
)),
|
|
||||||
Expr::SpaceAfter(sub_expr, spaces) => Ok(Pattern::SpaceAfter(
|
|
||||||
arena.alloc(expr_to_pattern_help(arena, sub_expr)?),
|
|
||||||
spaces,
|
|
||||||
)),
|
|
||||||
|
|
||||||
Expr::ParensAround(sub_expr) => expr_to_pattern_help(arena, sub_expr),
|
|
||||||
|
|
||||||
Expr::Record(fields) => {
|
Expr::Record(fields) => {
|
||||||
let patterns = fields.map_items_result(arena, |loc_assigned_field| {
|
let patterns = fields.map_items_result(arena, |loc_assigned_field| {
|
||||||
|
|
@ -1847,30 +1868,27 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
Ok(Loc { region, value })
|
Ok(Loc { region, value })
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Pattern::RecordDestructure(patterns))
|
Pattern::RecordDestructure(patterns)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Tuple(fields) => Ok(Pattern::Tuple(fields.map_items_result(
|
Expr::Tuple(fields) => Pattern::Tuple(fields.map_items_result(arena, |loc_expr| {
|
||||||
arena,
|
Ok(Loc {
|
||||||
|loc_expr| {
|
region: loc_expr.region,
|
||||||
Ok(Loc {
|
value: expr_to_pattern_help(arena, &loc_expr.value)?,
|
||||||
region: loc_expr.region,
|
})
|
||||||
value: expr_to_pattern_help(arena, &loc_expr.value)?,
|
})?),
|
||||||
})
|
|
||||||
},
|
|
||||||
)?)),
|
|
||||||
|
|
||||||
&Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
Expr::Float(string) => Pattern::FloatLiteral(string),
|
||||||
&Expr::Num(string) => Ok(Pattern::NumLiteral(string)),
|
Expr::Num(string) => Pattern::NumLiteral(string),
|
||||||
Expr::NonBase10Int {
|
Expr::NonBase10Int {
|
||||||
string,
|
string,
|
||||||
base,
|
base,
|
||||||
is_negative,
|
is_negative,
|
||||||
} => Ok(Pattern::NonBase10Literal {
|
} => Pattern::NonBase10Literal {
|
||||||
string,
|
string,
|
||||||
base: *base,
|
base,
|
||||||
is_negative: *is_negative,
|
is_negative,
|
||||||
}),
|
},
|
||||||
// These would not have parsed as patterns
|
// These would not have parsed as patterns
|
||||||
Expr::RecordAccessorFunction(_)
|
Expr::RecordAccessorFunction(_)
|
||||||
| Expr::RecordAccess(_, _)
|
| Expr::RecordAccess(_, _)
|
||||||
|
|
@ -1889,12 +1907,23 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
| Expr::PrecedenceConflict { .. }
|
| Expr::PrecedenceConflict { .. }
|
||||||
| Expr::RecordUpdate { .. }
|
| Expr::RecordUpdate { .. }
|
||||||
| Expr::UnaryOp(_, _)
|
| Expr::UnaryOp(_, _)
|
||||||
| Expr::Crash => Err(()),
|
| Expr::Crash => return Err(()),
|
||||||
|
|
||||||
Expr::Str(string) => Ok(Pattern::StrLiteral(*string)),
|
Expr::Str(string) => Pattern::StrLiteral(string),
|
||||||
Expr::SingleQuote(string) => Ok(Pattern::SingleQuote(string)),
|
Expr::SingleQuote(string) => Pattern::SingleQuote(string),
|
||||||
Expr::MalformedIdent(string, problem) => Ok(Pattern::MalformedIdent(string, *problem)),
|
Expr::MalformedIdent(string, problem) => Pattern::MalformedIdent(string, problem),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now we re-add the spaces
|
||||||
|
|
||||||
|
if !expr.before.is_empty() {
|
||||||
|
pat = Pattern::SpaceBefore(arena.alloc(pat), expr.before);
|
||||||
}
|
}
|
||||||
|
if !expr.after.is_empty() {
|
||||||
|
pat = Pattern::SpaceAfter(arena.alloc(pat), expr.after);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(pat)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assigned_expr_field_to_pattern_help<'a>(
|
fn assigned_expr_field_to_pattern_help<'a>(
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ pub enum BadIdent {
|
||||||
WeirdDotQualified(Position),
|
WeirdDotQualified(Position),
|
||||||
StrayDot(Position),
|
StrayDot(Position),
|
||||||
BadOpaqueRef(Position),
|
BadOpaqueRef(Position),
|
||||||
|
QualifiedTupleAccessor(Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_alnum(ch: char) -> bool {
|
fn is_alnum(ch: char) -> bool {
|
||||||
|
|
@ -500,6 +501,13 @@ fn chomp_identifier_chain<'a>(
|
||||||
|
|
||||||
match chomp_access_chain(&buffer[chomped..], &mut parts) {
|
match chomp_access_chain(&buffer[chomped..], &mut parts) {
|
||||||
Ok(width) => {
|
Ok(width) => {
|
||||||
|
if matches!(parts[0], Accessor::TupleIndex(_)) && first_is_uppercase {
|
||||||
|
return Err((
|
||||||
|
chomped as u32,
|
||||||
|
BadIdent::QualifiedTupleAccessor(pos.bump_column(chomped as u32)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
chomped += width as usize;
|
chomped += width as usize;
|
||||||
|
|
||||||
let ident = Ident::Access {
|
let ident = Ident::Access {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
MalformedIdent(
|
||||||
|
"I.5",
|
||||||
|
QualifiedTupleAccessor(
|
||||||
|
@1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
I.5
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
a
|
||||||
|
&& (\x -> x)
|
||||||
|
8
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
@2-4 And,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@5-12 Apply(
|
||||||
|
@5-10 Closure(
|
||||||
|
[
|
||||||
|
@6-7 Identifier(
|
||||||
|
"x",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@9-10 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@11-12 SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"8",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
a && \x->x
|
||||||
|
8
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
i
|
||||||
|
> (\s -> s
|
||||||
|
)
|
||||||
|
-a
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "i",
|
||||||
|
},
|
||||||
|
@1-2 GreaterThan,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@2-10 Apply(
|
||||||
|
@2-7 SpaceAfter(
|
||||||
|
Closure(
|
||||||
|
[
|
||||||
|
@3-4 Identifier(
|
||||||
|
"s",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@6-7 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "s",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@8-10 UnaryOp(
|
||||||
|
@9-10 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
@8-9 Negate,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
i>\s->s
|
||||||
|
-a
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
B : {}
|
||||||
|
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-4,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "B",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @2-4 Record {
|
||||||
|
fields: [],
|
||||||
|
ext: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@6-10 SpaceBefore(
|
||||||
|
ParensAround(
|
||||||
|
SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
B:{}
|
||||||
|
|
||||||
|
(
|
||||||
|
a)
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
!
|
-(
|
||||||
"""
|
"""
|
||||||
"""
|
""")
|
||||||
|
|
@ -4,5 +4,5 @@ UnaryOp(
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@0-1 Not,
|
@0-1 Negate,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
!""""""
|
-""""""
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
-(
|
||||||
|
"""
|
||||||
|
"<
|
||||||
|
""")
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
UnaryOp(
|
||||||
|
@1-9 Str(
|
||||||
|
PlainLine(
|
||||||
|
"\"<",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@0-1 Negate,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
-""""<"""
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
!
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
UnaryOp(
|
||||||
|
@1-7 Str(
|
||||||
|
Block(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@0-1 Not,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
!""""""
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
U (b a) : b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-8,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "U",
|
||||||
|
vars: [
|
||||||
|
@2-5 Apply(
|
||||||
|
@2-3 Identifier(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@4-5 Identifier(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ann: @7-8 BoundVariable(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@9-10 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
U(b a):b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
i #
|
||||||
|
N : b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Apply(
|
||||||
|
@0-1 Identifier(
|
||||||
|
"i",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@5-6 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"N",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@8-9 BoundVariable(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@10-11 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
i
|
||||||
|
(#
|
||||||
|
N):b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
D : b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@1-5,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @1-2 "D",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @4-5 BoundVariable(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@6-7 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
(D):b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
A : b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@2-6,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @2-3 "A",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @5-6 BoundVariable(
|
||||||
|
"b",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@7-8 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
(
|
||||||
|
A):b
|
||||||
|
a
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
""" #
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Str(
|
||||||
|
Block(
|
||||||
|
[
|
||||||
|
[
|
||||||
|
Plaintext(
|
||||||
|
"\n",
|
||||||
|
),
|
||||||
|
Plaintext(
|
||||||
|
"\n",
|
||||||
|
),
|
||||||
|
Plaintext(
|
||||||
|
"#",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
#"""#
|
||||||
|
|
@ -251,6 +251,7 @@ mod test_snapshots {
|
||||||
malformed/malformed_ident_due_to_underscore.expr,
|
malformed/malformed_ident_due_to_underscore.expr,
|
||||||
malformed/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399
|
malformed/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||||
malformed/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399
|
malformed/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399
|
||||||
|
malformed/module_dot_tuple.expr,
|
||||||
malformed/qualified_tag.expr,
|
malformed/qualified_tag.expr,
|
||||||
malformed/underscore_expr_in_def.expr,
|
malformed/underscore_expr_in_def.expr,
|
||||||
pass/ability_demand_signature_is_multiline.expr,
|
pass/ability_demand_signature_is_multiline.expr,
|
||||||
|
|
@ -278,6 +279,8 @@ mod test_snapshots {
|
||||||
pass/basic_var.expr,
|
pass/basic_var.expr,
|
||||||
pass/bound_variable.expr,
|
pass/bound_variable.expr,
|
||||||
pass/call_with_newlines.expr,
|
pass/call_with_newlines.expr,
|
||||||
|
pass/closure_in_binop.expr,
|
||||||
|
pass/closure_in_binop_with_spaces.expr,
|
||||||
pass/closure_with_underscores.expr,
|
pass/closure_with_underscores.expr,
|
||||||
pass/comment_after_annotation.expr,
|
pass/comment_after_annotation.expr,
|
||||||
pass/comment_after_def.moduledefs,
|
pass/comment_after_def.moduledefs,
|
||||||
|
|
@ -306,6 +309,7 @@ mod test_snapshots {
|
||||||
pass/equals_with_spaces.expr,
|
pass/equals_with_spaces.expr,
|
||||||
pass/expect.expr,
|
pass/expect.expr,
|
||||||
pass/expect_fx.moduledefs,
|
pass/expect_fx.moduledefs,
|
||||||
|
pass/extra_newline_in_parens.expr,
|
||||||
pass/float_with_underscores.expr,
|
pass/float_with_underscores.expr,
|
||||||
pass/full_app_header.header,
|
pass/full_app_header.header,
|
||||||
pass/full_app_header_trailing_commas.header,
|
pass/full_app_header_trailing_commas.header,
|
||||||
|
|
@ -343,6 +347,7 @@ mod test_snapshots {
|
||||||
pass/multiple_operators.expr,
|
pass/multiple_operators.expr,
|
||||||
pass/neg_inf_float.expr,
|
pass/neg_inf_float.expr,
|
||||||
pass/negate_multiline_string.expr,
|
pass/negate_multiline_string.expr,
|
||||||
|
pass/negate_multiline_string_with_quote.expr,
|
||||||
pass/negative_float.expr,
|
pass/negative_float.expr,
|
||||||
pass/negative_in_apply_def.expr,
|
pass/negative_in_apply_def.expr,
|
||||||
pass/negative_int.expr,
|
pass/negative_int.expr,
|
||||||
|
|
@ -368,6 +373,7 @@ mod test_snapshots {
|
||||||
pass/nonempty_package_header.header,
|
pass/nonempty_package_header.header,
|
||||||
pass/nonempty_platform_header.header,
|
pass/nonempty_platform_header.header,
|
||||||
pass/not_docs.expr,
|
pass/not_docs.expr,
|
||||||
|
pass/not_multiline_string.expr,
|
||||||
pass/number_literal_suffixes.expr,
|
pass/number_literal_suffixes.expr,
|
||||||
pass/one_backpassing.expr,
|
pass/one_backpassing.expr,
|
||||||
pass/one_char_string.expr,
|
pass/one_char_string.expr,
|
||||||
|
|
@ -389,6 +395,10 @@ mod test_snapshots {
|
||||||
pass/outdented_list.expr,
|
pass/outdented_list.expr,
|
||||||
pass/outdented_record.expr,
|
pass/outdented_record.expr,
|
||||||
pass/packed_singleton_list.expr,
|
pass/packed_singleton_list.expr,
|
||||||
|
pass/parens_in_type_def_apply.expr,
|
||||||
|
pass/parens_in_value_def_annotation.expr,
|
||||||
|
pass/parenthesized_type_def.expr,
|
||||||
|
pass/parenthesized_type_def_space_before.expr,
|
||||||
pass/parenthetical_apply.expr,
|
pass/parenthetical_apply.expr,
|
||||||
pass/parenthetical_basic_field.expr,
|
pass/parenthetical_basic_field.expr,
|
||||||
pass/parenthetical_field_qualified_var.expr,
|
pass/parenthetical_field_qualified_var.expr,
|
||||||
|
|
@ -420,6 +430,7 @@ mod test_snapshots {
|
||||||
pass/spaced_singleton_list.expr,
|
pass/spaced_singleton_list.expr,
|
||||||
pass/spaces_inside_empty_list.expr,
|
pass/spaces_inside_empty_list.expr,
|
||||||
pass/standalone_module_defs.moduledefs,
|
pass/standalone_module_defs.moduledefs,
|
||||||
|
pass/str_block_multiple_newlines.expr,
|
||||||
pass/string_without_escape.expr,
|
pass/string_without_escape.expr,
|
||||||
pass/sub_var_with_spaces.expr,
|
pass/sub_var_with_spaces.expr,
|
||||||
pass/sub_with_spaces.expr,
|
pass/sub_with_spaces.expr,
|
||||||
|
|
|
||||||
|
|
@ -1218,6 +1218,21 @@ fn to_bad_ident_expr_report<'b>(
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
QualifiedTupleAccessor(pos) => {
|
||||||
|
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||||
|
|
||||||
|
alloc.stack([
|
||||||
|
alloc.reflow("I am trying to parse a qualified name here:"),
|
||||||
|
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||||
|
alloc.concat([
|
||||||
|
alloc.reflow("This looks like a tuple accessor on a module or tag name,"),
|
||||||
|
alloc.reflow(r"but neither modules nor tags can have tuple elements! "),
|
||||||
|
alloc.reflow(r"Maybe you wanted a qualified name, something like "),
|
||||||
|
alloc.parser_suggestion("Json.Decode.string"),
|
||||||
|
alloc.text("."),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
QualifiedTag(pos) => {
|
QualifiedTag(pos) => {
|
||||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||||
|
|
||||||
|
|
@ -1358,7 +1373,7 @@ fn to_bad_ident_pattern_report<'b>(
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
WeirdDotQualified(pos) => {
|
QualifiedTupleAccessor(pos) | WeirdDotQualified(pos) => {
|
||||||
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));
|
||||||
|
|
||||||
alloc.stack([
|
alloc.stack([
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue