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

@ -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),
)