1
Fork 0

Fix problem causing rusqlite compilation to OOM.

This makes the expression re-parsing more like how it's originally done
in `parse_nonterminal`.
This commit is contained in:
Nicholas Nethercote 2025-03-31 16:32:55 +11:00
parent 81afdbc161
commit 592d113ff2
2 changed files with 136 additions and 1 deletions

View file

@ -1397,7 +1397,9 @@ impl<'a> Parser<'a> {
} else if let Some(expr) = self.eat_metavar_seq_with_matcher(
|mv_kind| matches!(mv_kind, MetaVarKind::Expr { .. }),
|this| {
let expr = this.parse_expr();
// Force collection (as opposed to just `parse_expr`) is required to avoid the
// attribute duplication seen in #138478.
let expr = this.parse_expr_force_collect();
// FIXME(nnethercote) Sometimes with expressions we get a trailing comma, possibly
// related to the FIXME in `collect_tokens_for_expr`. Examples are the multi-line
// `assert_eq!` calls involving arguments annotated with `#[rustfmt::skip]` in

View file

@ -0,0 +1,133 @@
//@ check-pass
//
// During development of #124141 at one point expression on attributes were
// being duplicated and `m1` caused an exponential blowup that caused OOM.
// The number of recursive calls depends on the number of doc comments on the
// expr block. On each recursive call, the `#[allow(deprecated)]` attribute(s) on
// the `0` somehow get duplicated, resulting in 1, 2, 4, 8, ... identical
// attributes.
//
// After the fix, the code compiles quickly and normally.
macro_rules! m1 {
($(#[$meta:meta])* { $e:expr }) => {
m1! { expr: { $e }, unprocessed: [$(#[$meta])*] }
};
(expr: { $e:expr }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => {
m1! { expr: { $e }, unprocessed: [ $($metas)* ] }
};
(expr: { $e:expr }, unprocessed: []) => {
{ $e }
}
}
macro_rules! m2 {
($(#[$meta:meta])* { $e:stmt }) => {
m2! { stmt: { $e }, unprocessed: [$(#[$meta])*] }
};
(stmt: { $e:stmt }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => {
m2! { stmt: { $e }, unprocessed: [ $($metas)* ] }
};
(stmt: { $e:stmt }, unprocessed: []) => {
{ $e }
}
}
macro_rules! m3 {
($(#[$meta:meta])* { $e:item }) => {
m3! { item: { $e }, unprocessed: [$(#[$meta])*] }
};
(item: { $e:item }, unprocessed: [ #[$meta:meta] $($metas:tt)* ]) => {
m3! { item: { $e }, unprocessed: [ $($metas)* ] }
};
(item: { $e:item }, unprocessed: []) => {
{ $e }
}
}
fn main() {
// Each additional doc comment line doubles the compile time.
m1!(
/// a1
/// a2
/// a3
/// a4
/// a5
/// a6
/// a7
/// a8
/// a9
/// a10
/// a11
/// a12
/// a13
/// a14
/// a15
/// a16
/// a17
/// a18
/// a19
/// a20
{
#[allow(deprecated)] 0
}
);
m2!(
/// a1
/// a2
/// a3
/// a4
/// a5
/// a6
/// a7
/// a8
/// a9
/// a10
/// a11
/// a12
/// a13
/// a14
/// a15
/// a16
/// a17
/// a18
/// a19
/// a20
{
#[allow(deprecated)] let x = 5
}
);
m3!(
/// a1
/// a2
/// a3
/// a4
/// a5
/// a6
/// a7
/// a8
/// a9
/// a10
/// a11
/// a12
/// a13
/// a14
/// a15
/// a16
/// a17
/// a18
/// a19
/// a20
{
#[allow(deprecated)] struct S;
}
);
}