mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-25 12:59:11 +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,
|
||||
buf: &mut Buf<'buf>,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
use roc_parse::ast::TypeDef::*;
|
||||
|
@ -97,22 +97,10 @@ impl<'a> Formattable for TypeDef<'a> {
|
|||
ann.format(buf, indent)
|
||||
}
|
||||
Opaque {
|
||||
header: TypeHeader { name, vars },
|
||||
header,
|
||||
typ: ann,
|
||||
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 =
|
||||
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;
|
||||
|
||||
ann.format(buf, indent);
|
||||
fmt_general_def(header, buf, indent, ":=", &ann.value, newlines);
|
||||
|
||||
if let Some(has_abilities) = has_abilities {
|
||||
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> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
use roc_parse::ast::ValueDef::*;
|
||||
|
@ -204,7 +215,14 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
use roc_parse::ast::ValueDef::*;
|
||||
match self {
|
||||
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) => {
|
||||
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
||||
|
@ -221,7 +239,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
body_pattern,
|
||||
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 {
|
||||
buf.push_str(" #");
|
||||
|
@ -236,54 +254,63 @@ impl<'a> Formattable for ValueDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn fmt_annotation(
|
||||
loc_pattern: &Loc<Pattern>,
|
||||
fn fmt_general_def<L: Formattable>(
|
||||
lhs: L,
|
||||
buf: &mut Buf,
|
||||
indent: u16,
|
||||
loc_annotation: &Loc<TypeAnnotation>,
|
||||
sep: &str,
|
||||
rhs: &TypeAnnotation,
|
||||
newlines: Newlines,
|
||||
) {
|
||||
loc_pattern.format(buf, indent);
|
||||
lhs.format(buf, indent);
|
||||
buf.indent(indent);
|
||||
|
||||
if loc_annotation.is_multiline() {
|
||||
buf.push_str(" :");
|
||||
if rhs.is_multiline() {
|
||||
buf.spaces(1);
|
||||
buf.push_str(sep);
|
||||
buf.spaces(1);
|
||||
|
||||
let should_outdent = match loc_annotation.value {
|
||||
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,
|
||||
};
|
||||
let should_outdent = should_outdent(rhs);
|
||||
|
||||
if should_outdent {
|
||||
match loc_annotation.value {
|
||||
match rhs {
|
||||
TypeAnnotation::SpaceBefore(sub_def, _) => {
|
||||
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||
}
|
||||
_ => {
|
||||
loc_annotation.format_with_options(
|
||||
buf,
|
||||
Parens::NotNeeded,
|
||||
Newlines::No,
|
||||
indent,
|
||||
);
|
||||
rhs.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loc_annotation.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
||||
rhs.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
||||
}
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
buf.push(':');
|
||||
buf.push_str(sep);
|
||||
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::def::fmt_defs;
|
||||
use crate::pattern::fmt_pattern;
|
||||
|
@ -49,7 +49,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
// These expressions always have newlines
|
||||
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),
|
||||
Apply(loc_expr, args, _) => {
|
||||
|
@ -96,9 +96,9 @@ impl<'a> Formattable for Expr<'a> {
|
|||
.any(|loc_pattern| loc_pattern.is_multiline())
|
||||
}
|
||||
|
||||
Record(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
Tuple(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
RecordUpdate { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
Record(fields) => is_collection_multiline(fields),
|
||||
Tuple(fields) => is_collection_multiline(fields),
|
||||
RecordUpdate { fields, .. } => is_collection_multiline(fields),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1319,7 +1319,7 @@ fn fmt_record<'a, 'buf>(
|
|||
let loc_fields = fields.items;
|
||||
let final_comments = fields.final_comments();
|
||||
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("{}");
|
||||
} else {
|
||||
buf.push('{');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue