rustfmt icon indicating copy to clipboard operation
rustfmt copied to clipboard

extremely slow performance with deeply indented bag of stuff

Open ehuss opened this issue 2 years ago • 1 comments

A user reported rustfmt hanging at https://github.com/dtolnay/cargo-expand/issues/161.

Here's a semi-reduced example:

fn foo() {
    {
        {
            {
                pub fn file (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . rule (Rule :: file , | state | { state . sequence (| state | { self :: SOI (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . optional (| state | { self :: A (state) . or_else (| state | { self :: B (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: NEWLINE (state) }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: A (state) . or_else (| state | { self :: B (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: NEWLINE (state) }) }) }) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: EOI (state) }) }) }) }
            }
        }
    }
}

That takes about 80s to format on my system.

Here is the original code in full:

# ! [feature (prelude_import)] # [prelude_import] use std :: prelude :: rust_2021 :: * ; # [macro_use] extern crate std ; extern crate pest ; # [macro_use] extern crate pest_derive ; # [grammar = "grm.pest"] pub struct MyParser ; # [allow (non_upper_case_globals)] const _PEST_GRAMMAR_MyParser : & 'static str = "A = { \"A\" }\nB = { \"B\" }\nfile = {\n    SOI ~\n    (( A | B )? ~ NEWLINE)* ~\n    EOI\n}\n\n" ; # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] pub enum Rule { EOI , A , B , file , } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: clone :: Clone for Rule { # [inline] fn clone (& self) -> Rule { * self } } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: marker :: Copy for Rule { } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: fmt :: Debug for Rule { fn fmt (& self , f : & mut :: core :: fmt :: Formatter) -> :: core :: fmt :: Result { match self { Rule :: EOI => :: core :: fmt :: Formatter :: write_str (f , "EOI") , Rule :: A => :: core :: fmt :: Formatter :: write_str (f , "A") , Rule :: B => :: core :: fmt :: Formatter :: write_str (f , "B") , Rule :: file => :: core :: fmt :: Formatter :: write_str (f , "file") , } } } # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: marker :: StructuralEq for Rule { } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: cmp :: Eq for Rule { # [inline] # [doc (hidden)] # [no_coverage] fn assert_receiver_is_total_eq (& self) -> () { } } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: hash :: Hash for Rule { fn hash < __H : :: core :: hash :: Hasher > (& self , state : & mut __H) -> () { let __self_tag = :: core :: intrinsics :: discriminant_value (self) ; :: core :: hash :: Hash :: hash (& __self_tag , state) } } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: cmp :: Ord for Rule { # [inline] fn cmp (& self , other : & Rule) -> :: core :: cmp :: Ordering { let __self_tag = :: core :: intrinsics :: discriminant_value (self) ; let __arg1_tag = :: core :: intrinsics :: discriminant_value (other) ; :: core :: cmp :: Ord :: cmp (& __self_tag , & __arg1_tag) } } # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: marker :: StructuralPartialEq for Rule { } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: cmp :: PartialEq for Rule { # [inline] fn eq (& self , other : & Rule) -> bool { let __self_tag = :: core :: intrinsics :: discriminant_value (self) ; let __arg1_tag = :: core :: intrinsics :: discriminant_value (other) ; __self_tag == __arg1_tag } } # [automatically_derived] # [allow (dead_code , non_camel_case_types , clippy :: upper_case_acronyms)] impl :: core :: cmp :: PartialOrd for Rule { # [inline] fn partial_cmp (& self , other : & Rule) -> :: core :: option :: Option < :: core :: cmp :: Ordering > { let __self_tag = :: core :: intrinsics :: discriminant_value (self) ; let __arg1_tag = :: core :: intrinsics :: discriminant_value (other) ; :: core :: cmp :: PartialOrd :: partial_cmp (& __self_tag , & __arg1_tag) } } # [allow (clippy :: all)] impl :: pest :: Parser < Rule > for MyParser { fn parse < 'i > (rule : Rule , input : & 'i str) -> :: std :: result :: Result < :: pest :: iterators :: Pairs < 'i , Rule > , :: pest :: error :: Error < Rule > > { mod rules { # ! [allow (clippy :: upper_case_acronyms)] pub mod hidden { use super :: super :: Rule ; # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn skip (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { Ok (state) } } pub mod visible { use super :: super :: Rule ; # [inline] # [allow (non_snake_case , unused_variables)] pub fn A (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . rule (Rule :: A , | state | { state . match_string ("A") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn B (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . rule (Rule :: B , | state | { state . match_string ("B") }) } # [inline] # [allow (non_snake_case , unused_variables)] pub fn file (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . rule (Rule :: file , | state | { state . sequence (| state | { self :: SOI (state) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { state . sequence (| state | { state . optional (| state | { state . sequence (| state | { state . optional (| state | { self :: A (state) . or_else (| state | { self :: B (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: NEWLINE (state) }) }) . and_then (| state | { state . repeat (| state | { state . sequence (| state | { super :: hidden :: skip (state) . and_then (| state | { state . sequence (| state | { state . optional (| state | { self :: A (state) . or_else (| state | { self :: B (state) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: NEWLINE (state) }) }) }) }) }) }) }) }) }) . and_then (| state | { super :: hidden :: skip (state) }) . and_then (| state | { self :: EOI (state) }) }) }) } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn EOI (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . rule (Rule :: EOI , | state | state . end_of_input ()) } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn SOI (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . start_of_input () } # [inline] # [allow (dead_code , non_snake_case , unused_variables)] pub fn NEWLINE (state : :: std :: boxed :: Box < :: pest :: ParserState < Rule > >) -> :: pest :: ParseResult < :: std :: boxed :: Box < :: pest :: ParserState < Rule > > > { state . match_string ("\n") . or_else (| state | state . match_string ("\r\n")) . or_else (| state | state . match_string ("\r")) } } pub use self :: visible :: * ; } :: pest :: state (input , | state | { match rule { Rule :: A => rules :: A (state) , Rule :: B => rules :: B (state) , Rule :: file => rules :: file (state) , Rule :: EOI => rules :: EOI (state) , } }) } } fn main () { }

And formatting that code looks like:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
extern crate pest;
#[macro_use]
extern crate pest_derive;

#[grammar = "grm.pest"]
pub struct MyParser;
#[allow(non_upper_case_globals)]
const _PEST_GRAMMAR_MyParser: &'static str =
    "A = { \"A\" }\nB = { \"B\" }\nfile = {\n    SOI ~\n    (( A | B )? ~ NEWLINE)* ~\n    EOI\n}\n\n";
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
pub enum Rule {
    EOI,
    A,
    B,
    file,
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::clone::Clone for Rule {
    #[inline]
    fn clone(&self) -> Rule {
        *self
    }
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::marker::Copy for Rule {}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::fmt::Debug for Rule {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Rule::EOI => ::core::fmt::Formatter::write_str(f, "EOI"),
            Rule::A => ::core::fmt::Formatter::write_str(f, "A"),
            Rule::B => ::core::fmt::Formatter::write_str(f, "B"),
            Rule::file => ::core::fmt::Formatter::write_str(f, "file"),
        }
    }
}
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::marker::StructuralEq for Rule {}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::cmp::Eq for Rule {
    #[inline]
    #[doc(hidden)]
    #[no_coverage]
    fn assert_receiver_is_total_eq(&self) -> () {}
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::hash::Hash for Rule {
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        let __self_tag = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_tag, state)
    }
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::cmp::Ord for Rule {
    #[inline]
    fn cmp(&self, other: &Rule) -> ::core::cmp::Ordering {
        let __self_tag = ::core::intrinsics::discriminant_value(self);
        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
        ::core::cmp::Ord::cmp(&__self_tag, &__arg1_tag)
    }
}
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::marker::StructuralPartialEq for Rule {}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::cmp::PartialEq for Rule {
    #[inline]
    fn eq(&self, other: &Rule) -> bool {
        let __self_tag = ::core::intrinsics::discriminant_value(self);
        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
        __self_tag == __arg1_tag
    }
}
#[automatically_derived]
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
impl ::core::cmp::PartialOrd for Rule {
    #[inline]
    fn partial_cmp(&self, other: &Rule) -> ::core::option::Option<::core::cmp::Ordering> {
        let __self_tag = ::core::intrinsics::discriminant_value(self);
        let __arg1_tag = ::core::intrinsics::discriminant_value(other);
        ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag)
    }
}
#[allow(clippy::all)]
impl ::pest::Parser<Rule> for MyParser {
    fn parse<'i>(
        rule: Rule,
        input: &'i str,
    ) -> ::std::result::Result<::pest::iterators::Pairs<'i, Rule>, ::pest::error::Error<Rule>> {
        mod rules {
            #![allow(clippy::upper_case_acronyms)]
            pub mod hidden {
                use super::super::Rule;
                #[inline]
                #[allow(dead_code, non_snake_case, unused_variables)]
                pub fn skip(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    Ok(state)
                }
            }
            pub mod visible {
                use super::super::Rule;
                #[inline]
                #[allow(non_snake_case, unused_variables)]
                pub fn A(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state.rule(Rule::A, |state| state.match_string("A"))
                }
                #[inline]
                #[allow(non_snake_case, unused_variables)]
                pub fn B(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state.rule(Rule::B, |state| state.match_string("B"))
                }
                #[inline]
                #[allow(non_snake_case, unused_variables)]
                pub fn file(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state.rule(Rule::file,
                        |state|
                            {
                                state.sequence(|state|
                                        {
                                            self::SOI(state).and_then(|state|
                                                                {
                                                                    super::hidden::skip(state)
                                                                }).and_then(|state|
                                                            {
                                                                state.sequence(|state|
                                                                        {
                                                                            state.optional(|state|
                                                                                    {
                                                                                        state.sequence(|state|
                                                                                                    {
                                                                                                        state.optional(|state|
                                                                                                                        {
                                                                                                                            self::A(state).or_else(|state| { self::B(state) })
                                                                                                                        }).and_then(|state|
                                                                                                                    {
                                                                                                                        super::hidden::skip(state)
                                                                                                                    }).and_then(|state| { self::NEWLINE(state) })
                                                                                                    }).and_then(|state|
                                                                                                {
                                                                                                    state.repeat(|state|
                                                                                                            {
                                                                                                                state.sequence(|state|
                                                                                                                        {
                                                                                                                            super::hidden::skip(state).and_then(|state|
                                                                                                                                    {
                                                                                                                                        state.sequence(|state|
                                                                                                                                                {
                                                                                                                                                    state.optional(|state|
                                                                                                                                                                    {
                                                                                                                                                                        self::A(state).or_else(|state| { self::B(state) })
                                                                                                                                                                    }).and_then(|state|
                                                                                                                                                                {
                                                                                                                                                                    super::hidden::skip(state)
                                                                                                                                                                }).and_then(|state| { self::NEWLINE(state) })
                                                                                                                                                })
                                                                                                                                    })
                                                                                                                        })
                                                                                                            })
                                                                                                })
                                                                                    })
                                                                        })
                                                            }).and_then(|state|
                                                        {
                                                            super::hidden::skip(state)
                                                        }).and_then(|state| { self::EOI(state) })
                                        })
                            })
                }
                #[inline]
                #[allow(dead_code, non_snake_case, unused_variables)]
                pub fn EOI(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state.rule(Rule::EOI, |state| state.end_of_input())
                }
                #[inline]
                #[allow(dead_code, non_snake_case, unused_variables)]
                pub fn SOI(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state.start_of_input()
                }
                #[inline]
                #[allow(dead_code, non_snake_case, unused_variables)]
                pub fn NEWLINE(
                    state: ::std::boxed::Box<::pest::ParserState<Rule>>,
                ) -> ::pest::ParseResult<::std::boxed::Box<::pest::ParserState<Rule>>>
                {
                    state
                        .match_string("\n")
                        .or_else(|state| state.match_string("\r\n"))
                        .or_else(|state| state.match_string("\r"))
                }
            }
            pub use self::visible::*;
        }
        ::pest::state(input, |state| match rule {
            Rule::A => rules::A(state),
            Rule::B => rules::B(state),
            Rule::file => rules::file(state),
            Rule::EOI => rules::EOI(state),
        })
    }
}

fn main() {}
rustfmt 1.5.1-nightly (20ffea69 2022-08-11)

ehuss avatar Aug 13 '22 18:08 ehuss

Thanks for reaching out. I think this is likely related to #4476 #4867

ytmimi avatar Aug 15 '22 13:08 ytmimi