Skip to content

extremely slow performance with deeply indented bag of stuff #5500

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ehuss opened this issue Aug 13, 2022 · 1 comment
Open

extremely slow performance with deeply indented bag of stuff #5500

ehuss opened this issue Aug 13, 2022 · 1 comment

Comments

@ehuss
Copy link
Contributor

ehuss commented Aug 13, 2022

A user reported rustfmt hanging at dtolnay/cargo-expand#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)
@ytmimi
Copy link
Contributor

ytmimi commented Aug 15, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants