mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Fix approximately a bajillion fmt and parsing bugs
(discovered by fuzzing) There's more to come, but this seems like a good batch for now.
This commit is contained in:
parent
8f62eeaf7e
commit
0b8e68f70d
68 changed files with 1011 additions and 229 deletions
|
@ -12,7 +12,7 @@ use roc_region::all::Loc;
|
|||
|
||||
/// Does an AST node need parens around it?
|
||||
///
|
||||
/// Usually not, but there are two cases where it may be required
|
||||
/// Usually not, but there are a few cases where it may be required
|
||||
///
|
||||
/// 1. In a function type, function types are in parens
|
||||
///
|
||||
|
@ -25,11 +25,19 @@ use roc_region::all::Loc;
|
|||
/// Just (Just a)
|
||||
/// List (List a)
|
||||
/// reverse (reverse l)
|
||||
///
|
||||
/// 3. In a chain of binary operators, things like nested defs require parens.
|
||||
///
|
||||
/// a + (
|
||||
/// x = 3
|
||||
/// x + 1
|
||||
/// )
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Parens {
|
||||
NotNeeded,
|
||||
InFunctionType,
|
||||
InApply,
|
||||
InOperator,
|
||||
}
|
||||
|
||||
/// In an AST node, do we show newlines around it
|
||||
|
@ -263,7 +271,6 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
|||
}
|
||||
Apply(pkg, name, arguments) => {
|
||||
buf.indent(indent);
|
||||
// NOTE apply is never multiline
|
||||
let write_parens = parens == Parens::InApply && !arguments.is_empty();
|
||||
|
||||
if write_parens {
|
||||
|
@ -277,11 +284,38 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
|||
|
||||
buf.push_str(name);
|
||||
|
||||
for argument in *arguments {
|
||||
buf.spaces(1);
|
||||
argument
|
||||
.value
|
||||
.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||
let needs_indent = except_last(arguments).any(|a| a.is_multiline())
|
||||
|| arguments
|
||||
.last()
|
||||
.map(|a| {
|
||||
a.is_multiline()
|
||||
&& (!a.extract_spaces().before.is_empty()
|
||||
|| !is_outdentable(&a.value))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let arg_indent = if needs_indent {
|
||||
indent + INDENT
|
||||
} else {
|
||||
indent
|
||||
};
|
||||
|
||||
for arg in arguments.iter() {
|
||||
if needs_indent {
|
||||
let arg = arg.extract_spaces();
|
||||
fmt_spaces(buf, arg.before.iter(), arg_indent);
|
||||
buf.ensure_ends_with_newline();
|
||||
arg.item.format_with_options(
|
||||
buf,
|
||||
Parens::InApply,
|
||||
Newlines::Yes,
|
||||
arg_indent,
|
||||
);
|
||||
fmt_spaces(buf, arg.after.iter(), arg_indent);
|
||||
} else {
|
||||
buf.spaces(1);
|
||||
arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
||||
}
|
||||
}
|
||||
|
||||
if write_parens {
|
||||
|
@ -372,6 +406,13 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_outdentable(ann: &TypeAnnotation) -> bool {
|
||||
matches!(
|
||||
ann.extract_spaces().item,
|
||||
TypeAnnotation::Tuple { .. } | TypeAnnotation::Record { .. }
|
||||
)
|
||||
}
|
||||
|
||||
/// Fields are subtly different on the type and term level:
|
||||
///
|
||||
/// > type: { x : Int, y : Bool }
|
||||
|
@ -691,3 +732,11 @@ impl<'a> Formattable for HasAbilities<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn except_last<T>(items: &[T]) -> impl Iterator<Item = &T> {
|
||||
if items.is_empty() {
|
||||
items.iter()
|
||||
} else {
|
||||
items[..items.len() - 1].iter()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue