mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-23 06:25:10 +00:00
Fix several fmt+parse bugs found by fuzzing
This commit is contained in:
parent
f0a74636a0
commit
b5f284cd78
14 changed files with 297 additions and 151 deletions
|
@ -61,7 +61,7 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
&self,
|
&self,
|
||||||
buf: &mut Buf<'buf>,
|
buf: &mut Buf<'buf>,
|
||||||
_parens: Parens,
|
_parens: Parens,
|
||||||
_newlines: Newlines,
|
newlines: Newlines,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
use roc_parse::ast::TypeDef::*;
|
use roc_parse::ast::TypeDef::*;
|
||||||
|
@ -97,22 +97,10 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
ann.format(buf, indent)
|
ann.format(buf, indent)
|
||||||
}
|
}
|
||||||
Opaque {
|
Opaque {
|
||||||
header: TypeHeader { name, vars },
|
header,
|
||||||
typ: ann,
|
typ: ann,
|
||||||
derived: has_abilities,
|
derived: has_abilities,
|
||||||
} => {
|
} => {
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str(name.value);
|
|
||||||
|
|
||||||
for var in *vars {
|
|
||||||
buf.spaces(1);
|
|
||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
|
||||||
buf.indent(indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(" :=");
|
|
||||||
buf.spaces(1);
|
|
||||||
|
|
||||||
let ann_is_where_clause =
|
let ann_is_where_clause =
|
||||||
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
|
matches!(ann.extract_spaces().item, TypeAnnotation::Where(..));
|
||||||
|
|
||||||
|
@ -126,7 +114,7 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
|
|
||||||
let make_multiline = ann.is_multiline() || has_abilities_multiline;
|
let make_multiline = ann.is_multiline() || has_abilities_multiline;
|
||||||
|
|
||||||
ann.format(buf, indent);
|
fmt_general_def(header, buf, indent, ":=", &ann.value, newlines);
|
||||||
|
|
||||||
if let Some(has_abilities) = has_abilities {
|
if let Some(has_abilities) = has_abilities {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
@ -178,6 +166,29 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Formattable for TypeHeader<'a> {
|
||||||
|
fn is_multiline(&self) -> bool {
|
||||||
|
self.vars.iter().any(|v| v.is_multiline())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_with_options<'buf>(
|
||||||
|
&self,
|
||||||
|
buf: &mut Buf<'buf>,
|
||||||
|
_parens: Parens,
|
||||||
|
_newlines: Newlines,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(self.name.value);
|
||||||
|
|
||||||
|
for var in self.vars.iter() {
|
||||||
|
buf.spaces(1);
|
||||||
|
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
||||||
|
buf.indent(indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Formattable for ValueDef<'a> {
|
impl<'a> Formattable for ValueDef<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use roc_parse::ast::ValueDef::*;
|
use roc_parse::ast::ValueDef::*;
|
||||||
|
@ -204,7 +215,14 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
use roc_parse::ast::ValueDef::*;
|
use roc_parse::ast::ValueDef::*;
|
||||||
match self {
|
match self {
|
||||||
Annotation(loc_pattern, loc_annotation) => {
|
Annotation(loc_pattern, loc_annotation) => {
|
||||||
fmt_annotation(loc_pattern, buf, indent, loc_annotation, newlines);
|
fmt_general_def(
|
||||||
|
loc_pattern,
|
||||||
|
buf,
|
||||||
|
indent,
|
||||||
|
":",
|
||||||
|
&loc_annotation.value,
|
||||||
|
newlines,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Body(loc_pattern, loc_expr) => {
|
Body(loc_pattern, loc_expr) => {
|
||||||
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
||||||
|
@ -221,7 +239,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
body_pattern,
|
body_pattern,
|
||||||
body_expr,
|
body_expr,
|
||||||
} => {
|
} => {
|
||||||
fmt_annotation(ann_pattern, buf, indent, ann_type, newlines);
|
fmt_general_def(ann_pattern, buf, indent, ":", &ann_type.value, newlines);
|
||||||
|
|
||||||
if let Some(comment_str) = comment {
|
if let Some(comment_str) = comment {
|
||||||
buf.push_str(" #");
|
buf.push_str(" #");
|
||||||
|
@ -236,54 +254,63 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_annotation(
|
fn fmt_general_def<L: Formattable>(
|
||||||
loc_pattern: &Loc<Pattern>,
|
lhs: L,
|
||||||
buf: &mut Buf,
|
buf: &mut Buf,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
loc_annotation: &Loc<TypeAnnotation>,
|
sep: &str,
|
||||||
|
rhs: &TypeAnnotation,
|
||||||
newlines: Newlines,
|
newlines: Newlines,
|
||||||
) {
|
) {
|
||||||
loc_pattern.format(buf, indent);
|
lhs.format(buf, indent);
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
|
||||||
if loc_annotation.is_multiline() {
|
if rhs.is_multiline() {
|
||||||
buf.push_str(" :");
|
buf.spaces(1);
|
||||||
|
buf.push_str(sep);
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
||||||
let should_outdent = match loc_annotation.value {
|
let should_outdent = should_outdent(rhs);
|
||||||
TypeAnnotation::SpaceBefore(sub_def, spaces) => match sub_def {
|
|
||||||
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => {
|
|
||||||
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
|
||||||
is_only_newlines && sub_def.is_multiline()
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_outdent {
|
if should_outdent {
|
||||||
match loc_annotation.value {
|
match rhs {
|
||||||
TypeAnnotation::SpaceBefore(sub_def, _) => {
|
TypeAnnotation::SpaceBefore(sub_def, _) => {
|
||||||
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
loc_annotation.format_with_options(
|
rhs.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
buf,
|
|
||||||
Parens::NotNeeded,
|
|
||||||
Newlines::No,
|
|
||||||
indent,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loc_annotation.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
rhs.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
buf.push(':');
|
buf.push_str(sep);
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
loc_annotation.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
rhs.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_outdent(mut rhs: &TypeAnnotation) -> bool {
|
||||||
|
loop {
|
||||||
|
match rhs {
|
||||||
|
TypeAnnotation::SpaceBefore(sub_def, spaces) => {
|
||||||
|
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||||
|
if !is_only_newlines || !sub_def.is_multiline() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rhs = sub_def;
|
||||||
|
}
|
||||||
|
TypeAnnotation::Where(ann, _clauses) => {
|
||||||
|
if !ann.is_multiline() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rhs = &ann.value;
|
||||||
|
}
|
||||||
|
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => return true,
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::annotation::{except_last, Formattable, Newlines, Parens};
|
use crate::annotation::{except_last, is_collection_multiline, Formattable, Newlines, Parens};
|
||||||
use crate::collection::{fmt_collection, Braces};
|
use crate::collection::{fmt_collection, Braces};
|
||||||
use crate::def::fmt_defs;
|
use crate::def::fmt_defs;
|
||||||
use crate::pattern::fmt_pattern;
|
use crate::pattern::fmt_pattern;
|
||||||
|
@ -49,7 +49,7 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
// These expressions always have newlines
|
// These expressions always have newlines
|
||||||
Defs(_, _) | When(_, _) => true,
|
Defs(_, _) | When(_, _) => true,
|
||||||
|
|
||||||
List(items) => items.iter().any(|loc_expr| loc_expr.is_multiline()),
|
List(items) => is_collection_multiline(items),
|
||||||
|
|
||||||
Str(literal) => is_str_multiline(literal),
|
Str(literal) => is_str_multiline(literal),
|
||||||
Apply(loc_expr, args, _) => {
|
Apply(loc_expr, args, _) => {
|
||||||
|
@ -96,9 +96,9 @@ impl<'a> Formattable for Expr<'a> {
|
||||||
.any(|loc_pattern| loc_pattern.is_multiline())
|
.any(|loc_pattern| loc_pattern.is_multiline())
|
||||||
}
|
}
|
||||||
|
|
||||||
Record(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
Record(fields) => is_collection_multiline(fields),
|
||||||
Tuple(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
Tuple(fields) => is_collection_multiline(fields),
|
||||||
RecordUpdate { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
RecordUpdate { fields, .. } => is_collection_multiline(fields),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1319,7 +1319,7 @@ fn fmt_record<'a, 'buf>(
|
||||||
let loc_fields = fields.items;
|
let loc_fields = fields.items;
|
||||||
let final_comments = fields.final_comments();
|
let final_comments = fields.final_comments();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) {
|
if loc_fields.is_empty() && final_comments.iter().all(|c| c.is_newline()) && update.is_none() {
|
||||||
buf.push_str("{}");
|
buf.push_str("{}");
|
||||||
} else {
|
} else {
|
||||||
buf.push('{');
|
buf.push('{');
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Has, HasAbilities,
|
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Has, HasAbilities,
|
||||||
Pattern, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
Pattern, Spaceable, Spaces, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||||
};
|
};
|
||||||
use crate::blankspace::{
|
use crate::blankspace::{
|
||||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||||
|
@ -1085,6 +1085,29 @@ enum AliasOrOpaque {
|
||||||
Opaque,
|
Opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option<Spaces<'a, &'a str>> {
|
||||||
|
let mut expr = expr.extract_spaces();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match &expr.item {
|
||||||
|
Expr::ParensAround(inner_expr) => {
|
||||||
|
let inner_expr = inner_expr.extract_spaces();
|
||||||
|
expr.item = inner_expr.item;
|
||||||
|
expr.before = merge_spaces(arena, expr.before, inner_expr.before);
|
||||||
|
expr.after = merge_spaces(arena, inner_expr.after, expr.after);
|
||||||
|
}
|
||||||
|
Expr::Tag(tag) => {
|
||||||
|
return Some(Spaces {
|
||||||
|
before: expr.before,
|
||||||
|
item: tag,
|
||||||
|
after: expr.after,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn finish_parsing_alias_or_opaque<'a>(
|
fn finish_parsing_alias_or_opaque<'a>(
|
||||||
min_indent: u32,
|
min_indent: u32,
|
||||||
|
@ -1105,120 +1128,113 @@ 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 = if let Some(tag) = extract_tag_and_spaces(arena, expr.value) {
|
||||||
Expr::ParensAround(Expr::SpaceBefore(Expr::Tag(name), _))
|
let name = tag.item;
|
||||||
| Expr::ParensAround(Expr::SpaceAfter(Expr::Tag(name), _))
|
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
|
||||||
| Expr::ParensAround(Expr::Tag(name))
|
|
||||||
| Expr::Tag(name) => {
|
|
||||||
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
|
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
match expr_to_pattern_help(arena, &argument.value) {
|
match expr_to_pattern_help(arena, &argument.value) {
|
||||||
Ok(good) => {
|
Ok(good) => {
|
||||||
type_arguments.push(Loc::at(argument.region, good));
|
type_arguments.push(Loc::at(argument.region, good));
|
||||||
}
|
|
||||||
Err(()) => {
|
|
||||||
return Err((
|
|
||||||
MadeProgress,
|
|
||||||
EExpr::Pattern(
|
|
||||||
arena.alloc(EPattern::NotAPattern(state.pos())),
|
|
||||||
state.pos(),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
Err(()) => {
|
||||||
|
return Err((
|
||||||
match kind {
|
MadeProgress,
|
||||||
AliasOrOpaque::Alias => {
|
EExpr::Pattern(
|
||||||
let (_, signature, state) =
|
arena.alloc(EPattern::NotAPattern(state.pos())),
|
||||||
alias_signature_with_space_before().parse(arena, state, min_indent)?;
|
state.pos(),
|
||||||
|
),
|
||||||
let def_region = Region::span_across(&expr.region, &signature.region);
|
));
|
||||||
|
|
||||||
let header = TypeHeader {
|
|
||||||
name: Loc::at(expr.region, name),
|
|
||||||
vars: type_arguments.into_bump_slice(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let def = TypeDef::Alias {
|
|
||||||
header,
|
|
||||||
ann: signature,
|
|
||||||
};
|
|
||||||
|
|
||||||
defs.push_type_def(def, def_region, &[], &[]);
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
AliasOrOpaque::Opaque => {
|
|
||||||
let (_, (signature, derived), state) =
|
|
||||||
opaque_signature_with_space_before().parse(arena, state, indented_more)?;
|
|
||||||
|
|
||||||
let def_region = Region::span_across(&expr.region, &signature.region);
|
|
||||||
|
|
||||||
let header = TypeHeader {
|
|
||||||
name: Loc::at(expr.region, name),
|
|
||||||
vars: type_arguments.into_bump_slice(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let def = TypeDef::Opaque {
|
|
||||||
header,
|
|
||||||
typ: signature,
|
|
||||||
derived,
|
|
||||||
};
|
|
||||||
|
|
||||||
defs.push_type_def(def, def_region, &[], &[]);
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
match kind {
|
||||||
let call = to_call(arena, arguments, expr);
|
AliasOrOpaque::Alias => {
|
||||||
|
let (_, signature, state) =
|
||||||
|
alias_signature_with_space_before().parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
match expr_to_pattern_help(arena, &call.value) {
|
let def_region = Region::span_across(&expr.region, &signature.region);
|
||||||
Ok(good) => {
|
|
||||||
let parser = specialize(
|
|
||||||
EExpr::Type,
|
|
||||||
space0_before_e(
|
|
||||||
set_min_indent(indented_more, type_annotation::located(false)),
|
|
||||||
EType::TIndentStart,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
match parser.parse(arena, state.clone(), min_indent) {
|
let header = TypeHeader {
|
||||||
Err((_, fail)) => return Err((MadeProgress, fail)),
|
name: Loc::at(expr.region, name),
|
||||||
Ok((_, mut ann_type, state)) => {
|
vars: type_arguments.into_bump_slice(),
|
||||||
// put the spaces from after the operator in front of the call
|
};
|
||||||
if !spaces_after_operator.is_empty() {
|
|
||||||
ann_type = arena
|
|
||||||
.alloc(ann_type.value)
|
|
||||||
.with_spaces_before(spaces_after_operator, ann_type.region);
|
|
||||||
}
|
|
||||||
|
|
||||||
let def_region = Region::span_across(&call.region, &ann_type.region);
|
let def = TypeDef::Alias {
|
||||||
|
header,
|
||||||
|
ann: signature,
|
||||||
|
};
|
||||||
|
|
||||||
let value_def =
|
defs.push_type_def(def, def_region, &[], &[]);
|
||||||
ValueDef::Annotation(Loc::at(expr_region, good), ann_type);
|
|
||||||
|
|
||||||
defs.push_value_def(value_def, def_region, &[], &[]);
|
state
|
||||||
|
}
|
||||||
|
|
||||||
state
|
AliasOrOpaque::Opaque => {
|
||||||
|
let (_, (signature, derived), state) =
|
||||||
|
opaque_signature_with_space_before().parse(arena, state, indented_more)?;
|
||||||
|
|
||||||
|
let def_region = Region::span_across(&expr.region, &signature.region);
|
||||||
|
|
||||||
|
let header = TypeHeader {
|
||||||
|
name: Loc::at(expr.region, name),
|
||||||
|
vars: type_arguments.into_bump_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let def = TypeDef::Opaque {
|
||||||
|
header,
|
||||||
|
typ: signature,
|
||||||
|
derived,
|
||||||
|
};
|
||||||
|
|
||||||
|
defs.push_type_def(def, def_region, &[], &[]);
|
||||||
|
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let call = to_call(arena, arguments, expr);
|
||||||
|
|
||||||
|
match expr_to_pattern_help(arena, &call.value) {
|
||||||
|
Ok(good) => {
|
||||||
|
let parser = specialize(
|
||||||
|
EExpr::Type,
|
||||||
|
space0_before_e(
|
||||||
|
set_min_indent(indented_more, type_annotation::located(false)),
|
||||||
|
EType::TIndentStart,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
match parser.parse(arena, state.clone(), min_indent) {
|
||||||
|
Err((_, fail)) => return Err((MadeProgress, fail)),
|
||||||
|
Ok((_, mut ann_type, state)) => {
|
||||||
|
// put the spaces from after the operator in front of the call
|
||||||
|
if !spaces_after_operator.is_empty() {
|
||||||
|
ann_type = arena
|
||||||
|
.alloc(ann_type.value)
|
||||||
|
.with_spaces_before(spaces_after_operator, ann_type.region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let def_region = Region::span_across(&call.region, &ann_type.region);
|
||||||
|
|
||||||
|
let value_def = ValueDef::Annotation(Loc::at(expr_region, good), ann_type);
|
||||||
|
|
||||||
|
defs.push_value_def(value_def, def_region, &[], &[]);
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
}
|
||||||
// this `:`/`:=` likely occurred inline; treat it as an invalid operator
|
Err(_) => {
|
||||||
let op = match kind {
|
// this `:`/`:=` likely occurred inline; treat it as an invalid operator
|
||||||
AliasOrOpaque::Alias => ":",
|
let op = match kind {
|
||||||
AliasOrOpaque::Opaque => ":=",
|
AliasOrOpaque::Alias => ":",
|
||||||
};
|
AliasOrOpaque::Opaque => ":=",
|
||||||
let fail = EExpr::BadOperator(op, loc_op.region.start());
|
};
|
||||||
|
let fail = EExpr::BadOperator(op, loc_op.region.start());
|
||||||
|
|
||||||
return Err((MadeProgress, fail));
|
return Err((MadeProgress, fail));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -243,10 +243,11 @@ impl<'a> Input<'a> {
|
||||||
let reformatted = reparsed_ast.format();
|
let reformatted = reparsed_ast.format();
|
||||||
|
|
||||||
if output != reformatted {
|
if output != reformatted {
|
||||||
eprintln!("Formatting bug; formatting is not stable.\nOriginal code:\n{}\n\nFormatted code:\n{}\n\nAST:\n{:#?}\n\n",
|
eprintln!("Formatting bug; formatting is not stable.\nOriginal code:\n{}\n\nFormatted code:\n{}\n\nAST:\n{:#?}\n\nReparsed AST:\n{:#?}\n\n",
|
||||||
self.as_str(),
|
self.as_str(),
|
||||||
output.as_ref().as_str(),
|
output.as_ref().as_str(),
|
||||||
actual);
|
actual,
|
||||||
|
reparsed_ast);
|
||||||
eprintln!("Reformatting the formatted code changed it again, as follows:\n\n");
|
eprintln!("Reformatting the formatted code changed it again, as follows:\n\n");
|
||||||
|
|
||||||
assert_multiline_str_eq!(output.as_ref().as_str(), reformatted.as_ref().as_str());
|
assert_multiline_str_eq!(output.as_ref().as_str(), reformatted.as_ref().as_str());
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{ e & }
|
|
@ -0,0 +1,7 @@
|
||||||
|
RecordUpdate {
|
||||||
|
update: @1-2 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "e",
|
||||||
|
},
|
||||||
|
fields: [],
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
{e&}
|
|
@ -0,0 +1,4 @@
|
||||||
|
[
|
||||||
|
K,
|
||||||
|
]
|
||||||
|
- i
|
|
@ -0,0 +1,23 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-5 List(
|
||||||
|
Collection {
|
||||||
|
items: [
|
||||||
|
@1-2 Tag(
|
||||||
|
"K",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
@5-6 Minus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@6-7 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "i",
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
[K,
|
||||||
|
]-i
|
|
@ -0,0 +1,4 @@
|
||||||
|
a : e
|
||||||
|
Na :=
|
||||||
|
e
|
||||||
|
e0
|
|
@ -0,0 +1,54 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
Index(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-3,
|
||||||
|
@4-11,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 0, length = 1),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 1, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
type_defs: [
|
||||||
|
Opaque {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @4-6 "Na",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
typ: @10-11 SpaceBefore(
|
||||||
|
BoundVariable(
|
||||||
|
"e",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
derived: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
@2-3 BoundVariable(
|
||||||
|
"e",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@12-14 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "e0",
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
a:e
|
||||||
|
Na:=
|
||||||
|
e e0
|
|
@ -304,6 +304,7 @@ mod test_snapshots {
|
||||||
pass/empty_package_header.header,
|
pass/empty_package_header.header,
|
||||||
pass/empty_platform_header.header,
|
pass/empty_platform_header.header,
|
||||||
pass/empty_record.expr,
|
pass/empty_record.expr,
|
||||||
|
pass/empty_record_update.expr,
|
||||||
pass/empty_string.expr,
|
pass/empty_string.expr,
|
||||||
pass/equals.expr,
|
pass/equals.expr,
|
||||||
pass/equals_with_spaces.expr,
|
pass/equals_with_spaces.expr,
|
||||||
|
@ -327,6 +328,7 @@ mod test_snapshots {
|
||||||
pass/list_closing_indent_not_enough.expr,
|
pass/list_closing_indent_not_enough.expr,
|
||||||
pass/list_closing_same_indent_no_trailing_comma.expr,
|
pass/list_closing_same_indent_no_trailing_comma.expr,
|
||||||
pass/list_closing_same_indent_with_trailing_comma.expr,
|
pass/list_closing_same_indent_with_trailing_comma.expr,
|
||||||
|
pass/list_minus_newlines.expr,
|
||||||
pass/list_pattern_weird_indent.expr,
|
pass/list_pattern_weird_indent.expr,
|
||||||
pass/list_patterns.expr,
|
pass/list_patterns.expr,
|
||||||
pass/lowest_float.expr,
|
pass/lowest_float.expr,
|
||||||
|
@ -389,6 +391,7 @@ mod test_snapshots {
|
||||||
pass/opaque_reference_pattern.expr,
|
pass/opaque_reference_pattern.expr,
|
||||||
pass/opaque_reference_pattern_with_arguments.expr,
|
pass/opaque_reference_pattern_with_arguments.expr,
|
||||||
pass/opaque_simple.moduledefs,
|
pass/opaque_simple.moduledefs,
|
||||||
|
pass/opaque_type_def_with_newline.expr,
|
||||||
pass/opaque_with_type_arguments.moduledefs,
|
pass/opaque_with_type_arguments.moduledefs,
|
||||||
pass/ops_with_newlines.expr,
|
pass/ops_with_newlines.expr,
|
||||||
pass/outdented_app_with_record.expr,
|
pass/outdented_app_with_record.expr,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue