Repository: rust-analyzer/rust-analyzer
Pull request #7851 (2021-03-02 15:35:09)

Compress tests
bors r+
🤖


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ec5aecfa09..739edb9328 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -879,7 +879,6 @@ dependencies = [
  "profile",
  "rustc-hash",
  "smallvec",
- "stdx",
  "syntax",
  "test_utils",
  "tt",
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 69fcdab07a..769945c474 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -33,11 +33,11 @@ mod has_source;
 pub use crate::{
     attrs::{HasAttrs, Namespace},
     code_model::{
-        Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable,
-        CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field,
-        FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam,
-        Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias,
-        TypeParam, Union, Variant, VariantDef,
+        Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
+        ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
+        GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
+        Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
+        Variant, VariantDef,
     },
     has_source::HasSource,
     semantics::{PathResolution, Semantics, SemanticsScope},
@@ -47,6 +47,7 @@ pub use hir_def::{
     adt::StructKind,
     attr::{Attrs, Documentation},
     body::scope::ExprScopes,
+    builtin_type::BuiltinType,
     find_path::PrefixKind,
     import_map,
     item_scope::ItemInNs,
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 4b1b245628..45170dd29d 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -6,7 +6,7 @@ use syntax::{
     ast::{self, AstNode, AstToken, VisibilityOwner},
     Direction, NodeOrToken, SourceFile,
     SyntaxKind::{self, *},
-    SyntaxNode, TextRange, TextSize,
+    SyntaxNode, TextRange,
 };
 
 #[derive(Debug, PartialEq, Eq)]
@@ -16,7 +16,6 @@ pub enum FoldKind {
     Mods,
     Block,
     ArgList,
-    Region,
 }
 
 #[derive(Debug)]
@@ -30,8 +29,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
     let mut visited_comments = FxHashSet::default();
     let mut visited_imports = FxHashSet::default();
     let mut visited_mods = FxHashSet::default();
-    // regions can be nested, here is a LIFO buffer
-    let mut regions_starts: Vec<TextSize> = vec![];
 
     for element in file.syntax().descendants_with_tokens() {
         // Fold items that span multiple lines
@@ -51,25 +48,10 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
                 // Fold groups of comments
                 if let Some(comment) = ast::Comment::cast(token) {
                     if !visited_comments.contains(&comment) {
-                        // regions are not real comments
-                        if comment.text().trim().starts_with("// region:") {
-                            regions_starts.push(comment.syntax().text_range().start());
-                        } else if comment.text().trim().starts_with("// endregion") {
-                            if let Some(region) = regions_starts.pop() {
-                                res.push(Fold {
-                                    range: TextRange::new(
-                                        region,
-                                        comment.syntax().text_range().end(),
-                                    ),
-                                    kind: FoldKind::Region,
-                                })
-                            }
-                        } else {
-                            if let Some(range) =
-                                contiguous_range_for_comment(comment, &mut visited_comments)
-                            {
-                                res.push(Fold { range, kind: FoldKind::Comment })
-                            }
+                        if let Some(range) =
+                            contiguous_range_for_comment(comment, &mut visited_comments)
+                        {
+                            res.push(Fold { range, kind: FoldKind::Comment })
                         }
                     }
                 }
@@ -193,16 +175,9 @@ fn contiguous_range_for_comment(
                 }
                 if let Some(c) = ast::Comment::cast(token) {
                     if c.kind() == group_kind {
-                        // regions are not real comments
-                        if c.text().trim().starts_with("// region:")
-                            || c.text().trim().starts_with("// endregion")
-                        {
-                            break;
-                        } else {
-                            visited.insert(c.clone());
-                            last = c;
-                            continue;
-                        }
+                        visited.insert(c.clone());
+                        last = c;
+                        continue;
                     }
                 }
                 // The comment group ends because either:
@@ -249,7 +224,6 @@ mod tests {
                 FoldKind::Mods => "mods",
                 FoldKind::Block => "block",
                 FoldKind::ArgList => "arglist",
-                FoldKind::Region => "region",
             };
             assert_eq!(kind, &attr.unwrap());
         }
@@ -441,19 +415,6 @@ fn foo<fold arglist>(
     x: i32,
     y: String,
 )</fold> {}
-"#,
-        )
-    }
-
-    #[test]
-    fn fold_region() {
-        check(
-            r#"
-// 1. some normal comment
-<fold region>// region: test
-// 2. some normal comment
-calling_function(x,y);
-// endregion: test</fold>
 "#,
         )
     }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index a9454cfa31..20b799490e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -5,7 +5,6 @@ use hir::{
 use ide_db::{
     base_db::SourceDatabase,
     defs::{Definition, NameClass, NameRefClass},
-    helpers::FamousDefs,
     RootDatabase,
 };
 use itertools::Itertools;
@@ -108,14 +107,16 @@ pub(crate) fn hover(
         }
     };
     if let Some(definition) = definition {
-        let famous_defs = match &definition {
-            Definition::ModuleDef(ModuleDef::BuiltinType(_)) => {
-                Some(FamousDefs(&sema, sema.scope(&node).krate()))
-            }
-            _ => None,
-        };
-        if let Some(markup) = hover_for_definition(db, definition, famous_defs.as_ref()) {
-            res.markup = process_markup(sema.db, definition, &markup, links_in_hover, markdown);
+        if let Some(markup) = hover_for_definition(db, definition) {
+            let markup = markup.as_str();
+            let markup = if !markdown {
+                remove_markdown(markup)
+            } else if links_in_hover {
+                rewrite_links(db, markup, &definition)
+            } else {
+                remove_links(markup)
+            };
+            res.markup = Markup::from(markup);
             if let Some(action) = show_implementations_action(db, definition) {
                 res.actions.push(action);
             }
@@ -137,9 +138,6 @@ pub(crate) fn hover(
         // don't highlight the entire parent node on comment hover
         return None;
     }
-    if let res @ Some(_) = hover_for_keyword(&sema, links_in_hover, markdown, &token) {
-        return res;
-    }
 
     let node = token
         .ancestors()
@@ -274,24 +272,6 @@ fn hover_markup(
     }
 }
 
-fn process_markup(
-    db: &RootDatabase,
-    def: Definition,
-    markup: &Markup,
-    links_in_hover: bool,
-    markdown: bool,
-) -> Markup {
-    let markup = markup.as_str();
-    let markup = if !markdown {
-        remove_markdown(markup)
-    } else if links_in_hover {
-        rewrite_links(db, markup, &def)
-    } else {
-        remove_links(markup)
-    };
-    Markup::from(markup)
-}
-
 fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> {
     match def {
         Definition::Field(f) => Some(f.parent_def(db).name(db)),
@@ -324,11 +304,7 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
     def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
 }
 
-fn hover_for_definition(
-    db: &RootDatabase,
-    def: Definition,
-    famous_defs: Option<&FamousDefs>,
-) -> Option<Markup> {
+fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
     let mod_path = definition_mod_path(db, &def);
     return match def {
         Definition::Macro(it) => {
@@ -363,9 +339,7 @@ fn hover_for_definition(
             ModuleDef::Static(it) => from_def_source(db, it, mod_path),
             ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
             ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
-            ModuleDef::BuiltinType(it) => famous_defs
-                .and_then(|fd| hover_for_builtin(fd, it))
-                .or_else(|| Some(Markup::fenced_block(&it.name()))),
+            ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it.name())),
         },
         Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
         Definition::SelfType(impl_def) => {
@@ -406,52 +380,11 @@ fn hover_for_definition(
     }
 }
 
-fn hover_for_keyword(
-    sema: &Semantics<RootDatabase>,
-    links_in_hover: bool,
-    markdown: bool,
-    token: &SyntaxToken,
-) -> Option<RangeInfo<HoverResult>> {
-    if !token.kind().is_keyword() {
-        return None;
-    }
-    let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate());
-    // std exposes {}_keyword modules with docstrings on the root to document keywords
-    let keyword_mod = format!("{}_keyword", token.text());
-    let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
-    let docs = doc_owner.attrs(sema.db).docs()?;
-    let markup = process_markup(
-        sema.db,
-        Definition::ModuleDef(doc_owner.into()),
-        &hover_markup(Some(docs.into()), Some(token.text().into()), None)?,
-        links_in_hover,
-        markdown,
-    );
-    Some(RangeInfo::new(token.text_range(), HoverResult { markup, actions: Default::default() }))
-}
-
-fn hover_for_builtin(famous_defs: &FamousDefs, builtin: hir::BuiltinType) -> Option<Markup> {
-    // std exposes prim_{} modules with docstrings on the root to document the builtins
-    let primitive_mod = format!("prim_{}", builtin.name());
-    let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
-    let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
-    hover_markup(Some(docs.into()), Some(builtin.name().to_string()), None)
-}
-
-fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> {
-    let db = famous_defs.0.db;
-    let std_crate = famous_defs.std()?;
-    let std_root_module = std_crate.root_module(db);
-    std_root_module
-        .children(db)
-        .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
-}
-
 fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
     return tokens.max_by_key(priority);
     fn priority(n: &SyntaxToken) -> usize {
         match n.kind() {
-            IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
+            IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 3,
             T!['('] | T![')'] => 2,
             kind if kind.is_trivia() => 0,
             _ => 1,
@@ -3590,48 +3523,6 @@ use foo::bar::{self$0};
 
                 But this should appear
             "#]],
-        )
-    }
-
-    #[test]
-    fn hover_keyword() {
-        let ra_fixture = r#"//- /main.rs crate:main deps:std
-fn f() { retur$0n; }"#;
-        let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
-        check(
-            &fixture,
-            expect![[r#"
-                *return*
-
-                ```rust
-                return
-                ```
-
-                ---
-
-                Docs for return_keyword
-            "#]],
-        );
-    }
-
-    #[test]
-    fn hover_builtin() {
-        let ra_fixture = r#"//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }"#;
-        let fixture = format!("{}\n{}", ra_fixture, FamousDefs::FIXTURE);
-        check(
-            &fixture,
-            expect![[r#"
-                *str*
-
-                ```rust
-                str
-                ```
-
-                ---
-
-                Docs for prim_str
-            "#]],
         );
     }
 }
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 7fcae13e0e..2c077ed1fc 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -7,7 +7,6 @@ use syntax::{
     SyntaxKind::{self, USE_TREE, WHITESPACE},
     SyntaxNode, SyntaxToken, TextRange, TextSize, T,
 };
-use test_utils::mark;
 use text_edit::{TextEdit, TextEditBuilder};
 
 // Feature: Join Lines
@@ -45,9 +44,9 @@ pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
         let text = token.text();
         for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
             let pos: TextSize = (pos as u32).into();
-            let offset = token.text_range().start() + range.start() + pos;
-            if !edit.invalidates_offset(offset) {
-                remove_newline(&mut edit, &token, offset);
+            let off = token.text_range().start() + range.start() + pos;
+            if !edit.invalidates_offset(off) {
+                remove_newline(&mut edit, &token, off);
             }
         }
     }
@@ -57,25 +56,14 @@ pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
 
 fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) {
     if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
-        let mut string_open_quote = false;
-        if let Some(string) = ast::String::cast(token.clone()) {
-            if let Some(range) = string.open_quote_text_range() {
-                mark::hit!(join_string_literal);
-                string_open_quote = range.end() == offset;
-            }
-        }
-
-        let n_spaces_after_line_break = {
-            let suff = &token.text()[TextRange::new(
-                offset - token.text_range().start() + TextSize::of('\n'),
-                TextSize::of(token.text()),
-            )];
-            suff.bytes().take_while(|&b| b == b' ').count()
-        };
-
-        let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into());
-        let replace_with = if string_open_quote { "" } else { " " };
-        edit.replace(range, replace_with.to_string());
+        // The node is either the first or the last in the file
+        let suff = &token.text()[TextRange::new(
+            offset - token.text_range().start() + TextSize::of('\n'),
+            TextSize::of(token.text()),
+        )];
+        let spaces = suff.bytes().take_while(|&b| b == b' ').count();
+
+        edit.replace(TextRange::at(offset, ((spaces + 1) as u32).into()), " ".to_string());
         return;
     }
 
@@ -206,7 +194,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str {
 #[cfg(test)]
 mod tests {
     use syntax::SourceFile;
-    use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark};
+    use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
 
     use super::*;
 
@@ -783,42 +771,4 @@ fn foo() {
         ",
         );
     }
-
-    #[test]
-    fn join_string_literal() {
-        mark::check!(join_string_literal);
-        check_join_lines(
-            r#"
-fn main() {
-    $0"
-hello
-";
-}
-"#,
-            r#"
-fn main() {
-    $0"hello
-";
-}
-"#,
-        );
-
-        check_join_lines(
-            r#"
-fn main() {
-    "
-$0hello
-world
-";
-}
-"#,
-            r#"
-fn main() {
-    "
-$0hello world
-";
-}
-"#,
-        );
-    }
 }
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index d37c626429..533624c1fd 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -11,17 +11,20 @@ use syntax::{
 
 /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
 const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
+
 /// Identifier names that won't be suggested, ever
 ///
 /// **NOTE**: they all must be snake lower case
 const USELESS_NAMES: &[&str] =
     &["new", "default", "option", "some", "none", "ok", "err", "str", "string"];
+
 /// Generic types replaced by their first argument
 ///
 /// # Examples
 /// `Option<Name>` -> `Name`
 /// `Result<User, Error>` -> `User`
 const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
+
 /// Prefixes to strip from methods names
 ///
 /// # Examples
@@ -29,6 +32,7 @@ const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
 /// `args.into_config()` -> `config`
 /// `bytes.to_vec()` -> `vec`
 const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
+
 /// Useless methods that are stripped from expression
 ///
 /// # Examples
@@ -253,625 +257,473 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
-
-    use crate::tests::check_name_suggestion;
-
-    mod from_func_call {
-        use super::*;
-
-        #[test]
-        fn no_args() {
-            check_name_suggestion(
-                |e, _| from_func_call(e),
-                r#"
-                fn foo() {
-                    $0bar()$0
-                }"#,
-                "bar",
-            );
-        }
+    use ide_db::base_db::{fixture::WithFixture, FileRange};
 
-        #[test]
-        fn single_arg() {
-            check_name_suggestion(
-                |e, _| from_func_call(e),
-                r#"
-                fn foo() {
-                    $0bar(1)$0
-                }"#,
-                "bar",
-            );
-        }
-
-        #[test]
-        fn many_args() {
-            check_name_suggestion(
-                |e, _| from_func_call(e),
-                r#"
-                fn foo() {
-                    $0bar(1, 2, 3)$0
-                }"#,
-                "bar",
-            );
-        }
-
-        #[test]
-        fn path() {
-            check_name_suggestion(
-                |e, _| from_func_call(e),
-                r#"
-                fn foo() {
-                    $0i32::bar(1, 2, 3)$0
-                }"#,
-                "bar",
-            );
-        }
+    use super::*;
 
-        #[test]
-        fn generic_params() {
-            check_name_suggestion(
-                |e, _| from_func_call(e),
-                r#"
-                fn foo() {
-                    $0bar::<i32>(1, 2, 3)$0
-                }"#,
-                "bar",
-            );
-        }
+    #[track_caller]
+    fn check(ra_fixture: &str, expected: &str) {
+        let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
+        let frange = FileRange { file_id, range: range_or_offset.into() };
+
+        let sema = Semantics::new(&db);
+        let source_file = sema.parse(frange.file_id);
+        let element = source_file.syntax().covering_element(frange.range);
+        let expr =
+            element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
+        assert_eq!(
+            expr.syntax().text_range(),
+            frange.range,
+            "selection is not an expression(yet contained in one)"
+        );
+        let name = variable(&expr, &sema);
+        assert_eq!(&name, expected);
     }
 
-    mod from_method_call {
-        use super::*;
-
-        #[test]
-        fn no_args() {
-            check_name_suggestion(
-                |e, _| from_method_call(e),
-                r#"
-                fn foo() {
-                    $0bar.frobnicate()$0
-                }"#,
-                "frobnicate",
-            );
-        }
-
-        #[test]
-        fn generic_params() {
-            check_name_suggestion(
-                |e, _| from_method_call(e),
-                r#"
-                fn foo() {
-                    $0bar.frobnicate::<i32, u32>()$0
-                }"#,
-                "frobnicate",
-            );
-        }
-
-        #[test]
-        fn to_name() {
-            check_name_suggestion(
-                |e, _| from_method_call(e),
-                r#"
-                struct Args;
-                struct Config;
-                impl Args {
-                    fn to_config(&self) -> Config {}
-                }
-                fn foo() {
-                    $0Args.to_config()$0;
-                }"#,
-                "config",
-            );
-        }
+    #[test]
+    fn no_args() {
+        check(r#"fn foo() { $0bar()$0 }"#, "bar");
+        check(r#"fn foo() { $0bar.frobnicate()$0 }"#, "frobnicate");
     }
 
-    mod from_param {
-        use crate::tests::check_name_suggestion_not_applicable;
+    #[test]
+    fn single_arg() {
+        check(r#"fn foo() { $0bar(1)$0 }"#, "bar");
+    }
 
-        use super::*;
+    #[test]
+    fn many_args() {
+        check(r#"fn foo() { $0bar(1, 2, 3)$0 }"#, "bar");
+    }
 
-        #[test]
-        fn plain_func() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar(n: i32, m: u32);
-                fn foo() {
-                    bar($01$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn path() {
+        check(r#"fn foo() { $0i32::bar(1, 2, 3)$0 }"#, "bar");
+    }
 
-        #[test]
-        fn mut_param() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar(mut n: i32, m: u32);
-                fn foo() {
-                    bar($01$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn generic_params() {
+        check(r#"fn foo() { $0bar::<i32>(1, 2, 3)$0 }"#, "bar");
+        check(r#"fn foo() { $0bar.frobnicate::<i32, u32>()$0 }"#, "frobnicate");
+    }
 
-        #[test]
-        fn func_does_not_exist() {
-            check_name_suggestion_not_applicable(
-                from_param,
-                r#"
-                fn foo() {
-                    bar($01$0, 2)
-                }"#,
-            );
-        }
+    #[test]
+    fn to_name() {
+        check(
+            r#"
+struct Args;
+struct Config;
+impl Args {
+    fn to_config(&self) -> Config {}
+}
+fn foo() {
+    $0Args.to_config()$0;
+}
+"#,
+            "config",
+        );
+    }
 
-        #[test]
-        fn unnamed_param() {
-            check_name_suggestion_not_applicable(
-                from_param,
-                r#"
-                fn bar(_: i32, m: u32);
-                fn foo() {
-                    bar($01$0, 2)
-                }"#,
-            );
-        }
+    #[test]
+    fn plain_func() {
+        check(
+            r#"
+fn bar(n: i32, m: u32);
+fn foo() { bar($01$0, 2) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn tuple_pat() {
-            check_name_suggestion_not_applicable(
-                from_param,
-                r#"
-                fn bar((n, k): (i32, i32), m: u32);
-                fn foo() {
-                    bar($0(1, 2)$0, 3)
-                }"#,
-            );
-        }
+    #[test]
+    fn mut_param() {
+        check(
+            r#"
+fn bar(mut n: i32, m: u32);
+fn foo() { bar($01$0, 2) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn ref_pat() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar(&n: &i32, m: u32);
-                fn foo() {
-                    bar($0&1$0, 3)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn func_does_not_exist() {
+        check(r#"fn foo() { bar($01$0, 2) }"#, "var_name");
+    }
 
-        #[test]
-        fn box_pat() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar(box n: &i32, m: u32);
-                fn foo() {
-                    bar($01$0, 3)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn unnamed_param() {
+        check(
+            r#"
+fn bar(_: i32, m: u32);
+fn foo() { bar($01$0, 2) }
+"#,
+            "var_name",
+        );
+    }
 
-        #[test]
-        fn param_out_of_index() {
-            check_name_suggestion_not_applicable(
-                from_param,
-                r#"
-                fn bar(n: i32, m: u32);
-                fn foo() {
-                    bar(1, 2, $03$0)
-                }"#,
-            );
-        }
+    #[test]
+    fn tuple_pat() {
+        check(
+            r#"
+fn bar((n, k): (i32, i32), m: u32);
+fn foo() {
+    bar($0(1, 2)$0, 3)
+}
+"#,
+            "var_name",
+        );
+    }
 
-        #[test]
-        fn generic_param_resolved() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar<T>(n: T, m: u32);
-                fn foo() {
-                    bar($01$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn ref_pat() {
+        check(
+            r#"
+fn bar(&n: &i32, m: u32);
+fn foo() { bar($0&1$0, 3) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn generic_param_unresolved() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                fn bar<T>(n: T, m: u32);
-                fn foo<T>(x: T) {
-                    bar($0x$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn box_pat() {
+        check(
+            r#"
+fn bar(box n: &i32, m: u32);
+fn foo() { bar($01$0, 3) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn method() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                struct S;
-                impl S {
-                    fn bar(&self, n: i32, m: u32);
-                }
-                fn foo() {
-                    S.bar($01$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn param_out_of_index() {
+        check(
+            r#"
+fn bar(n: i32, m: u32);
+fn foo() { bar(1, 2, $03$0) }
+"#,
+            "var_name",
+        );
+    }
 
-        #[test]
-        fn method_ufcs() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                struct S;
-                impl S {
-                    fn bar(&self, n: i32, m: u32);
-                }
-                fn foo() {
-                    S::bar(&S, $01$0, 2)
-                }"#,
-                "n",
-            );
-        }
+    #[test]
+    fn generic_param_resolved() {
+        check(
+            r#"
+fn bar<T>(n: T, m: u32);
+fn foo() { bar($01$0, 2) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn method_self() {
-            check_name_suggestion_not_applicable(
-                from_param,
-                r#"
-                struct S;
-                impl S {
-                    fn bar(&self, n: i32, m: u32);
-                }
-                fn foo() {
-                    S::bar($0&S$0, 1, 2)
-                }"#,
-            );
-        }
+    #[test]
+    fn generic_param_unresolved() {
+        check(
+            r#"
+fn bar<T>(n: T, m: u32);
+fn foo<T>(x: T) { bar($0x$0, 2) }
+"#,
+            "n",
+        );
+    }
 
-        #[test]
-        fn method_self_named() {
-            check_name_suggestion(
-                from_param,
-                r#"
-                struct S;
-                impl S {
-                    fn bar(strukt: &Self, n: i32, m: u32);
-                }
-                fn foo() {
-                    S::bar($0&S$0, 1, 2)
-                }"#,
-                "strukt",
-            );
-        }
+    #[test]
+    fn method() {
+        check(
+            r#"
+struct S;
+impl S { fn bar(&self, n: i32, m: u32); }
+fn foo() { S.bar($01$0, 2) }
+"#,
+            "n",
+        );
     }
 
-    mod from_type {
-        use crate::tests::check_name_suggestion_not_applicable;
+    #[test]
+    fn method_ufcs() {
+        check(
+            r#"
+struct S;
+impl S { fn bar(&self, n: i32, m: u32); }
+fn foo() { S::bar(&S, $01$0, 2) }
+"#,
+            "n",
+        );
+    }
 
-        use super::*;
+    #[test]
+    fn method_self() {
+        check(
+            r#"
+struct S;
+impl S { fn bar(&self, n: i32, m: u32); }
+fn foo() { S::bar($0&S$0, 1, 2) }
+"#,
+            "s",
+        );
+    }
 
-        #[test]
-        fn i32() {
-            check_name_suggestion_not_applicable(
-                from_type,
-                r#"
-                fn foo() {
-                    let _: i32 = $01$0;
-                }"#,
-            );
-        }
+    #[test]
+    fn method_self_named() {
+        check(
+            r#"
+struct S;
+impl S { fn bar(strukt: &Self, n: i32, m: u32); }
+fn foo() { S::bar($0&S$0, 1, 2) }
+"#,
+            "strukt",
+        );
+    }
 
-        #[test]
-        fn u64() {
-            check_name_suggestion_not_applicable(
-                from_type,
-                r#"
-                fn foo() {
-                    let _: u64 = $01$0;
-                }"#,
-            );
-        }
+    #[test]
+    fn i32() {
+        check(r#"fn foo() { let _: i32 = $01$0; }"#, "var_name");
+    }
 
-        #[test]
-        fn bool() {
-            check_name_suggestion_not_applicable(
-                from_type,
-                r#"
-                fn foo() {
-                    let _: bool = $0true$0;
-                }"#,
-            );
-        }
+    #[test]
+    fn u64() {
+        check(r#"fn foo() { let _: u64 = $01$0; }"#, "var_name");
+    }
 
-        #[test]
-        fn struct_unit() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct Seed;
-                fn foo() {
-                    let _ = $0Seed$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn bool() {
+        check(r#"fn foo() { let _: bool = $0true$0; }"#, "var_name");
+    }
 
-        #[test]
-        fn struct_unit_to_snake() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct SeedState;
-                fn foo() {
-                    let _ = $0SeedState$0;
-                }"#,
-                "seed_state",
-            );
-        }
+    #[test]
+    fn struct_unit() {
+        check(
+            r#"
+struct Seed;
+fn foo() { let _ = $0Seed$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn struct_single_arg() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct Seed(u32);
-                fn foo() {
-                    let _ = $0Seed(0)$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn struct_unit_to_snake() {
+        check(
+            r#"
+struct SeedState;
+fn foo() { let _ = $0SeedState$0; }
+"#,
+            "seed_state",
+        );
+    }
 
-        #[test]
-        fn struct_with_fields() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct Seed { value: u32 }
-                fn foo() {
-                    let _ = $0Seed { value: 0 }$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn struct_single_arg() {
+        check(
+            r#"
+struct Seed(u32);
+fn foo() { let _ = $0Seed(0)$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn enum_() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                enum Kind { A, B }
-                fn foo() {
-                    let _ = $0Kind::A$0;
-                }"#,
-                "kind",
-            );
-        }
+    #[test]
+    fn struct_with_fields() {
+        check(
+            r#"
+struct Seed { value: u32 }
+fn foo() { let _ = $0Seed { value: 0 }$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn enum_generic_resolved() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                enum Kind<T> { A(T), B }
-                fn foo() {
-                    let _ = $0Kind::A(1)$0;
-                }"#,
-                "kind",
-            );
-        }
+    #[test]
+    fn enum_() {
+        check(
+            r#"
+enum Kind { A, B }
+fn foo() { let _ = $0Kind::A$0; }
+"#,
+            "kind",
+        );
+    }
 
-        #[test]
-        fn enum_generic_unresolved() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                enum Kind<T> { A(T), B }
-                fn foo<T>(x: T) {
-                    let _ = $0Kind::A(x)$0;
-                }"#,
-                "kind",
-            );
-        }
+    #[test]
+    fn enum_generic_resolved() {
+        check(
+            r#"
+enum Kind<T> { A { x: T }, B }
+fn foo() { let _ = $0Kind::A { x:1 }$0; }
+"#,
+            "kind",
+        );
+    }
 
-        #[test]
-        fn dyn_trait() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                trait DynHandler {}
-                fn bar() -> dyn DynHandler {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "dyn_handler",
-            );
-        }
+    #[test]
+    fn enum_generic_unresolved() {
+        check(
+            r#"
+enum Kind<T> { A { x: T }, B }
+fn foo<T>(x: T) { let _ = $0Kind::A { x }$0; }
+"#,
+            "kind",
+        );
+    }
 
-        #[test]
-        fn impl_trait() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                trait StaticHandler {}
-                fn bar() -> impl StaticHandler {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "static_handler",
-            );
-        }
+    #[test]
+    fn dyn_trait() {
+        check(
+            r#"
+trait DynHandler {}
+fn bar() -> dyn DynHandler {}
+fn foo() { $0(bar())$0; }
+"#,
+            "dyn_handler",
+        );
+    }
 
-        #[test]
-        fn impl_trait_plus_clone() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                trait StaticHandler {}
-                trait Clone {}
-                fn bar() -> impl StaticHandler + Clone {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "static_handler",
-            );
-        }
+    #[test]
+    fn impl_trait() {
+        check(
+            r#"
+trait StaticHandler {}
+fn bar() -> impl StaticHandler {}
+fn foo() { $0(bar())$0; }
+"#,
+            "static_handler",
+        );
+    }
 
-        #[test]
-        fn impl_trait_plus_lifetime() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                trait StaticHandler {}
-                trait Clone {}
-                fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
-                fn foo() {
-                    $0bar(&1)$0;
-                }"#,
-                "static_handler",
-            );
-        }
+    #[test]
+    fn impl_trait_plus_clone() {
+        check(
+            r#"
+trait StaticHandler {}
+trait Clone {}
+fn bar() -> impl StaticHandler + Clone {}
+fn foo() { $0(bar())$0; }
+"#,
+            "static_handler",
+        );
+    }
 
-        #[test]
-        fn impl_trait_plus_trait() {
-            check_name_suggestion_not_applicable(
-                from_type,
-                r#"
-                trait Handler {}
-                trait StaticHandler {}
-                fn bar() -> impl StaticHandler + Handler {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-            );
-        }
+    #[test]
+    fn impl_trait_plus_lifetime() {
+        check(
+            r#"
+trait StaticHandler {}
+trait Clone {}
+fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
+fn foo() { $0(bar(&1))$0; }
+"#,
+            "static_handler",
+        );
+    }
 
-        #[test]
-        fn ref_value() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct Seed;
-                fn bar() -> &Seed {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn impl_trait_plus_trait() {
+        check(
+            r#"
+trait Handler {}
+trait StaticHandler {}
+fn bar() -> impl StaticHandler + Handler {}
+fn foo() { $0(bar())$0; }
+"#,
+            "bar",
+        );
+    }
 
-        #[test]
-        fn box_value() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                struct Box<T>(*const T);
-                struct Seed;
-                fn bar() -> Box<Seed> {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn ref_value() {
+        check(
+            r#"
+struct Seed;
+fn bar() -> &Seed {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn box_generic() {
-            check_name_suggestion_not_applicable(
-                from_type,
-                r#"
-                struct Box<T>(*const T);
-                fn bar<T>() -> Box<T> {}
-                fn foo<T>() {
-                    $0bar::<T>()$0;
-                }"#,
-            );
-        }
+    #[test]
+    fn box_value() {
+        check(
+            r#"
+struct Box<T>(*const T);
+struct Seed;
+fn bar() -> Box<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn option_value() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                enum Option<T> { Some(T) }
-                struct Seed;
-                fn bar() -> Option<Seed> {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn box_generic() {
+        check(
+            r#"
+struct Box<T>(*const T);
+fn bar<T>() -> Box<T> {}
+fn foo<T>() { $0(bar::<T>())$0; }
+"#,
+            "bar",
+        );
+    }
 
-        #[test]
-        fn result_value() {
-            check_name_suggestion(
-                from_type,
-                r#"
-                enum Result<T, E> { Ok(T), Err(E) }
-                struct Seed;
-                struct Error;
-                fn bar() -> Result<Seed, Error> {}
-                fn foo() {
-                    $0bar()$0;
-                }"#,
-                "seed",
-            );
-        }
+    #[test]
+    fn option_value() {
+        check(
+            r#"
+enum Option<T> { Some(T) }
+struct Seed;
+fn bar() -> Option<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seed",
+        );
     }
 
-    mod variable {
-        use super::*;
+    #[test]
+    fn result_value() {
+        check(
+            r#"
+enum Result<T, E> { Ok(T), Err(E) }
+struct Seed;
+struct Error;
+fn bar() -> Result<Seed, Error> {}
+fn foo() { $0(bar())$0; }
+"#,
+            "seed",
+        );
+    }
 
-        #[test]
-        fn ref_call() {
-            check_name_suggestion(
-                |e, c| Some(variable(e, c)),
-                r#"
-                fn foo() {
-                    $0&bar(1, 3)$0
-                }"#,
-                "bar",
-            );
-        }
+    #[test]
+    fn ref_call() {
+        check(
+            r#"
+fn foo() { $0&bar(1, 3)$0 }
+"#,
+            "bar",
+        );
+    }
 
-        #[test]
-        fn name_to_string() {
-            check_name_suggestion(
-                |e, c| Some(variable(e, c)),
-                r#"
-                fn foo() {
-                    $0function.name().to_string()$0
-                }"#,
-                "name",
-            );
-        }
+    #[test]
+    fn name_to_string() {
+        check(
+            r#"
+fn foo() { $0function.name().to_string()$0 }
+"#,
+            "name",
+        );
+    }
 
-        #[test]
-        fn nested_useless_method() {
-            check_name_suggestion(
-                |e, c| Some(variable(e, c)),
-                r#"
-                fn foo() {
-                    $0function.name().as_ref().unwrap().to_string()$0
-                }"#,
-                "name",
-            );
-        }
+    #[test]
+    fn nested_useless_method() {
+        check(
+            r#"
+fn foo() { $0function.name().as_ref().unwrap().to_string()$0 }
+"#,
+            "name",
+        );
     }
 }
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3ff77400bb..f9de8ce0e6 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -41,10 +41,6 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
 impl FamousDefs<'_, '_> {
     pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
 
-    pub fn std(&self) -> Option<Crate> {
-        self.find_crate("std")
-    }
-
     pub fn core(&self) -> Option<Crate> {
         self.find_crate("core")
     }
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index d3464ae17b..bb4e9666b1 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -129,11 +129,3 @@ pub mod prelude {
 }
 #[prelude_import]
 pub use prelude::*;
-//- /libstd.rs crate:std deps:core
-//! Signatures of traits, types and functions from the std lib for use in tests.
-
-/// Docs for return_keyword
-mod return_keyword {}
-
-/// Docs for prim_str
-mod prim_str {}
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index bb2656a80a..8b5cca22c7 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -18,7 +18,6 @@ syntax = { path = "../syntax", version = "0.0.0" }
 parser = { path = "../parser", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
 test_utils = { path = "../test_utils", version = "0.0.0" }
-stdx = { path = "../stdx", version = "0.0.0" }
 
 [dev-dependencies]
 profile = { path = "../profile" }
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 503ad13556..6d81be8804 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -40,12 +40,18 @@ fn benchmark_expand_macro_rules() {
             .into_iter()
             .map(|(id, tt)| {
                 let res = rules[&id].expand(&tt);
-                assert!(res.err.is_none());
+                if res.err.is_some() {
+                    // FIXME:
+                    // Currently `invocation_fixtures` will generate some correct invocations but
+                    // cannot be expanded by mbe. We ignore errors here.
+                    // See: https://github.com/rust-analyzer/rust-analyzer/issues/4777
+                    eprintln!("err from {} {:?}", id, res.err);
+                }
                 res.value.token_trees.len()
             })
             .sum()
     };
-    assert_eq!(hash, 69413);
+    assert_eq!(hash, 66995);
 }
 
 fn macro_rules_fixtures() -> FxHashMap<String, MacroRules> {
@@ -71,7 +77,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree> {
         .collect()
 }
 
-/// Generate random invocation fixtures from rules
+// Generate random invocation fixtures from rules
 fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt::Subtree)> {
     let mut seed = 123456789;
     let mut res = Vec::new();
@@ -80,31 +86,11 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt
         for rule in &it.rules {
             // Generate twice
             for _ in 0..2 {
-                // The input are generated by filling the `Op` randomly.
-                // However, there are some cases generated are ambiguous for expanding, for example:
-                // ```rust
-                // macro_rules! m {
-                //    ($($t:ident),* as $ty:ident) => {}
-                // }
-                // m!(as u32);  // error: local ambiguity: multiple parsing options: built-in NTs ident ('t') or 1 other option.
-                // ```
-                //
-                // So we just skip any error cases and try again
-                let mut try_cnt = 0;
-                loop {
-                    let mut subtree = tt::Subtree::default();
-                    for op in rule.lhs.iter() {
-                        collect_from_op(op, &mut subtree, &mut seed);
-                    }
-                    if it.expand(&subtree).err.is_none() {
-                        res.push((name.clone(), subtree));
-                        break;
-                    }
-                    try_cnt += 1;
-                    if try_cnt > 100 {
-                        panic!("invocaton fixture {} cannot be generated.\n", name);
-                    }
+                let mut subtree = tt::Subtree::default();
+                for op in rule.lhs.iter() {
+                    collect_from_op(op, &mut subtree, &mut seed);
                 }
+                res.push((name.clone(), subtree));
             }
         }
     }
diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs
index 2efff8f52e..e7e14b3cce 100644
--- a/crates/mbe/src/expander.rs
+++ b/crates/mbe/src/expander.rs
@@ -5,7 +5,7 @@
 mod matcher;
 mod transcriber;
 
-use smallvec::SmallVec;
+use rustc_hash::FxHashMap;
 use syntax::SmolStr;
 
 use crate::{ExpandError, ExpandResult};
@@ -28,10 +28,10 @@ pub(crate) fn expand_rules(
                 return ExpandResult::ok(value);
             }
         }
-        // Use the rule if we matched more tokens, or bound variables count
+        // Use the rule if we matched more tokens, or had fewer errors
         if let Some((prev_match, _)) = &match_ {
-            if (new_match.unmatched_tts, -(new_match.bound_count as i32))
-                < (prev_match.unmatched_tts, -(prev_match.bound_count as i32))
+            if (new_match.unmatched_tts, new_match.err_count)
+                < (prev_match.unmatched_tts, prev_match.err_count)
             {
                 match_ = Some((new_match, rule));
             }
@@ -94,19 +94,19 @@ pub(crate) fn expand_rules(
 /// In other words, `Bindings` is a *multi* mapping from `SmolStr` to
 /// `tt::TokenTree`, where the index to select a particular `TokenTree` among
 /// many is not a plain `usize`, but an `&[usize]`.
-#[derive(Debug, Default, Clone, PartialEq, Eq)]
+#[derive(Debug, Default)]
 struct Bindings {
-    inner: SmallVec<[(SmolStr, Binding); 4]>,
+    inner: FxHashMap<SmolStr, Binding>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug)]
 enum Binding {
     Fragment(Fragment),
     Nested(Vec<Binding>),
     Empty,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone)]
 enum Fragment {
     /// token fragments are just copy-pasted into the output
     Tokens(tt::TokenTree),
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 9d3d280552..e3bd4c09a0 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -1,74 +1,14 @@
-//! An NFA-based parser, which is porting from rustc mbe parsing code
-//!
-//! See https://github.com/rust-lang/rust/blob/70b18bc2cbac4712020019f5bf57c00905373205/compiler/rustc_expand/src/mbe/macro_parser.rs
-//! Here is a quick intro to how the parser works, copied from rustc:
-//!
-//! A 'position' is a dot in the middle of a matcher, usually represented as a
-//! dot. For example `· a $( a )* a b` is a position, as is `a $( · a )* a b`.
-//!
-//! The parser walks through the input a character at a time, maintaining a list
-//! of threads consistent with the current position in the input string: `cur_items`.
-//!
-//! As it processes them, it fills up `eof_items` with threads that would be valid if
-//! the macro invocation is now over, `bb_items` with threads that are waiting on
-//! a Rust non-terminal like `$e:expr`, and `next_items` with threads that are waiting
-//! on a particular token. Most of the logic concerns moving the · through the
-//! repetitions indicated by Kleene stars. The rules for moving the · without
-//! consuming any input are called epsilon transitions. It only advances or calls
-//! out to the real Rust parser when no `cur_items` threads remain.
-//!
-//! Example:
-//!
-//! ```text, ignore
-//! Start parsing a a a a b against [· a $( a )* a b].
-//!
-//! Remaining input: a a a a b
-//! next: [· a $( a )* a b]
-//!
-//! - - - Advance over an a. - - -
-//!
-//! Remaining input: a a a b
-//! cur: [a · $( a )* a b]
-//! Descend/Skip (first item).
-//! next: [a $( · a )* a b]  [a $( a )* · a b].
-//!
-//! - - - Advance over an a. - - -
-//!
-//! Remaining input: a a b
-//! cur: [a $( a · )* a b]  [a $( a )* a · b]
-//! Follow epsilon transition: Finish/Repeat (first item)
-//! next: [a $( a )* · a b]  [a $( · a )* a b]  [a $( a )* a · b]
-//!
-//! - - - Advance over an a. - - - (this looks exactly like the last step)
-//!
-//! Remaining input: a b
-//! cur: [a $( a · )* a b]  [a $( a )* a · b]
-//! Follow epsilon transition: Finish/Repeat (first item)
-//! next: [a $( a )* · a b]  [a $( · a )* a b]  [a $( a )* a · b]
-//!
-//! - - - Advance over an a. - - - (this looks exactly like the last step)
-//!
-//! Remaining input: b
-//! cur: [a $( a · )* a b]  [a $( a )* a · b]
-//! Follow epsilon transition: Finish/Repeat (first item)
-//! next: [a $( a )* · a b]  [a $( · a )* a b]  [a $( a )* a · b]
-//!
-//! - - - Advance over a b. - - -
-//!
-//! Remaining input: ''
-//! eof: [a $( a )* a b ·]
-//! ```
+//! FIXME: write short doc here
 
 use crate::{
     expander::{Binding, Bindings, Fragment},
-    parser::{Op, OpDelimited, OpDelimitedIter, RepeatKind, Separator},
+    parser::{Op, RepeatKind, Separator},
     tt_iter::TtIter,
     ExpandError, MetaTemplate,
 };
 
 use super::ExpandResult;
 use parser::FragmentKind::*;
-use smallvec::{smallvec, SmallVec};
 use syntax::SmolStr;
 
 impl Bindings {
@@ -76,19 +16,19 @@ impl Bindings {
         // FIXME: Do we have a better way to represent an empty token ?
         // Insert an empty subtree for empty token
         let tt = tt::Subtree::default().into();
-        self.inner.push((name.clone(), Binding::Fragment(Fragment::Tokens(tt))));
+        self.inner.insert(name.clone(), Binding::Fragment(Fragment::Tokens(tt)));
     }
 
     fn push_empty(&mut self, name: &SmolStr) {
-        self.inner.push((name.clone(), Binding::Empty));
+        self.inner.insert(name.clone(), Binding::Empty);
     }
 
     fn push_nested(&mut self, idx: usize, nested: Bindings) -> Result<(), ExpandError> {
         for (key, value) in nested.inner {
-            if self.get_mut(&key).is_none() {
-                self.inner.push((key.clone(), Binding::Nested(Vec::new())));
+            if !self.inner.contains_key(&key) {
+                self.inner.insert(key.clone(), Binding::Nested(Vec::new()));
             }
-            match self.get_mut(&key) {
+            match self.inner.get_mut(&key) {
                 Some(Binding::Nested(it)) => {
                     // insert empty nested bindings before this one
                     while it.len() < idx {
@@ -106,14 +46,6 @@ impl Bindings {
         }
         Ok(())
     }
-
-    fn get_mut(&mut self, name: &str) -> Option<&mut Binding> {
-        self.inner.iter_mut().find_map(|(n, b)| if n == name { Some(b) } else { None })
-    }
-
-    fn bindings(&self) -> impl Iterator<Item = &Binding> {
-        self.inner.iter().map(|(_, b)| b)
-    }
 }
 
 macro_rules! err {
@@ -125,7 +57,7 @@ macro_rules! err {
     };
 }
 
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Debug, Default)]
 pub(super) struct Match {
     pub(super) bindings: Bindings,
     /// We currently just keep the first error and count the rest to compare matches.
@@ -133,8 +65,6 @@ pub(super) struct Match {
     pub(super) err_count: usize,
     /// How many top-level token trees were left to match.
     pub(super) unmatched_tts: usize,
-    /// The number of bound variables
-    pub(super) bound_count: usize,
 }
 
 impl Match {
@@ -146,373 +76,72 @@ impl Match {
 }
 
 /// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
-    let mut res = match_loop(pattern, &input);
-    res.bound_count = count(res.bindings.bindings());
-    return res;
-
-    fn count<'a>(bindings: impl Iterator<Item = &'a Binding>) -> usize {
-        bindings
-            .map(|it| match it {
-                Binding::Fragment(_) => 1,
-                Binding::Empty => 1,
-                Binding::Nested(it) => count(it.iter()),
-            })
-            .sum()
-    }
-}
-
-#[derive(Debug, Clone)]
-struct MatchState<'t> {
-    /// The position of the "dot" in this matcher
-    dot: OpDelimitedIter<'t>,
-
-    /// Token subtree stack
-    /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. )
-    /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does
-    /// that where the bottom of the stack is the outermost matcher.
-    stack: SmallVec<[OpDelimitedIter<'t>; 4]>,
-
-    /// The "parent" matcher position if we are in a repetition. That is, the matcher position just
-    /// before we enter the repetition.
-    up: Option<Box<MatchState<'t>>>,
-
-    /// The separator if we are in a repetition.
-    sep: Option<Separator>,
-
-    /// The KleeneOp of this sequence if we are in a repetition.
-    sep_kind: Option<RepeatKind>,
-
-    /// Number of tokens of seperator parsed
-    sep_parsed: Option<usize>,
-
-    /// Matched meta variables bindings
-    bindings: SmallVec<[Bindings; 4]>,
-
-    /// Cached result of meta variable parsing
-    meta_result: Option<(TtIter<'t>, ExpandResult<Option<Fragment>>)>,
+pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
+    let mut res = Match::default();
+    let mut src = TtIter::new(src);
 
-    /// Is error occuried in this state, will `poised` to "parent"
-    is_error: bool,
-}
+    match_tokens(&mut res, pattern, &mut src);
 
-/// Process the matcher positions of `cur_items` until it is empty. In the process, this will
-/// produce more items in `next_items`, `eof_items`, and `bb_items`.
-///
-/// For more info about the how this happens, see the module-level doc comments and the inline
-/// comments of this function.
-///
-/// # Parameters
-///
-/// - `src`: the current token of the parser.
-/// - `stack`: the "parent" frames of the token tree
-/// - `res`: the match result to store errors
-/// - `cur_items`: the set of current items to be processed. This should be empty by the end of a
-///   successful execution of this function.
-/// - `next_items`: the set of newly generated items. These are used to replenish `cur_items` in
-///   the function `parse`.
-/// - `eof_items`: the set of items that would be valid if this was the EOF.
-/// - `bb_items`: the set of items that are waiting for the black-box parser.
-/// - `error_items`: the set of items in errors, used for error-resilient parsing
-fn match_loop_inner<'t>(
-    src: TtIter<'t>,
-    stack: &[TtIter<'t>],
-    res: &mut Match,
-    cur_items: &mut SmallVec<[MatchState<'t>; 1]>,
-    bb_items: &mut SmallVec<[MatchState<'t>; 1]>,
-    next_items: &mut Vec<MatchState<'t>>,
-    eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
-    error_items: &mut SmallVec<[MatchState<'t>; 1]>,
-) {
-    macro_rules! try_push {
-        ($items: expr, $it:expr) => {
-            if $it.is_error {
-                error_items.push($it);
-            } else {
-                $items.push($it);
-            }
-        };
+    if src.len() > 0 {
+        res.unmatched_tts += src.len();
+        res.add_err(err!("leftover tokens"));
     }
 
-    while let Some(mut item) = cur_items.pop() {
-        while item.dot.is_eof() {
-            match item.stack.pop() {
-                Some(frame) => {
-                    item.dot = frame;
-                    item.dot.next();
-                }
-                None => break,
-            }
-        }
-        let op = match item.dot.peek() {
-            None => {
-                // We are at or past the end of the matcher of `item`.
-                if item.up.is_some() {
-                    if item.sep_parsed.is_none() {
-                        // Get the `up` matcher
-                        let mut new_pos = *item.up.clone().unwrap();
-                        // Add matches from this repetition to the `matches` of `up`
-                        if let Some(bindings) = new_pos.bindings.last_mut() {
-                            for (i, b) in item.bindings.iter_mut().enumerate() {
-                                bindings.push_nested(i, b.clone()).unwrap();
-                            }
-                        }
-                        // Move the "dot" past the repetition in `up`
-                        new_pos.dot.next();
-                        new_pos.is_error = new_pos.is_error || item.is_error;
-                        cur_items.push(new_pos);
-                    }
-
-                    // Check if we need a separator.
-                    // We check the separator one by one
-                    let sep_idx = *item.sep_parsed.as_ref().unwrap_or(&0);
-                    let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
-                    if item.sep.is_some() && sep_idx != sep_len {
-                        let sep = item.sep.as_ref().unwrap();
-                        if src.clone().expect_separator(&sep, sep_idx) {
-                            item.dot.next();
-                            item.sep_parsed = Some(sep_idx + 1);
-                            try_push!(next_items, item);
-                        }
-                    }
-                    // We don't need a separator. Move the "dot" back to the beginning of the matcher
-                    // and try to match again UNLESS we are only allowed to have _one_ repetition.
-                    else if item.sep_kind != Some(RepeatKind::ZeroOrOne) {
-                        item.dot = item.dot.reset();
-                        item.sep_parsed = None;
-                        item.bindings.push(Bindings::default());
-                        cur_items.push(item);
-                    }
-                } else {
-                    // If we are not in a repetition, then being at the end of a matcher means that we have
-                    // reached the potential end of the input.
-                    try_push!(eof_items, item);
-                }
-                continue;
-            }
-            Some(it) => it,
-        };
+    res
+}
 
-        // We are in the middle of a matcher.
+fn match_tokens(res: &mut Match, pattern: &MetaTemplate, src: &mut TtIter) {
+    for op in pattern.iter() {
         match op {
-            OpDelimited::Op(Op::Repeat { tokens, kind, separator }) => {
-                if matches!(kind, RepeatKind::ZeroOrMore | RepeatKind::ZeroOrOne) {
-                    let mut new_item = item.clone();
-                    new_item.dot.next();
-                    let mut vars = Vec::new();
-                    let bindings = new_item.bindings.last_mut().unwrap();
-                    collect_vars(&mut vars, tokens);
-                    for var in vars {
-                        bindings.push_empty(&var);
-                    }
-                    cur_items.push(new_item);
-                }
-                cur_items.push(MatchState {
-                    dot: tokens.iter_delimited(None),
-                    stack: Default::default(),
-                    up: Some(Box::new(item)),
-                    sep: separator.clone(),
-                    sep_kind: Some(*kind),
-                    sep_parsed: None,
-                    bindings: smallvec![Bindings::default()],
-                    meta_result: None,
-                    is_error: false,
-                })
-            }
-            OpDelimited::Op(Op::Subtree { tokens, delimiter }) => {
-                if let Ok(subtree) = src.clone().expect_subtree() {
-                    if subtree.delimiter_kind() == delimiter.map(|it| it.kind) {
-                        item.stack.push(item.dot);
-                        item.dot = tokens.iter_delimited(delimiter.as_ref());
-                        cur_items.push(item);
-                    }
-                }
-            }
-            OpDelimited::Op(Op::Var { kind, name, .. }) => {
-                if let Some(kind) = kind {
-                    let mut fork = src.clone();
-                    let match_res = match_meta_var(kind.as_str(), &mut fork);
-                    match match_res.err {
-                        None => {
-                            // Some meta variables are optional (e.g. vis)
-                            if match_res.value.is_some() {
-                                item.meta_result = Some((fork, match_res));
-                                try_push!(bb_items, item);
-                            } else {
-                                item.bindings.last_mut().unwrap().push_optional(name);
-                                item.dot.next();
-                                cur_items.push(item);
-                            }
-                        }
-                        Some(err) => {
-                            res.add_err(err);
-                            match match_res.value {
-                                Some(fragment) => {
-                                    item.bindings
-                                        .last_mut()
-                                        .unwrap()
-                                        .inner
-                                        .push((name.clone(), Binding::Fragment(fragment)));
-                                }
-                                _ => {}
-                            }
-                            item.is_error = true;
-                            error_items.push(item);
-                        }
-                    }
-                }
-            }
-            OpDelimited::Op(Op::Leaf(leaf)) => {
-                if let Err(err) = match_leaf(&leaf, &mut src.clone()) {
+            Op::Leaf(lhs) => {
+                if let Err(err) = match_leaf(lhs, src) {
                     res.add_err(err);
-                    item.is_error = true;
-                } else {
-                    item.dot.next();
+                    continue;
                 }
-                try_push!(next_items, item);
             }
-            OpDelimited::Open => {
-                if matches!(src.clone().next(), Some(tt::TokenTree::Subtree(..))) {
-                    item.dot.next();
-                    try_push!(next_items, item);
-                }
-            }
-            OpDelimited::Close => {
-                let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty();
-                if is_delim_closed {
-                    item.dot.next();
-                    try_push!(next_items, item);
-                }
-            }
-        }
-    }
-}
-
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
-    let mut src = TtIter::new(src);
-    let mut stack: SmallVec<[TtIter; 1]> = SmallVec::new();
-    let mut res = Match::default();
-    let mut error_reover_item = None;
-
-    let mut cur_items = smallvec![MatchState {
-        dot: pattern.iter_delimited(None),
-        stack: Default::default(),
-        up: None,
-        sep: None,
-        sep_kind: None,
-        sep_parsed: None,
-        bindings: smallvec![Bindings::default()],
-        is_error: false,
-        meta_result: None,
-    }];
-
-    let mut next_items = vec![];
-
-    loop {
-        let mut bb_items = SmallVec::new();
-        let mut eof_items = SmallVec::new();
-        let mut error_items = SmallVec::new();
-
-        stdx::always!(next_items.is_empty());
-
-        match_loop_inner(
-            src.clone(),
-            &stack,
-            &mut res,
-            &mut cur_items,
-            &mut bb_items,
-            &mut next_items,
-            &mut eof_items,
-            &mut error_items,
-        );
-        stdx::always!(cur_items.is_empty());
-
-        if error_items.len() > 0 {
-            error_reover_item = error_items.pop();
-        } else if eof_items.len() > 0 {
-            error_reover_item = Some(eof_items[0].clone());
-        }
-
-        // We need to do some post processing after the `match_loop_inner`.
-        // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
-        // either the parse is ambiguous (which should never happen) or there is a syntax error.
-        if src.peek_n(0).is_none() && stack.is_empty() {
-            if eof_items.len() == 1 {
-                // remove all errors, because it is the correct answer !
-                res = Match::default();
-                res.bindings = eof_items[0].bindings[0].clone();
-            } else {
-                // Error recovery
-                if error_reover_item.is_some() {
-                    res.bindings = error_reover_item.unwrap().bindings[0].clone();
+            Op::Subtree { tokens, delimiter: delim } => {
+                let rhs = match src.expect_subtree() {
+                    Ok(s) => s,
+                    Err(()) => {
+                        res.add_err(err!("expected subtree"));
+                        continue;
+                    }
+                };
+                if delim.map(|it| it.kind) != rhs.delimiter_kind() {
+                    res.add_err(err!("mismatched delimiter"));
+                    continue;
                 }
-                res.add_err(ExpandError::UnexpectedToken);
-            }
-            return res;
-        }
-
-        // If there are no possible next positions AND we aren't waiting for the black-box parser,
-        // then there is a syntax error.
-        //
-        // Another possibility is that we need to call out to parse some rust nonterminal
-        // (black-box) parser. However, if there is not EXACTLY ONE of these, something is wrong.
-        if (bb_items.is_empty() && next_items.is_empty())
-            || (!bb_items.is_empty() && !next_items.is_empty())
-            || bb_items.len() > 1
-        {
-            res.unmatched_tts += src.len();
-            while let Some(it) = stack.pop() {
-                src = it;
-                res.unmatched_tts += src.len();
-            }
-            res.add_err(err!("leftover tokens"));
-
-            if let Some(mut error_reover_item) = error_reover_item {
-                res.bindings = error_reover_item.bindings.remove(0);
-            }
-            return res;
-        }
-        // Dump all possible `next_items` into `cur_items` for the next iteration.
-        else if !next_items.is_empty() {
-            // Now process the next token
-            cur_items.extend(next_items.drain(..));
-
-            match src.next() {
-                Some(tt::TokenTree::Subtree(subtree)) => {
-                    stack.push(src.clone());
-                    src = TtIter::new(subtree);
+                let mut src = TtIter::new(rhs);
+                match_tokens(res, tokens, &mut src);
+                if src.len() > 0 {
+                    res.add_err(err!("leftover tokens"));
                 }
-                None if !stack.is_empty() => src = stack.pop().unwrap(),
-                _ => (),
             }
-        }
-        // Finally, we have the case where we need to call the black-box parser to get some
-        // nonterminal.
-        else {
-            stdx::always!(bb_items.len() == 1);
-            let mut item = bb_items.pop().unwrap();
-
-            if let Some(OpDelimited::Op(Op::Var { name, .. })) = item.dot.peek() {
-                let (iter, match_res) = item.meta_result.take().unwrap();
-                let bindings = item.bindings.last_mut().unwrap();
-                match match_res.value {
+            Op::Var { name, kind, .. } => {
+                let kind = match kind {
+                    Some(k) => k,
+                    None => {
+                        res.add_err(ExpandError::UnexpectedToken);
+                        continue;
+                    }
+                };
+                let ExpandResult { value: matched, err: match_err } =
+                    match_meta_var(kind.as_str(), src);
+                match matched {
                     Some(fragment) => {
-                        bindings.inner.push((name.clone(), Binding::Fragment(fragment)));
+                        res.bindings.inner.insert(name.clone(), Binding::Fragment(fragment));
                     }
-                    None if match_res.err.is_none() => bindings.push_optional(name),
+                    None if match_err.is_none() => res.bindings.push_optional(name),
                     _ => {}
                 }
-                if let Some(err) = match_res.err {
+                if let Some(err) = match_err {
                     res.add_err(err);
                 }
-                src = iter.clone();
-                item.dot.next();
-            } else {
-                unreachable!()
             }
-            cur_items.push(item);
+            Op::Repeat { tokens: subtree, kind, separator } => {
+                match_repeat(res, subtree, *kind, separator, src);
+            }
         }
-        stdx::always!(!cur_items.is_empty());
     }
 }
 
@@ -544,6 +173,73 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
     Ok(())
 }
 
+fn match_repeat(
+    res: &mut Match,
+    pattern: &MetaTemplate,
+    kind: RepeatKind,
+    separator: &Option<Separator>,
+    src: &mut TtIter,
+) {
+    // Dirty hack to make macro-expansion terminate.
+    // This should be replaced by a proper macro-by-example implementation
+    let mut limit = 65536;
+    let mut counter = 0;
+
+    for i in 0.. {
+        let mut fork = src.clone();
+
+        if let Some(separator) = &separator {
+            if i != 0 && !fork.eat_separator(separator) {
+                break;
+            }
+        }
+
+        let mut nested = Match::default();
+        match_tokens(&mut nested, pattern, &mut fork);
+        if nested.err.is_none() {
+            limit -= 1;
+            if limit == 0 {
+                log::warn!(
+                    "match_lhs exceeded repeat pattern limit => {:#?}\n{:#?}\n{:#?}\n{:#?}",
+                    pattern,
+                    src,
+                    kind,
+                    separator
+                );
+                break;
+            }
+            *src = fork;
+
+            if let Err(err) = res.bindings.push_nested(counter, nested.bindings) {
+                res.add_err(err);
+            }
+            counter += 1;
+            if counter == 1 {
+                if let RepeatKind::ZeroOrOne = kind {
+                    break;
+                }
+            }
+        } else {
+            break;
+        }
+    }
+
+    match (kind, counter) {
+        (RepeatKind::OneOrMore, 0) => {
+            res.add_err(ExpandError::UnexpectedToken);
+        }
+        (_, 0) => {
+            // Collect all empty variables in subtrees
+            let mut vars = Vec::new();
+            collect_vars(&mut vars, pattern);
+            for var in vars {
+                res.bindings.push_empty(&var)
+            }
+        }
+        _ => (),
+    }
+}
+
 fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> {
     let fragment = match kind {
         "path" => Path,
@@ -607,14 +303,14 @@ fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) {
 }
 
 impl<'a> TtIter<'a> {
-    fn expect_separator(&mut self, separator: &Separator, idx: usize) -> bool {
+    fn eat_separator(&mut self, separator: &Separator) -> bool {
         let mut fork = self.clone();
         let ok = match separator {
-            Separator::Ident(lhs) if idx == 0 => match fork.expect_ident() {
+            Separator::Ident(lhs) => match fork.expect_ident() {
                 Ok(rhs) => rhs.text == lhs.text,
                 _ => false,
             },
-            Separator::Literal(lhs) if idx == 0 => match fork.expect_literal() {
+            Separator::Literal(lhs) => match fork.expect_literal() {
                 Ok(rhs) => match rhs {
                     tt::Leaf::Literal(rhs) => rhs.text == lhs.text,
                     tt::Leaf::Ident(rhs) => rhs.text == lhs.text,
@@ -622,11 +318,10 @@ impl<'a> TtIter<'a> {
                 },
                 _ => false,
             },
-            Separator::Puncts(lhss) if idx < lhss.len() => match fork.expect_punct() {
-                Ok(rhs) => rhs.char == lhss[idx].char,
+            Separator::Puncts(lhss) => lhss.iter().all(|lhs| match fork.expect_punct() {
+                Ok(rhs) => rhs.char == lhs.char,
                 _ => false,
-            },
-            _ => false,
+            }),
         };
         if ok {
             *self = fork;
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index ad9953a7d2..78368a33e5 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -13,17 +13,13 @@ use crate::{
 
 impl Bindings {
     fn contains(&self, name: &str) -> bool {
-        self.inner.iter().any(|(n, _)| n == name)
+        self.inner.contains_key(name)
     }
 
     fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
-        let mut b: &Binding = self
-            .inner
-            .iter()
-            .find_map(|(n, b)| if n == name { Some(b) } else { None })
-            .ok_or_else(|| {
-                ExpandError::BindingError(format!("could not find binding `{}`", name))
-            })?;
+        let mut b = self.inner.get(name).ok_or_else(|| {
+            ExpandError::BindingError(format!("could not find binding `{}`", name))
+        })?;
         for nesting_state in nesting.iter_mut() {
             nesting_state.hit = true;
             b = match b {
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index f3d2da55a7..4c298f85fb 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -21,7 +21,7 @@ use test_utils::mark;
 pub use tt::{Delimiter, DelimiterKind, Punct};
 
 use crate::{
-    parser::{parse_pattern, parse_template, MetaTemplate, Op},
+    parser::{parse_pattern, parse_template, Op},
     tt_iter::TtIter,
 };
 
@@ -94,6 +94,15 @@ struct Rule {
     rhs: MetaTemplate,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct MetaTemplate(Vec<Op>);
+
+impl<'a> MetaTemplate {
+    fn iter(&self) -> impl Iterator<Item = &Op> {
+        self.0.iter()
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 struct Shift(u32);
 
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 8671322e1f..f891ec29c6 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -5,75 +5,7 @@ use smallvec::SmallVec;
 use syntax::SmolStr;
 use tt::Delimiter;
 
-use crate::{tt_iter::TtIter, ParseError};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) struct MetaTemplate(pub(crate) Vec<Op>);
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum OpDelimited<'a> {
-    Op(&'a Op),
-    Open,
-    Close,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) struct OpDelimitedIter<'a> {
-    inner: &'a Vec<Op>,
-    delimited: Option<&'a Delimiter>,
-    idx: usize,
-}
-
-impl<'a> OpDelimitedIter<'a> {
-    pub(crate) fn is_eof(&self) -> bool {
-        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
-        self.idx >= len
-    }
-
-    pub(crate) fn peek(&self) -> Option<OpDelimited<'a>> {
-        match self.delimited {
-            None => self.inner.get(self.idx).map(OpDelimited::Op),
-            Some(_) => match self.idx {
-                0 => Some(OpDelimited::Open),
-                i if i == self.inner.len() + 1 => Some(OpDelimited::Close),
-                i => self.inner.get(i - 1).map(OpDelimited::Op),
-            },
-        }
-    }
-
-    pub(crate) fn reset(&self) -> Self {
-        Self { inner: &self.inner, idx: 0, delimited: self.delimited }
-    }
-}
-
-impl<'a> Iterator for OpDelimitedIter<'a> {
-    type Item = OpDelimited<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let res = self.peek();
-        self.idx += 1;
-        res
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.inner.len() + if self.delimited.is_some() { 2 } else { 0 };
-        let remain = len.checked_sub(self.idx).unwrap_or(0);
-        (remain, Some(remain))
-    }
-}
-
-impl<'a> MetaTemplate {
-    pub(crate) fn iter(&self) -> impl Iterator<Item = &Op> {
-        self.0.iter()
-    }
-
-    pub(crate) fn iter_delimited(
-        &'a self,
-        delimited: Option<&'a Delimiter>,
-    ) -> OpDelimitedIter<'a> {
-        OpDelimitedIter { inner: &self.0, idx: 0, delimited }
-    }
-}
+use crate::{tt_iter::TtIter, MetaTemplate, ParseError};
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum Op {
@@ -115,16 +47,6 @@ impl PartialEq for Separator {
     }
 }
 
-impl Separator {
-    pub(crate) fn tt_count(&self) -> usize {
-        match self {
-            Separator::Literal(_) => 1,
-            Separator::Ident(_) => 1,
-            Separator::Puncts(it) => it.len(),
-        }
-    }
-}
-
 pub(crate) fn parse_template(template: &tt::Subtree) -> Result<Vec<Op>, ParseError> {
     parse_inner(&template, Mode::Template).into_iter().collect()
 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c1ca88df67..70cb7fbab5 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -474,7 +474,7 @@ pub(crate) fn folding_range(
     let kind = match fold.kind {
         FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
         FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
-        FoldKind::Mods | FoldKind::Block | FoldKind::ArgList | FoldKind::Region => None,
+        FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None,
     };
 
     let range = range(line_index, fold.range);
diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs
index 72de92c649..eb58b32745 100644
--- a/xtask/src/metrics.rs
+++ b/xtask/src/metrics.rs
@@ -80,7 +80,7 @@ impl Metrics {
     fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
         eprintln!("\nMeasuring analysis-stats/{}", name);
         let output =
-            cmd!("./target/release/rust-analyzer --quiet analysis-stats --memory-usage {path}")
+            cmd!("./target/release/rust-analyzer analysis-stats --quiet --memory-usage {path}")
                 .read()?;
         for (metric, value, unit) in parse_metrics(&output) {
             self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());


Test patch:
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 66820058b6..b7f6167607 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -12,7 +12,7 @@ use ide_db::{
     RootDatabase,
 };
 use stdx::{format_to, trim_indent};
-use syntax::{ast, AstNode, TextRange};
+use syntax::TextRange;
 use test_utils::{assert_eq_text, extract_offset};
 
 use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
@@ -180,50 +180,6 @@ fn labels(assists: &[Assist]) -> String {
     labels.into_iter().collect::<String>()
 }
 
-pub(crate) type NameSuggestion = fn(&ast::Expr, &Semantics<'_, RootDatabase>) -> Option<String>;
-
-#[track_caller]
-pub(crate) fn check_name_suggestion(
-    suggestion: NameSuggestion,
-    ra_fixture: &str,
-    suggested_name: &str,
-) {
-    check_name(suggestion, ra_fixture, Some(suggested_name));
-}
-
-#[track_caller]
-pub(crate) fn check_name_suggestion_not_applicable(suggestion: NameSuggestion, ra_fixture: &str) {
-    check_name(suggestion, ra_fixture, None);
-}
-
-#[track_caller]
-fn check_name(suggestion: NameSuggestion, ra_fixture: &str, expected: Option<&str>) {
-    let (db, file_with_carret_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
-    let frange = FileRange { file_id: file_with_carret_id, range: range_or_offset.into() };
-
-    let sema = Semantics::new(&db);
-    let source_file = sema.parse(frange.file_id);
-    let element = source_file.syntax().covering_element(frange.range);
-    let expr =
-        element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
-    assert_eq!(
-        expr.syntax().text_range(),
-        frange.range,
-        "selection is not an expression(yet contained in one)"
-    );
-
-    let name = suggestion(&expr, &sema);
-
-    match (name, expected) {
-        (Some(name), Some(expected_name)) => {
-            assert_eq_text!(&name, expected_name);
-        }
-        (Some(_), None) => panic!("name suggestion should not be applicable"),
-        (None, Some(_)) => panic!("name suggestion is not applicable"),
-        (None, None) => (),
-    }
-}
-
 #[test]
 fn assist_order_field_struct() {
     let before = "struct Foo { $0bar: u32 }";
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 5c641ebf28..f1eadcd1ee 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -456,17 +456,6 @@ fn test_match_group_with_multichar_sep() {
     .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}");
 }
 
-#[test]
-fn test_match_group_with_multichar_sep2() {
-    parse_macro(
-        r#"
-        macro_rules! foo {
-            (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} );
-        }"#,
-    )
-    .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}");
-}
-
 #[test]
 fn test_match_group_zero_match() {
     parse_macro(
@@ -1278,18 +1267,6 @@ macro_rules! m {
     .is_some());
 }
 
-#[test]
-fn test_match_is_not_greedy() {
-    parse_macro(
-        r#"
-macro_rules! foo {
-    ($($i:ident $(,)*),*) => {};
-}
-"#,
-    )
-    .assert_expand_items(r#"foo!(a,b);"#, r#""#);
-}
-
 // The following tests are based on real world situations
 #[test]
 fn test_vec() {