Repository: rust-analyzer/rust-analyzer
Pull request #11170 (2022-01-02 21:23:51)

minor: Cleanup syntax highlighting
bors r+


Patch:
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index a908cdb718..28c7c546ff 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -265,6 +265,7 @@ fn traverse(
         }
 
         let element = match event {
+            WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind() == WHITESPACE => continue,
             WalkEvent::Enter(it) => it,
             WalkEvent::Leave(NodeOrToken::Token(_)) => continue,
             WalkEvent::Leave(NodeOrToken::Node(node)) => {
@@ -347,13 +348,16 @@ fn traverse(
         }
 
         // do the normal highlighting
-        let element = highlight::element(
-            sema,
-            krate,
-            &mut bindings_shadow_count,
-            syntactic_name_ref_highlighting,
-            element_to_highlight,
-        );
+        let element = match element_to_highlight {
+            NodeOrToken::Node(node) => highlight::node(
+                sema,
+                krate,
+                &mut bindings_shadow_count,
+                syntactic_name_ref_highlighting,
+                node,
+            ),
+            NodeOrToken::Token(token) => highlight::token(sema, krate, token).zip(Some(None)),
+        };
         if let Some((mut highlight, binding_hash)) = element {
             if inside_attribute {
                 highlight |= HlMod::Attribute
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 3b322bd525..4a09d341f7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -8,7 +8,7 @@ use ide_db::{
 };
 use rustc_hash::FxHashMap;
 use syntax::{
-    ast, match_ast, AstNode, AstToken, NodeOrToken, SyntaxElement,
+    ast, match_ast, AstNode, AstToken, NodeOrToken,
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, T,
 };
@@ -18,174 +18,46 @@ use crate::{
     Highlight, HlMod, HlTag,
 };
 
-pub(super) fn element(
-    sema: &Semantics<RootDatabase>,
-    krate: Option<hir::Crate>,
-    bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
-    syntactic_name_ref_highlighting: bool,
-    element: SyntaxElement,
-) -> Option<(Highlight, Option<u64>)> {
-    match element {
-        NodeOrToken::Node(it) => {
-            node(sema, krate, bindings_shadow_count, syntactic_name_ref_highlighting, it)
-        }
-        NodeOrToken::Token(it) => Some((token(sema, krate, it)?, None)),
-    }
-}
-
-fn token(
+pub(super) fn token(
     sema: &Semantics<RootDatabase>,
     krate: Option<hir::Crate>,
     token: SyntaxToken,
 ) -> Option<Highlight> {
-    let highlight: Highlight = if let Some(comment) = ast::Comment::cast(token.clone()) {
+    if let Some(comment) = ast::Comment::cast(token.clone()) {
         let h = HlTag::Comment;
-        match comment.kind().doc {
+        return Some(match comment.kind().doc {
             Some(_) => h | HlMod::Documentation,
             None => h.into(),
+        });
+    }
+
+    let highlight: Highlight = match token.kind() {
+        STRING | BYTE_STRING => HlTag::StringLiteral.into(),
+        INT_NUMBER if token.ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
+            SymbolKind::Field.into()
         }
-    } else {
-        match token.kind() {
-            STRING | BYTE_STRING => HlTag::StringLiteral.into(),
-            INT_NUMBER if token.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => {
-                SymbolKind::Field.into()
-            }
-            INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
-            BYTE => HlTag::ByteLiteral.into(),
-            CHAR => HlTag::CharLiteral.into(),
-            T![?] => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
-            IDENT if parent_matches::<ast::TokenTree>(&token) => {
-                if let Some(attr) = token.ancestors().nth(2).and_then(ast::Attr::cast) {
+        INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
+        BYTE => HlTag::ByteLiteral.into(),
+        CHAR => HlTag::CharLiteral.into(),
+        IDENT if parent_matches::<ast::TokenTree>(&token) => {
+            match token.ancestors().nth(2).and_then(ast::Attr::cast) {
+                Some(attr) => {
                     match try_resolve_derive_input(sema, &attr, &ast::Ident::cast(token).unwrap()) {
                         Some(res) => highlight_def(sema, krate, Definition::from(res)),
                         None => HlTag::None.into(),
                     }
-                } else {
-                    HlTag::None.into()
-                }
-            }
-            p if p.is_punct() => match p {
-                T![&] if parent_matches::<ast::BinExpr>(&token) => HlOperator::Bitwise.into(),
-                T![&] => {
-                    let h = HlTag::Operator(HlOperator::Other).into();
-                    let is_unsafe = token
-                        .parent()
-                        .and_then(ast::RefExpr::cast)
-                        .map_or(false, |ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
-                    if is_unsafe {
-                        h | HlMod::Unsafe
-                    } else {
-                        h
-                    }
-                }
-                T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
-                    HlOperator::Other.into()
-                }
-                T![!] if parent_matches::<ast::MacroCall>(&token) => HlPunct::MacroBang.into(),
-                T![!] if parent_matches::<ast::NeverType>(&token) => HlTag::BuiltinType.into(),
-                T![!] if parent_matches::<ast::PrefixExpr>(&token) => HlOperator::Logical.into(),
-                T![*] if parent_matches::<ast::PtrType>(&token) => HlTag::Keyword.into(),
-                T![*] if parent_matches::<ast::PrefixExpr>(&token) => {
-                    let prefix_expr = token.parent().and_then(ast::PrefixExpr::cast)?;
-
-                    let expr = prefix_expr.expr()?;
-                    let ty = sema.type_of_expr(&expr)?.original;
-                    if ty.is_raw_ptr() {
-                        HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
-                    } else if let Some(ast::UnaryOp::Deref) = prefix_expr.op_kind() {
-                        HlOperator::Other.into()
-                    } else {
-                        HlPunct::Other.into()
-                    }
-                }
-                T![-] if parent_matches::<ast::PrefixExpr>(&token) => {
-                    let prefix_expr = token.parent().and_then(ast::PrefixExpr::cast)?;
-
-                    let expr = prefix_expr.expr()?;
-                    match expr {
-                        ast::Expr::Literal(_) => HlTag::NumericLiteral,
-                        _ => HlTag::Operator(HlOperator::Other),
-                    }
-                    .into()
-                }
-                _ if parent_matches::<ast::PrefixExpr>(&token) => HlOperator::Other.into(),
-                T![+] | T![-] | T![*] | T![/] if parent_matches::<ast::BinExpr>(&token) => {
-                    HlOperator::Arithmetic.into()
-                }
-                T![+=] | T![-=] | T![*=] | T![/=] if parent_matches::<ast::BinExpr>(&token) => {
-                    Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
-                }
-                T![|] | T![&] | T![!] | T![^] if parent_matches::<ast::BinExpr>(&token) => {
-                    HlOperator::Bitwise.into()
-                }
-                T![|=] | T![&=] | T![^=] if parent_matches::<ast::BinExpr>(&token) => {
-                    Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
-                }
-                T![&&] | T![||] if parent_matches::<ast::BinExpr>(&token) => {
-                    HlOperator::Logical.into()
-                }
-                T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
-                    if parent_matches::<ast::BinExpr>(&token) =>
-                {
-                    HlOperator::Comparison.into()
-                }
-                _ if parent_matches::<ast::BinExpr>(&token) => HlOperator::Other.into(),
-                _ if parent_matches::<ast::RangeExpr>(&token) => HlOperator::Other.into(),
-                _ if parent_matches::<ast::RangePat>(&token) => HlOperator::Other.into(),
-                _ if parent_matches::<ast::RestPat>(&token) => HlOperator::Other.into(),
-                _ if parent_matches::<ast::Attr>(&token) => HlTag::AttributeBracket.into(),
-                kind => match kind {
-                    T!['['] | T![']'] => HlPunct::Bracket,
-                    T!['{'] | T!['}'] => HlPunct::Brace,
-                    T!['('] | T![')'] => HlPunct::Parenthesis,
-                    T![<] | T![>] => HlPunct::Angle,
-                    T![,] => HlPunct::Comma,
-                    T![:] => HlPunct::Colon,
-                    T![;] => HlPunct::Semi,
-                    T![.] => HlPunct::Dot,
-                    _ => HlPunct::Other,
-                }
-                .into(),
-            },
-            k if k.is_keyword() => {
-                let h = Highlight::new(HlTag::Keyword);
-                match k {
-                    T![await] => h | HlMod::Async | HlMod::ControlFlow,
-                    T![break]
-                    | T![continue]
-                    | T![else]
-                    | T![if]
-                    | T![in]
-                    | T![loop]
-                    | T![match]
-                    | T![return]
-                    | T![while]
-                    | T![yield] => h | HlMod::ControlFlow,
-                    T![for] if !is_child_of_impl(&token) => h | HlMod::ControlFlow,
-                    T![unsafe] => h | HlMod::Unsafe,
-                    T![true] | T![false] => HlTag::BoolLiteral.into(),
-                    // crate is handled just as a token if it's in an `extern crate`
-                    T![crate] if parent_matches::<ast::ExternCrate>(&token) => h,
-                    // self, crate and super are handled as either a Name or NameRef already
-                    T![self] | T![crate] | T![super] => return None,
-                    T![ref] => token
-                        .parent()
-                        .and_then(ast::IdentPat::cast)
-                        .and_then(|ident_pat| {
-                            (sema.is_unsafe_ident_pat(&ident_pat)).then(|| HlMod::Unsafe)
-                        })
-                        .map_or(h, |modifier| h | modifier),
-                    T![async] => h | HlMod::Async,
-                    _ => h,
                 }
+                None => HlTag::None.into(),
             }
-            _ => return None,
         }
+        p if p.is_punct() => punctuation(sema, token, p),
+        k if k.is_keyword() => keyword(sema, token, k)?,
+        _ => return None,
     };
     Some(highlight)
 }
 
-fn node(
+pub(super) fn node(
     sema: &Semantics<RootDatabase>,
     krate: Option<hir::Crate>,
     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
@@ -195,18 +67,6 @@ fn node(
     let mut binding_hash = None;
     let highlight = match_ast! {
         match node {
-            ast::Fn(__) => {
-                bindings_shadow_count.clear();
-                return None;
-            },
-            ast::Attr(__) => {
-                HlTag::AttributeBracket.into()
-            },
-            // Highlight definitions depending on the "type" of the definition.
-            ast::Name(name) => {
-                highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name)
-            },
-            // Highlight references like the definitions they resolve to
             ast::NameRef(name_ref) => {
                 highlight_name_ref(
                     sema,
@@ -217,6 +77,9 @@ fn node(
                     name_ref,
                 )
             },
+            ast::Name(name) => {
+                highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name)
+            },
             ast::Lifetime(lifetime) => {
                 match NameClass::classify_lifetime(sema, &lifetime) {
                     Some(NameClass::Definition(def)) => {
@@ -229,12 +92,129 @@ fn node(
                     _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
                 }
             },
-            _ => return None,
+            ast::Fn(_) => {
+                bindings_shadow_count.clear();
+                return None;
+            },
+            _ => {
+                if [FN, CONST, STATIC].contains(&node.kind()) {
+                    bindings_shadow_count.clear();
+                }
+                return None
+            },
         }
     };
     Some((highlight, binding_hash))
 }
 
+fn punctuation(sema: &Semantics<RootDatabase>, token: SyntaxToken, kind: SyntaxKind) -> Highlight {
+    let parent = token.parent();
+    let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind);
+    match (kind, parent_kind) {
+        (T![?], _) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
+        (T![&], BIN_EXPR) => HlOperator::Bitwise.into(),
+        (T![&], _) => {
+            let h = HlTag::Operator(HlOperator::Other).into();
+            let is_unsafe = parent
+                .and_then(ast::RefExpr::cast)
+                .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
+            if let Some(true) = is_unsafe {
+                h | HlMod::Unsafe
+            } else {
+                h
+            }
+        }
+        (T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(),
+        (T![!], MACRO_CALL) => HlPunct::MacroBang.into(),
+        (T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
+        (T![!], PREFIX_EXPR) => HlOperator::Logical.into(),
+        (T![*], PTR_TYPE) => HlTag::Keyword.into(),
+        (T![*], PREFIX_EXPR) => {
+            let is_raw_ptr = (|| {
+                let prefix_expr = parent.and_then(ast::PrefixExpr::cast)?;
+                let expr = prefix_expr.expr()?;
+                sema.type_of_expr(&expr)?.original.is_raw_ptr().then(|| ())
+            })();
+            if let Some(()) = is_raw_ptr {
+                HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
+            } else {
+                HlOperator::Other.into()
+            }
+        }
+        (T![-], PREFIX_EXPR) => {
+            let prefix_expr = parent.and_then(ast::PrefixExpr::cast).and_then(|e| e.expr());
+            match prefix_expr {
+                Some(ast::Expr::Literal(_)) => HlTag::NumericLiteral,
+                _ => HlTag::Operator(HlOperator::Other),
+            }
+            .into()
+        }
+        (T![+] | T![-] | T![*] | T![/], BIN_EXPR) => HlOperator::Arithmetic.into(),
+        (T![+=] | T![-=] | T![*=] | T![/=], BIN_EXPR) => {
+            Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
+        }
+        (T![|] | T![&] | T![!] | T![^], BIN_EXPR) => HlOperator::Bitwise.into(),
+        (T![|=] | T![&=] | T![^=], BIN_EXPR) => {
+            Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
+        }
+        (T![&&] | T![||], BIN_EXPR) => HlOperator::Logical.into(),
+        (T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=], BIN_EXPR) => {
+            HlOperator::Comparison.into()
+        }
+        (_, PREFIX_EXPR | BIN_EXPR | RANGE_EXPR | RANGE_PAT | REST_PAT) => HlOperator::Other.into(),
+        (_, ATTR) => HlTag::AttributeBracket.into(),
+        (kind, _) => match kind {
+            T!['['] | T![']'] => HlPunct::Bracket,
+            T!['{'] | T!['}'] => HlPunct::Brace,
+            T!['('] | T![')'] => HlPunct::Parenthesis,
+            T![<] | T![>] => HlPunct::Angle,
+            T![,] => HlPunct::Comma,
+            T![:] => HlPunct::Colon,
+            T![;] => HlPunct::Semi,
+            T![.] => HlPunct::Dot,
+            _ => HlPunct::Other,
+        }
+        .into(),
+    }
+}
+
+fn keyword(
+    sema: &Semantics<RootDatabase>,
+    token: SyntaxToken,
+    kind: SyntaxKind,
+) -> Option<Highlight> {
+    let h = Highlight::new(HlTag::Keyword);
+    let h = match kind {
+        T![await] => h | HlMod::Async | HlMod::ControlFlow,
+        T![async] => h | HlMod::Async,
+        T![break]
+        | T![continue]
+        | T![else]
+        | T![if]
+        | T![in]
+        | T![loop]
+        | T![match]
+        | T![return]
+        | T![while]
+        | T![yield] => h | HlMod::ControlFlow,
+        T![for] if parent_matches::<ast::ForExpr>(&token) => h | HlMod::ControlFlow,
+        T![unsafe] => h | HlMod::Unsafe,
+        T![true] | T![false] => HlTag::BoolLiteral.into(),
+        // crate is handled just as a token if it's in an `extern crate`
+        T![crate] if parent_matches::<ast::ExternCrate>(&token) => h,
+        // self, crate and super are handled as either a Name or NameRef already, unless they
+        // are inside unmapped token trees
+        T![self] | T![crate] | T![super] if parent_matches::<ast::NameRef>(&token) => return None,
+        T![self] if parent_matches::<ast::Name>(&token) => return None,
+        T![ref] => match token.parent().and_then(ast::IdentPat::cast) {
+            Some(ident) if sema.is_unsafe_ident_pat(&ident) => h | HlMod::Unsafe,
+            _ => h,
+        },
+        _ => h,
+    };
+    Some(h)
+}
+
 fn highlight_name_ref(
     sema: &Semantics<RootDatabase>,
     krate: Option<hir::Crate>,
@@ -325,10 +305,9 @@ fn highlight_name(
     krate: Option<hir::Crate>,
     name: ast::Name,
 ) -> Highlight {
-    let db = sema.db;
     let name_kind = NameClass::classify(sema, &name);
     if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
-        if let Some(name) = local.name(db) {
+        if let Some(name) = local.name(sema.db) {
             let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
             *shadow_count += 1;
             *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
@@ -338,7 +317,7 @@ fn highlight_name(
         Some(NameClass::Definition(def)) => {
             let mut h = highlight_def(sema, krate, def) | HlMod::Definition;
             if let Definition::Trait(trait_) = &def {
-                if trait_.is_unsafe(db) {
+                if trait_.is_unsafe(sema.db) {
                     h |= HlMod::Unsafe;
                 }
             }
@@ -347,7 +326,7 @@ fn highlight_name(
         Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def),
         Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
             let mut h = HlTag::Symbol(SymbolKind::Field).into();
-            if let hir::VariantDef::Union(_) = field_ref.parent_def(db) {
+            if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) {
                 h |= HlMod::Unsafe;
             }
             h
@@ -729,21 +708,14 @@ fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[Sy
     kinds.is_empty()
 }
 
-#[inline]
 fn parent_matches<N: AstNode>(token: &SyntaxToken) -> bool {
     token.parent().map_or(false, |it| N::can_cast(it.kind()))
 }
 
-fn is_child_of_impl(token: &SyntaxToken) -> bool {
-    match token.parent() {
-        Some(e) => e.kind() == IMPL,
-        _ => false,
-    }
-}
-
 fn is_in_fn_with_self_param<N: AstNode>(node: &N) -> bool {
     node.syntax()
         .ancestors()
+        .take_while(|node| ast::Expr::can_cast(node.kind()) || ast::Fn::can_cast(node.kind()))
         .find_map(ast::Fn::cast)
         .and_then(|s| s.param_list()?.self_param())
         .is_some()
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index ffef33b6c8..c9b8c94205 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -359,18 +359,6 @@ impl NameRefClass {
 
         let parent = name_ref.syntax().parent()?;
 
-        if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
-            if let Some(func) = sema.resolve_method_call(&method_call) {
-                return Some(NameRefClass::Definition(Definition::Function(func)));
-            }
-        }
-
-        if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
-            if let Some(field) = sema.resolve_field(&field_expr) {
-                return Some(NameRefClass::Definition(Definition::Field(field)));
-            }
-        }
-
         if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
             if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
                 let res = match local {
@@ -383,38 +371,6 @@ impl NameRefClass {
             }
         }
 
-        if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
-            if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
-                let field = Definition::Field(field);
-                return Some(NameRefClass::Definition(field));
-            }
-        }
-
-        if let Some(assoc_type_arg) = ast::AssocTypeArg::cast(parent.clone()) {
-            if assoc_type_arg.name_ref().as_ref() == Some(name_ref) {
-                // `Trait<Assoc = Ty>`
-                //        ^^^^^
-                let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
-                let resolved = sema.resolve_path(&path)?;
-                if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
-                    // FIXME: resolve in supertraits
-                    if let Some(ty) = tr
-                        .items(sema.db)
-                        .iter()
-                        .filter_map(|assoc| match assoc {
-                            hir::AssocItem::TypeAlias(it) => Some(*it),
-                            _ => None,
-                        })
-                        .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str())
-                    {
-                        return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
-                    }
-                }
-
-                return None;
-            }
-        }
-
         if let Some(path) = ast::PathSegment::cast(parent.clone()).map(|it| it.parent_path()) {
             if path.parent_path().is_none() {
                 if let Some(macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
@@ -428,10 +384,52 @@ impl NameRefClass {
             return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
         }
 
-        let extern_crate = ast::ExternCrate::cast(parent)?;
-        let krate = sema.resolve_extern_crate(&extern_crate)?;
-        let root_module = krate.root_module(sema.db);
-        Some(NameRefClass::Definition(Definition::Module(root_module)))
+        match_ast! {
+            match parent {
+                ast::MethodCallExpr(method_call) => {
+                    sema.resolve_method_call(&method_call)
+                        .map(Definition::Function)
+                        .map(NameRefClass::Definition)
+                },
+                ast::FieldExpr(field_expr) => {
+                    sema.resolve_field(&field_expr)
+                        .map(Definition::Field)
+                        .map(NameRefClass::Definition)
+                },
+                ast::RecordPatField(record_pat_field) => {
+                    sema.resolve_record_pat_field(&record_pat_field)
+                        .map(Definition::Field)
+                        .map(NameRefClass::Definition)
+                },
+                ast::AssocTypeArg(_) => {
+                    // `Trait<Assoc = Ty>`
+                    //        ^^^^^
+                    let containing_path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
+                    let resolved = sema.resolve_path(&containing_path)?;
+                    if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
+                        // FIXME: resolve in supertraits
+                        if let Some(ty) = tr
+                            .items(sema.db)
+                            .iter()
+                            .filter_map(|&assoc| match assoc {
+                                hir::AssocItem::TypeAlias(it) => Some(it),
+                                _ => None,
+                            })
+                            .find(|alias| alias.name(sema.db).to_smol_str() == name_ref.text().as_str())
+                        {
+                            return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
+                        }
+                    }
+                    None
+                },
+                ast::ExternCrate(extern_crate) => {
+                    let krate = sema.resolve_extern_crate(&extern_crate)?;
+                    let root_module = krate.root_module(sema.db);
+                    Some(NameRefClass::Definition(Definition::Module(root_module)))
+                },
+                _ => None
+            }
+        }
     }
 
     pub fn classify_lifetime(
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 910724411d..bcda2381a4 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -571,18 +571,18 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
 
         if !error_items.is_empty() {
             error_recover_item = error_items.pop().map(|it| it.bindings);
-        } else if let [state, ..] = &*eof_items {
-            error_recover_item = Some(state.bindings.clone());
+        } else if !eof_items.is_empty() {
+            error_recover_item = Some(eof_items[0].bindings.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 let [state] = &*eof_items {
+            if eof_items.len() == 1 {
                 // remove all errors, because it is the correct answer !
                 res = Match::default();
-                res.bindings = bindings_builder.build(&state.bindings);
+                res.bindings = bindings_builder.build(&eof_items[0].bindings);
             } else {
                 // Error recovery
                 if let Some(item) = error_recover_item {
@@ -598,10 +598,10 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
         //
         // 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.
-        let has_leftover_tokens = (bb_items.is_empty() && next_items.is_empty())
-            || !(bb_items.is_empty() || next_items.is_empty())
-            || bb_items.len() > 1;
-        if has_leftover_tokens {
+        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;
@@ -624,11 +624,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
                     stack.push(src.clone());
                     src = TtIter::new(subtree);
                 }
-                None => {
-                    if let Some(iter) = stack.pop() {
-                        src = iter;
-                    }
-                }
+                None if !stack.is_empty() => src = stack.pop().unwrap(),
                 _ => (),
             }
         }
@@ -666,23 +662,29 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
 fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
     let rhs = match src.expect_leaf() {
         Ok(l) => l,
-        Err(()) => return Err(err!("expected leaf: `{}`", lhs)),
+        Err(()) => {
+            return Err(err!("expected leaf: `{}`", lhs));
+        }
     };
     match (lhs, rhs) {
         (
             tt::Leaf::Punct(tt::Punct { char: lhs, .. }),
             tt::Leaf::Punct(tt::Punct { char: rhs, .. }),
-        ) if lhs == rhs => Ok(()),
+        ) if lhs == rhs => (),
         (
             tt::Leaf::Ident(tt::Ident { text: lhs, .. }),
             tt::Leaf::Ident(tt::Ident { text: rhs, .. }),
-        ) if lhs == rhs => Ok(()),
+        ) if lhs == rhs => (),
         (
             tt::Leaf::Literal(tt::Literal { text: lhs, .. }),
             tt::Leaf::Literal(tt::Literal { text: rhs, .. }),
-        ) if lhs == rhs => Ok(()),
-        _ => Err(ExpandError::UnexpectedToken),
+        ) if lhs == rhs => (),
+        _ => {
+            return Err(ExpandError::UnexpectedToken);
+        }
     }
+
+    Ok(())
 }
 
 fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragment>> {
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index b5ae5b91bb..98b37dd122 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -4,10 +4,11 @@
 use syntax::SmolStr;
 use tt::{Delimiter, Subtree};
 
+use super::ExpandResult;
 use crate::{
     expander::{Binding, Bindings, Fragment},
     parser::{Op, RepeatKind, Separator},
-    ExpandError, ExpandResult, MetaTemplate,
+    ExpandError, MetaTemplate,
 };
 
 impl Bindings {
@@ -16,36 +17,36 @@ impl Bindings {
     }
 
     fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
-        macro_rules! binding_err {
-            ($($arg:tt)*) => { ExpandError::BindingError(format!($($arg)*)) };
-        }
-
-        let mut b: &Binding = self
-            .inner
-            .get(name)
-            .ok_or_else(|| binding_err!("could not find binding `{}`", name))?;
+        let mut b: &Binding = 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 {
                 Binding::Fragment(_) => break,
                 Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
                     nesting_state.at_end = true;
-                    binding_err!("could not find nested binding `{}`", name)
+                    ExpandError::BindingError(format!("could not find nested binding `{}`", name))
                 })?,
                 Binding::Empty => {
                     nesting_state.at_end = true;
-                    return Err(binding_err!("could not find empty binding `{}`", name));
+                    return Err(ExpandError::BindingError(format!(
+                        "could not find empty binding `{}`",
+                        name
+                    )));
                 }
             };
         }
         match b {
             Binding::Fragment(it) => Ok(it),
-            Binding::Nested(_) => {
-                Err(binding_err!("expected simple binding, found nested binding `{}`", name))
-            }
-            Binding::Empty => {
-                Err(binding_err!("expected simple binding, found empty binding `{}`", name))
-            }
+            Binding::Nested(_) => Err(ExpandError::BindingError(format!(
+                "expected simple binding, found nested binding `{}`",
+                name
+            ))),
+            Binding::Empty => Err(ExpandError::BindingError(format!(
+                "expected simple binding, found empty binding `{}`",
+                name
+            ))),
         }
     }
 }
@@ -108,7 +109,7 @@ fn expand_subtree(
         }
     }
     // drain the elements added in this instance of expand_subtree
-    let tts = arena.drain(start_elements..).collect();
+    let tts = arena.drain(start_elements..arena.len()).collect();
     ExpandResult { value: tt::Subtree { delimiter, token_trees: tts }, err }
 }
 
@@ -192,22 +193,23 @@ fn expand_repeat(
         push_subtree(&mut buf, t);
 
         if let Some(sep) = separator {
-            has_seps = match sep {
+            match sep {
                 Separator::Ident(ident) => {
+                    has_seps = 1;
                     buf.push(tt::Leaf::from(ident.clone()).into());
-                    1
                 }
                 Separator::Literal(lit) => {
+                    has_seps = 1;
                     buf.push(tt::Leaf::from(lit.clone()).into());
-                    1
                 }
+
                 Separator::Puncts(puncts) => {
-                    for &punct in puncts {
-                        buf.push(tt::Leaf::from(punct).into());
+                    has_seps = puncts.len();
+                    for punct in puncts {
+                        buf.push(tt::Leaf::from(*punct).into());
                     }
-                    puncts.len()
                 }
-            };
+            }
         }
 
         if RepeatKind::ZeroOrOne == kind {
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index ef29380557..7543054494 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -41,7 +41,7 @@ impl MetaTemplate {
         let mut res = Vec::new();
         while let Some(first) = src.next() {
             let op = next_op(first, &mut src, mode)?;
-            res.push(op);
+            res.push(op)
         }
 
         Ok(MetaTemplate(res))
@@ -110,6 +110,12 @@ macro_rules! err {
     };
 }
 
+macro_rules! bail {
+    ($($tt:tt)*) => {
+        return Err(err!($($tt)*))
+    };
+}
+
 fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ParseError> {
     let res = match first {
         tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => {
@@ -125,24 +131,26 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
                     Op::Repeat { tokens, separator, kind }
                 }
                 tt::TokenTree::Leaf(leaf) => match leaf {
+                    tt::Leaf::Punct(_) => return Err(ParseError::Expected("ident".to_string())),
                     tt::Leaf::Ident(ident) if ident.text == "crate" => {
                         // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
                         Op::Leaf(tt::Leaf::from(tt::Ident { text: "$crate".into(), id: ident.id }))
                     }
                     tt::Leaf::Ident(ident) => {
-                        let kind = eat_fragment_kind(src, mode)?;
                         let name = ident.text.clone();
-                        let id = ident.id;
-                        Op::Var { name, kind, id }
-                    }
-                    tt::Leaf::Literal(lit) if is_boolean_literal(lit) => {
                         let kind = eat_fragment_kind(src, mode)?;
-                        let name = lit.text.clone();
-                        let id = lit.id;
+                        let id = ident.id;
                         Op::Var { name, kind, id }
                     }
-                    tt::Leaf::Punct(_) | tt::Leaf::Literal(_) => {
-                        return Err(ParseError::Expected("ident".to_string()))
+                    tt::Leaf::Literal(lit) => {
+                        if is_boolean_literal(lit) {
+                            let name = lit.text.clone();
+                            let kind = eat_fragment_kind(src, mode)?;
+                            let id = lit.id;
+                            Op::Var { name, kind, id }
+                        } else {
+                            bail!("bad var 2");
+                        }
                     }
                 },
             }
@@ -158,8 +166,8 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
 
 fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
     if let Mode::Pattern = mode {
-        src.expect_char(':').map_err(|()| err!("missing fragment specifier"))?;
-        let ident = src.expect_ident().map_err(|()| err!("missing fragment specifier"))?;
+        src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
+        let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
         return Ok(Some(ident.text.clone()));
     };
     Ok(None)
@@ -191,15 +199,21 @@ fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), Par
                     '*' => RepeatKind::ZeroOrMore,
                     '+' => RepeatKind::OneOrMore,
                     '?' => RepeatKind::ZeroOrOne,
-                    _ => match &mut separator {
-                        Separator::Puncts(puncts) if puncts.len() != 3 => {
-                            puncts.push(*punct);
-                            continue;
+                    _ => {
+                        match &mut separator {
+                            Separator::Puncts(puncts) => {
+                                if puncts.len() == 3 {
+                                    return Err(ParseError::InvalidRepeat);
+                                }
+                                puncts.push(*punct)
+                            }
+                            _ => return Err(ParseError::InvalidRepeat),
                         }
-                        _ => return Err(ParseError::InvalidRepeat),
-                    },
+                        continue;
+                    }
                 };
-                return Ok((has_sep.then(|| separator), repeat_kind));
+                let separator = if has_sep { Some(separator) } else { None };
+                return Ok((separator, repeat_kind));
             }
         }
     }


Test patch:
diff --git a/crates/hir_def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir_def/src/macro_expansion_tests/mbe/meta_syntax.rs
index 2de10ddbdf..dd5effa368 100644
--- a/crates/hir_def/src/macro_expansion_tests/mbe/meta_syntax.rs
+++ b/crates/hir_def/src/macro_expansion_tests/mbe/meta_syntax.rs
@@ -69,11 +69,11 @@ macro_rules! e3 { ($(i:ident)_) => () }
 /* error: invalid macro definition: invalid repeat */
 
 macro_rules! f1 { ($i) => ($i) }
-/* error: invalid macro definition: missing fragment specifier */
+/* error: invalid macro definition: bad fragment specifier 1 */
 macro_rules! f2 { ($i:) => ($i) }
-/* error: invalid macro definition: missing fragment specifier */
+/* error: invalid macro definition: bad fragment specifier 1 */
 macro_rules! f3 { ($i:_) => () }
-/* error: invalid macro definition: missing fragment specifier */
+/* error: invalid macro definition: bad fragment specifier 1 */
 "#]],
     )
 }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 7dae4a1a14..50013c06a7 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -143,12 +143,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="comment documentation">///</span>
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute_bracket attribute"> </span><span class="none attribute">doc</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">doc</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span>
 <span class="comment documentation">/// ```</span>
 <span class="comment documentation">///</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute_bracket attribute"> </span><span class="none attribute">doc</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute_bracket attribute"> </span><span class="none attribute">doc</span><span class="attribute_bracket attribute"> </span><span class="operator attribute">=</span><span class="attribute_bracket attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
 <span class="comment documentation">/// ```</span>
 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index d7f996fba9..1ae07a125f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -262,7 +262,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">impl</span> <span class="enum public">Bool</span> <span class="brace">{</span>
     <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function associated consuming declaration public">to_primitive</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
-        <span class="unresolved_reference">matches</span><span class="macro_bang">!</span><span class="parenthesis">(</span>self<span class="comma">,</span> Self<span class="colon">:</span><span class="colon">:</span>True<span class="parenthesis">)</span>
+        <span class="unresolved_reference">matches</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword">self</span><span class="comma">,</span> Self<span class="colon">:</span><span class="colon">:</span>True<span class="parenthesis">)</span>
     <span class="brace">}</span>
 <span class="brace">}</span>
 <span class="keyword">const</span> <span class="constant declaration">USAGE_OF_BOOL</span><span class="colon">:</span><span class="builtin_type">bool</span> <span class="operator">=</span> <span class="enum public">Bool</span><span class="operator">::</span><span class="enum_variant public">True</span><span class="operator">.</span><span class="function associated consuming public">to_primitive</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>