Several fixes found in fuzzing

This commit is contained in:
Joshua Warner 2024-11-29 18:52:54 -08:00
parent e9caada3f1
commit b9862b47dc
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
49 changed files with 1060 additions and 523 deletions

View file

@ -43,6 +43,7 @@ pub enum Parens {
InApply,
InOperator,
InAsPattern,
InApplyLastArg,
}
/// In an AST node, do we show newlines around it
@ -238,6 +239,7 @@ fn fmt_ty_ann(
let is_first = index == 0;
if !is_first {
buf.indent(indent);
buf.push_str(",");
if !self_is_multiline {
buf.spaces(1);

View file

@ -1009,7 +1009,7 @@ pub fn fmt_body<'a>(
return body.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
}
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
pattern.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
buf.indent(indent);
buf.push_str(" =");

File diff suppressed because it is too large Load diff

View file

@ -204,7 +204,6 @@ fn fmt_pattern_inner(
if it.peek().is_some() {
buf.push_str(",");
buf.spaces(1);
}
if !item.after.is_empty() {
@ -218,6 +217,9 @@ fn fmt_pattern_inner(
fmt_spaces(buf, item.after.iter(), indent);
}
}
if it.peek().is_some() {
buf.ensure_ends_with_whitespace();
}
}
buf.spaces(1);
}

View file

@ -294,13 +294,18 @@ fn crash_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
fn loc_possibly_negative_or_negated_term<'a>(
options: ExprParseOptions,
allow_negate: bool,
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
let parse_unary_negate = move |arena, state: State<'a>, min_indent: u32| {
let initial = state.clone();
if !allow_negate {
return Err((NoProgress, EExpr::UnaryNegate(state.pos())));
}
let (_, (loc_op, loc_expr), state) = and(
loc(unary_negate()),
loc_possibly_negative_or_negated_term(options),
loc_possibly_negative_or_negated_term(options, true),
)
.parse(arena, state, min_indent)?;
@ -310,24 +315,26 @@ fn loc_possibly_negative_or_negated_term<'a>(
};
one_of![
parse_unary_negate.trace("d"),
parse_unary_negate.trace("negate_expr"),
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
loc(specialize_err(EExpr::Number, number_literal_help())).trace("c"),
// loc(specialize_err(EExpr::Number, number_literal_help())),
loc(map_with_arena(
and(
loc(byte(b'!', EExpr::Start)),
loc(unary_not()).trace("not"),
space0_before_e(
loc_possibly_negative_or_negated_term(options),
loc_possibly_negative_or_negated_term(options, true),
EExpr::IndentStart
)
.trace("not_expr")
),
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not))
}
))
.trace("b"),
loc_term_or_underscore_or_conditional(options).trace("a")
.trace("not_expr"),
loc_term_or_underscore_or_conditional(options)
]
.trace("loc_possibly_negative_or_negated_term")
}
fn fail_expr_start_e<'a, T: 'a>() -> impl Parser<'a, T, EExpr<'a>> {
@ -340,14 +347,15 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
//
// - it is preceded by whitespace (spaces, newlines, comments)
// - it is not followed by whitespace
let followed_by_whitespace = state
// - it is not followed by >, making ->
let followed_by_illegal_char = state
.bytes()
.get(1)
.map(|c| c.is_ascii_whitespace() || *c == b'#')
.map(|c| c.is_ascii_whitespace() || *c == b'#' || *c == b'>')
.unwrap_or(false);
if state.bytes().starts_with(b"-")
&& !followed_by_whitespace
&& !followed_by_illegal_char
&& state.column() >= min_indent
{
// the negate is only unary if it is not followed by whitespace
@ -360,6 +368,19 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
}
}
fn unary_not<'a>() -> impl Parser<'a, (), EExpr<'a>> {
move |_arena: &'a Bump, state: State<'a>, min_indent: u32| {
let followed_by_equals = state.bytes().get(1).map(|c| *c == b'=').unwrap_or(false);
if state.bytes().starts_with(b"!") && !followed_by_equals && state.column() >= min_indent {
let state = state.advance(1);
Ok((MadeProgress, (), state))
} else {
Err((NoProgress, EExpr::UnaryNot(state.pos())))
}
}
}
/// Entry point for parsing an expression.
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of![
@ -389,7 +410,7 @@ fn parse_expr_operator_chain<'a>(
let line_indent = state.line_indent();
let (_, expr, state) =
loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?;
loc_possibly_negative_or_negated_term(options, true).parse(arena, state, min_indent)?;
let mut initial_state = state.clone();
@ -415,7 +436,8 @@ fn parse_expr_operator_chain<'a>(
let parser = skip_first(
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options),
);
)
.trace("term_or_underscore");
match parser.parse(arena, state.clone(), call_min_indent) {
Err((MadeProgress, f)) => return Err((MadeProgress, f)),
Err((NoProgress, _)) => {
@ -583,7 +605,7 @@ fn parse_stmt_operator_chain<'a>(
let line_indent = state.line_indent();
let (_, expr, state) =
loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?;
loc_possibly_negative_or_negated_term(options, true).parse(arena, state, min_indent)?;
let mut initial_state = state.clone();
let end = state.pos();
@ -594,6 +616,8 @@ fn parse_stmt_operator_chain<'a>(
Ok((_, spaces_before_op, state)) => (spaces_before_op, state),
};
let allow_negate = state.pos() > end;
let mut expr_state = ExprState {
operators: Vec::new_in(arena),
arguments: Vec::new_in(arena),
@ -609,7 +633,7 @@ fn parse_stmt_operator_chain<'a>(
loop {
let parser = skip_first(
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options),
loc_possibly_negative_or_negated_term(options, allow_negate),
);
match parser.parse(arena, state.clone(), call_min_indent) {
Err((MadeProgress, f)) => return Err((MadeProgress, f)),
@ -1566,10 +1590,10 @@ fn parse_after_binop<'a>(
mut expr_state: ExprState<'a>,
loc_op: Loc<BinOp>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
match loc_possibly_negative_or_negated_term(options).parse(
match loc_possibly_negative_or_negated_term(options, true).parse(
arena,
state.clone(),
call_min_indent,
min_indent,
) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, mut new_expr, state)) => {
@ -2596,7 +2620,8 @@ fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
specialize_err_ref(EIf::Condition, loc_expr(true)),
EIf::IndentCondition,
EIf::IndentThenToken,
),
)
.trace("if_condition"),
parser::keyword(keyword::THEN, EIf::Then),
),
map_with_arena(
@ -2614,6 +2639,7 @@ fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
),
parser::keyword(keyword::ELSE, EIf::Else),
)
.trace("if_branch")
}
fn expect_help<'a>(
@ -2702,9 +2728,10 @@ fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
}
fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<'a>> {
move |arena: &'a Bump, state, min_indent| {
let (_, _, state) =
parser::keyword(keyword::IF, EIf::If).parse(arena, state, min_indent)?;
(move |arena: &'a Bump, state, min_indent| {
let (_, _, state) = parser::keyword(keyword::IF, EIf::If)
.trace("if_kw")
.parse(arena, state, min_indent)?;
let if_indent = state.line_indent();
@ -2713,8 +2740,9 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
let mut loop_state = state;
let state_final_else = loop {
let (_, (cond, then_branch), state) =
if_branch().parse(arena, loop_state, min_indent)?;
let (_, (cond, then_branch), state) = if_branch()
.parse(arena, loop_state, min_indent)
.map_err(|(_p, err)| (MadeProgress, err))?;
branches.push((cond, then_branch));
@ -2767,7 +2795,8 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
};
Ok((MadeProgress, expr, state))
}
})
.trace("if")
}
/// Parse a block of statements (parser combinator version of `parse_block`)
@ -3761,26 +3790,6 @@ fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
)
}
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;
match literal {
Num(s) => Expr::Num(s),
Float(s) => Expr::Float(s),
NonBase10Int {
string,
base,
is_negative,
} => Expr::NonBase10Int {
string,
base,
is_negative,
},
}
})
}
const BINOP_CHAR_SET: &[u8] = b"+-/*=.<>:&|^?%!";
const BINOP_CHAR_MASK: [bool; 125] = {

View file

@ -456,7 +456,8 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help()))),
loc(allocated(reset_min_indent(expr::expr_help())))
.trace("str_interpolation"),
),
byte(b')', EString::FormatEnd),
)

