Repository: rust-analyzer/rust-analyzer
Pull request #11173 (2022-01-02 23:46:06)

Allow adding partially resolved types
Sometimes when writing something like `let foo = Arc::new(Mutex::new(CrazyGenerics::new(HashMap::new())))`, I want/have to specify an explicit type for the expression.
Using turbofish isn't very readable and not always appreciated by guidelines, so `let foo: T` has to be filled.

To ease that, the PR enables the `add_explicit_type` assist on types that contain unknown types and some generics.
Fully unresolved types, arrays with unknown types and other known cases behave the same.

`_` placeholder was chosen to replace an unknown type:

```rust
let foo = HashMap::new();
// after assist usage, turns into
let foo: HashMap<_, _> = HashMap::new();
```


Patch:
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 233c814809..2d779393f0 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -290,7 +290,9 @@ impl SourceAnalyzer {
                 return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
             }
             prefer_value_ns = true;
-        } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
+        }
+
+        if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
             let pat_id = self.pat_id(&path_pat.into())?;
             if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
                 return Some(PathResolution::AssocItem(assoc.into()));
@@ -300,7 +302,9 @@ impl SourceAnalyzer {
             {
                 return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
             }
-        } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
+        }
+
+        if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
             let expr_id = self.expr_id(db, &rec_lit.into())?;
             if let Some(VariantId::EnumVariantId(variant)) =
                 self.infer.as_ref()?.variant_resolution_for_expr(expr_id)
@@ -327,34 +331,32 @@ impl SourceAnalyzer {
         // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
         // trying to resolve foo::bar.
         if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
-            if use_tree.coloncolon_token().is_some() {
-                return resolve_hir_path_qualifier(db, &self.resolver, &hir_path);
+            if let Some(qualifier) = use_tree.path() {
+                if path == &qualifier && use_tree.coloncolon_token().is_some() {
+                    return resolve_hir_path_qualifier(db, &self.resolver, &hir_path);
+                }
             }
         }
 
         let is_path_of_attr = path
+            .top_path()
             .syntax()
             .ancestors()
-            .map(|it| it.kind())
-            .take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind))
-            .last()
-            .map_or(false, ast::Meta::can_cast);
+            .nth(2) // Path -> Meta -> Attr
+            .map_or(false, |it| ast::Attr::can_cast(it.kind()));
 
         // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
         // trying to resolve foo::bar.
-        if path.parent_path().is_some() {
-            return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) {
-                None if is_path_of_attr => {
-                    path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
-                        ToolModule::by_name(&name_ref.text()).map(PathResolution::ToolModule)
-                    })
+        if let Some(outer_path) = path.parent_path() {
+            if let Some(qualifier) = outer_path.qualifier() {
+                if path == &qualifier {
+                    return resolve_hir_path_qualifier(db, &self.resolver, &hir_path);
                 }
-                res => res,
-            };
+            }
         } else if is_path_of_attr {
             // Case where we are resolving the final path segment of a path in an attribute
             // in this case we have to check for inert/builtin attributes and tools and prioritize
-            // resolution of attributes over other namespaces
+            // resolution of attributes over other namesapces
             let name_ref = path.as_single_name_ref();
             let builtin =
                 name_ref.as_ref().map(ast::NameRef::text).as_deref().and_then(BuiltinAttr::by_name);
@@ -363,17 +365,27 @@ impl SourceAnalyzer {
             }
             return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
                 res @ Some(m) if m.is_attr() => res.map(PathResolution::Macro),
-                // this labels any path that starts with a tool module as the tool itself, this is technically wrong
-                // but there is no benefit in differentiating these two cases for the time being
-                _ => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
+                _ => name_ref.and_then(|name_ref| {
                     ToolModule::by_name(&name_ref.text()).map(PathResolution::ToolModule)
                 }),
             };
         }
-        if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
+
+        let res = if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
             resolve_hir_path_qualifier(db, &self.resolver, &hir_path)
         } else {
             resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns)
+        };
+        match res {
+            Some(_) => res,
+            // this labels any path that starts with a tool module as the tool itself, this is technically wrong
+            // but there is no benefit in differentiating these two cases for the time being
+            None if is_path_of_attr => path
+                .first_segment()
+                .and_then(|seg| seg.name_ref())
+                .and_then(|name_ref| ToolModule::by_name(&name_ref.text()))
+                .map(PathResolution::ToolModule),
+            None => None,
         }
     }
 
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index cd898a45d2..95d1550afc 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -567,7 +567,27 @@ impl HirDisplay for Ty {
                     };
                     if !parameters_to_write.is_empty() {
                         write!(f, "<")?;
-                        f.write_joined(parameters_to_write, ", ")?;
+
+                        if f.display_target.is_source_code() {
+                            let mut first = true;
+                            for generic_arg in parameters_to_write {
+                                if !first {
+                                    write!(f, ", ")?;
+                                }
+                                first = false;
+
+                                if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
+                                    == Some(&TyKind::Error)
+                                {
+                                    write!(f, "_")?;
+                                } else {
+                                    generic_arg.hir_fmt(f)?;
+                                }
+                            }
+                        } else {
+                            f.write_joined(parameters_to_write, ", ")?;
+                        }
+
                         write!(f, ">")?;
                     }
                 }
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 28c7c546ff..a908cdb718 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -265,7 +265,6 @@ 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)) => {
@@ -348,16 +347,13 @@ fn traverse(
         }
 
         // do the normal highlighting
-        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)),
-        };
+        let element = highlight::element(
+            sema,
+            krate,
+            &mut bindings_shadow_count,
+            syntactic_name_ref_highlighting,
+            element_to_highlight,
+        );
         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 4a09d341f7..3b322bd525 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,
+    ast, match_ast, AstNode, AstToken, NodeOrToken, SyntaxElement,
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, T,
 };
@@ -18,46 +18,174 @@ use crate::{
     Highlight, HlMod, HlTag,
 };
 
-pub(super) fn token(
+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(
     sema: &Semantics<RootDatabase>,
     krate: Option<hir::Crate>,
     token: SyntaxToken,
 ) -> Option<Highlight> {
-    if let Some(comment) = ast::Comment::cast(token.clone()) {
+    let highlight: Highlight = if let Some(comment) = ast::Comment::cast(token.clone()) {
         let h = HlTag::Comment;
-        return Some(match comment.kind().doc {
+        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()
         }
-        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) => {
+    } 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) {
                     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)
 }
 
-pub(super) fn node(
+fn node(
     sema: &Semantics<RootDatabase>,
     krate: Option<hir::Crate>,
     bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
@@ -67,6 +195,18 @@ pub(super) 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,
@@ -77,9 +217,6 @@ pub(super) 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)) => {
@@ -92,129 +229,12 @@ pub(super) fn node(
                     _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
                 }
             },
-            ast::Fn(_) => {
-                bindings_shadow_count.clear();
-                return None;
-            },
-            _ => {
-                if [FN, CONST, STATIC].contains(&node.kind()) {
-                    bindings_shadow_count.clear();
-                }
-                return None
-            },
+            _ => 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>,
@@ -305,9 +325,10 @@ 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(sema.db) {
+        if let Some(name) = local.name(db) {
             let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
             *shadow_count += 1;
             *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
@@ -317,7 +338,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(sema.db) {
+                if trait_.is_unsafe(db) {
                     h |= HlMod::Unsafe;
                 }
             }
@@ -326,7 +347,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(sema.db) {
+            if let hir::VariantDef::Union(_) = field_ref.parent_def(db) {
                 h |= HlMod::Unsafe;
             }
             h
@@ -708,14 +729,21 @@ 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_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs
index 169bb0cbf4..d7e1be900f 100644
--- a/crates/ide_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide_assists/src/handlers/add_explicit_type.rs
@@ -60,8 +60,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
     }
     .adjusted();
 
-    // Unresolved or unnameable types can't be annotated
-    if ty.contains_unknown() || ty.is_closure() {
+    // Fully unresolved or unnameable types can't be annotated
+    if (ty.contains_unknown() && ty.type_arguments().count() == 0) || ty.is_closure() {
         cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
         return None;
     }
@@ -139,11 +139,34 @@ fn f() {
     }
 
     #[test]
-    fn add_explicit_type_not_applicable_unresolved() {
+    fn add_explicit_type_not_applicable_for_fully_unresolved() {
         cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
         check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
     }
 
+    #[test]
+    fn add_explicit_type_applicable_for_partially_unresolved() {
+        check_assist(
+            add_explicit_type,
+            r#"
+        struct Vec<T, V> { t: T, v: V }
+        impl<T> Vec<T, Vec<ZZZ, i32>> {
+            fn new() -> Self {
+                panic!()
+            }
+        }
+        fn f() { let a$0 = Vec::new(); }"#,
+            r#"
+        struct Vec<T, V> { t: T, v: V }
+        impl<T> Vec<T, Vec<ZZZ, i32>> {
+            fn new() -> Self {
+                panic!()
+            }
+        }
+        fn f() { let a: Vec<_, Vec<_, i32>> = Vec::new(); }"#,
+        );
+    }
+
     #[test]
     fn add_explicit_type_not_applicable_closure_expr() {
         check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = || {}; }"#);
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index c9b8c94205..ffef33b6c8 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -359,6 +359,18 @@ 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 {
@@ -371,6 +383,38 @@ 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) {
@@ -384,52 +428,10 @@ impl NameRefClass {
             return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
         }
 
-        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
-            }
-        }
+        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)))
     }
 
     pub fn classify_lifetime(


Test patch:
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 50013c06a7..7dae4a1a14 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="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="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="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="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="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="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 3ec6b6a77d..d7f996fba9 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -44,7 +44,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">allow</span><span class="parenthesis 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="tool_module attribute library">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute library">skip</span><span class="attribute_bracket attribute">]</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">identity</span><span class="attribute_bracket attribute">]</span>
 <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
     <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span>
@@ -263,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><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="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="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>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 4e62edc7c1..c5834e1942 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -17,7 +17,6 @@ use inner::{self as inner_mod};
 mod inner {}
 
 #[allow()]
-#[rustfmt::skip]
 #[proc_macros::identity]
 pub mod ops {
     #[lang = "fn_once"]