View file

@ -1 +1 @@
Expr(When(IfGuard(Start(@28), @27), @0), @0)
Expr(When(IfGuard(Start(@27), @27), @0), @0)

View file

@ -0,0 +1 @@
Expr(If(Condition(Start(@2), @2), @0), @0)

View file

@ -0,0 +1 @@
if!==9

View file

@ -1,4 +1,4 @@
UserId x : [UserId I64]
(UserId x) = UserId 42
UserId x = UserId 42
x

View file

@ -0,0 +1,4 @@
d
+
(\w -> x)
x

View file

@ -0,0 +1,41 @@
BinOps(
[
(
@0-1 Var {
module_name: "",
ident: "d",
},
@1-2 Plus,
),
],
@4-12 Apply(
@4-9 SpaceBefore(
Closure(
[
@5-6 Identifier {
ident: "w",
},
],
@8-9 Var {
module_name: "",
ident: "x",
},
),
[
Newline,
],
),
[
@11-12 SpaceBefore(
Var {
module_name: "",
ident: "x",
},
[
Newline,
],
),
],
Space,
),
)

View file

@ -0,0 +1,3 @@
d+
\w->x
x

View file

@ -0,0 +1,42 @@
BinOps(
[
(
@0-1 Var {
module_name: "",
ident: "r",
},
@1-2 Caret,
),
],
@3-11 Apply(
@3-5 SpaceAfter(
SpaceBefore(
UnaryOp(
@4-5 Var {
module_name: "",
ident: "f",
},
@3-4 Negate,
),
[
Newline,
],
),
[
Newline,
LineComment(
"",
),
],
),
[
@9-11 UnaryOp(
@10-11 Tag(
"P",
),
@9-10 Negate,
),
],
Space,
),
)

View file

@ -0,0 +1,25 @@
Closure(
[
@1-8 RecordDestructure(
[
@2-3 SpaceAfter(
Identifier {
ident: "i",
},
[
LineComment(
"",
),
],
),
@6-7 Identifier {
ident: "e",
},
],
),
],
@10-11 Var {
module_name: "",
ident: "a",
},
)

View file

@ -0,0 +1,74 @@
SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(2147483648),
],
regions: [
@0-16,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [],
value_defs: [
Annotation(
@0-1 NumLiteral(
"1",
),
@2-16 Tuple {
elems: [
@3-14 Function(
[
@3-4 Apply(
"",
"M",
[],
),
@5-6 SpaceAfter(
BoundVariable(
"b",
),
[
LineComment(
",",
),
],
),
@10-11 BoundVariable(
"h",
),
],
Pure,
@13-14 BoundVariable(
"g",
),
),
],
ext: Some(
@15-16 BoundVariable(
"e",
),
),
},
),
],
},
@17-18 SpaceBefore(
Var {
module_name: "",
ident: "h",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,27 @@
ParensAround(
DbgStmt {
first: @5-6 Tag(
"D",
),
extra_args: [
@8-9 SpaceBefore(
Var {
module_name: "",
ident: "q",
},
[
Newline,
],
),
],
continuation: @10-11 SpaceBefore(
Var {
module_name: "",
ident: "h",
},
[
Newline,
],
),
},
)

View file

@ -0,0 +1,3 @@
(dbg D
q
h)

View file

@ -1,2 +1,2 @@
(Email str) = Email "blah@example.com"
Email str = Email "blah@example.com"
str

View file

@ -1,28 +1,33 @@
BinOps(
[
(
@0-1 Var {
module_name: "",
ident: "i",
},
@1-2 Slash,
),
],
@3-7 SpaceBefore(
UnaryOp(
@6-7 SpaceBefore(
Var {
module_name: "",
ident: "g",
},
[
Newline,
],
),
@3-4 Not,
),
SpaceAfter(
BinOps(
[
Newline,
(
@0-1 Var {
module_name: "",
ident: "i",
},
@1-2 Slash,
),
],
@4-8 SpaceBefore(
UnaryOp(
@7-8 SpaceBefore(
Var {
module_name: "",
ident: "g",
},
[
Newline,
],
),
@4-5 Not,
),
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,43 @@
SpaceAfter(
If {
if_thens: [
(
@3-4 SpaceBefore(
SpaceAfter(
Var {
module_name: "",
ident: "h",
},
[
Newline,
],
),
[
Newline,
],
),
@9-11 SpaceAfter(
UnaryOp(
@10-11 Var {
module_name: "",
ident: "f",
},
@9-10 Not,
),
[
LineComment(
"",
),
],
),
),
],
final_else: @17-19 RecordUpdater(
"m",
),
indented_else: false,
},
[
Newline,
],
)

View file

@ -0,0 +1,5 @@
"""
"""
"$(i
"""
""")"

View file

@ -0,0 +1,32 @@
Apply(
@0-6 Str(
Block(
[],
),
),
[
@6-18 Str(
Line(
[
Interpolated(
@9-16 Apply(
@9-10 Var {
module_name: "",
ident: "i",
},
[
@10-16 Str(
Block(
[],
),
),
],
Space,
),
),
],
),
),
],
Space,
)

View file

@ -0,0 +1 @@
"""""""$(i"""""")"

View file

@ -0,0 +1,52 @@
SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(0),
],
regions: [
@0-8,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [
Alias {
header: TypeHeader {
name: @0-1 "N",
vars: [
@2-4 NumLiteral(
"-0",
),
@5-6 Tag(
"T",
),
],
},
ann: @7-8 Apply(
"",
"A",
[],
),
},
],
value_defs: [],
},
@9-11 SpaceBefore(
Var {
module_name: "",
ident: "zT",
},
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -1,2 +1,2 @@
(@Thunk it) = id (@A {})
@Thunk it = id (@A {})
it {}

View file

@ -0,0 +1,53 @@
SpaceAfter(
Defs(
Defs {
tags: [
EitherIndex(2147483648),
],
regions: [
@0-8,
],
space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
space_after: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
],
spaces: [],
type_defs: [],
value_defs: [
Body(
@0-6 RecordDestructure(
[
@1-3 SpaceAfter(
Identifier {
ident: "i",
},
[
Newline,
],
),
@4-5 Identifier {
ident: "p",
},
],
),
@7-8 Num(
"5",
),
),
],
},
@9-10 SpaceBefore(
Tag(
"Q",
),
[
Newline,
],
),
),
[
Newline,
],
)

View file

@ -0,0 +1,3 @@
{i
,p}=5
Q

View file

@ -0,0 +1,25 @@
SpaceAfter(
Record(
Collection {
items: [
@1-4 SpaceAfter(
LabelOnly(
@1-2 "t",
),
[
LineComment(
"",
),
],
),
],
final_comments: [
Newline,
Newline,
],
},
),
[
Newline,
],
)

View file

@ -1,3 +1,3 @@
(Config launchTheNukes! code) = cfg
Config launchTheNukes! code = cfg
launchTheNukes! code

View file

@ -1,3 +1,3 @@
(Config launchTheNukes! code) = cfg
Config launchTheNukes! code = cfg
launchTheNukes! code

View file

@ -1,7 +1,7 @@
(Pair x _) = Pair 0 1
(Pair _ y) = Pair 0 1
(Pair _ _) = Pair 0 1
Pair x _ = Pair 0 1
Pair _ y = Pair 0 1
Pair _ _ = Pair 0 1
_ = Pair 0 1
(Pair (Pair x _) (Pair _ y)) = Pair (Pair 0 1) (Pair 2 3)
Pair (Pair x _) (Pair _ y) = Pair (Pair 0 1) (Pair 2 3)
0

View file

@ -207,6 +207,7 @@ mod test_snapshots {
fail/if_missing_else.expr,
fail/if_outdented_else_branch.expr,
fail/if_outdented_then.expr,
fail/ifbang_eqeq.expr,
fail/import_with_lowercase_alias.moduledefs,
fail/imports_missing_comma.header,
fail/inline_hastype.expr,
@ -317,6 +318,8 @@ mod test_snapshots {
pass/basic_var.expr,
pass/binop_apply_complex.expr,
pass/binop_assign_defs_nested.expr,
pass/binop_closure_apply.expr,
pass/binops_comment_indent_change.expr,
pass/block_string_ann.expr,
pass/body_block_string_apply_string.expr,
pass/body_with_unneeded_parens.expr,
@ -324,6 +327,7 @@ mod test_snapshots {
pass/call_bang_no_space.expr,
pass/closure_in_apply_in_binop.expr,
pass/closure_in_binop_with_spaces.expr,
pass/closure_pat_reccord_comment.expr,
pass/closure_with_underscores.expr,
pass/comma_prefixed_indented_record.expr,
pass/comment_after_annotation.expr,
@ -331,6 +335,7 @@ mod test_snapshots {
pass/comment_after_expr_in_parens.expr,
pass/comment_after_op.expr,
pass/comment_before_colon_def.expr,
pass/comment_before_comma_in_tuple_type_with_func.expr,
pass/comment_before_equals_def.expr,
pass/comment_before_op.expr,
pass/comment_before_pat_in_parens.expr,
@ -353,6 +358,7 @@ mod test_snapshots {
pass/dbg_extra_parens.expr,
pass/dbg_newline_apply.expr,
pass/dbg_stmt.expr,
pass/dbg_stmt_in_parens.expr,
pass/dbg_stmt_multiline.expr,
pass/dbg_stmt_two_exprs.expr,
pass/def_bang.expr,
@ -396,6 +402,7 @@ mod test_snapshots {
pass/highest_int.expr,
pass/i_over_not_g.expr,
pass/if_def.expr,
pass/if_newline_then_negate_else_recordupdater.expr,
pass/implements_newlines_comments.expr,
pass/import.moduledefs,
pass/import_from_package.moduledefs,
@ -447,6 +454,7 @@ mod test_snapshots {
pass/multiline_apply_equals_multiline_apply.expr,
pass/multiline_backpassing.expr,
pass/multiline_binop_when_with_comments.expr,
pass/multiline_str_crazyness.expr,
pass/multiline_str_in_pat.expr,
pass/multiline_string.expr,
pass/multiline_string_in_apply.expr,
@ -464,6 +472,7 @@ mod test_snapshots {
pass/negative_float.expr,
pass/negative_in_apply_def.expr,
pass/negative_int.expr,
pass/negative_number_in_pattern.expr,
pass/negative_single_quote.expr,
pass/nested_def_annotation.moduledefs,
pass/nested_if.expr,
@ -530,6 +539,7 @@ mod test_snapshots {
pass/parenthetical_var.expr,
pass/parse_alias.expr,
pass/parse_as_ann.expr,
pass/pat_space_after_comma.expr,
pass/pattern_as.expr,
pass/pattern_as_list_rest.expr,
pass/pattern_as_spaces.expr,
@ -560,6 +570,7 @@ mod test_snapshots {
pass/record_updater_literal_apply.expr,
pass/record_updater_var_apply.expr,
pass/record_with_if.expr,
pass/record_with_lots_of_newlines.expr,
pass/requires_type.header,
pass/return_apply_newline.expr,
pass/return_field_access_in_parens.expr,