Repository: rust-analyzer/rust-analyzer
Pull request #4034 (2020-04-18 19:21:14)

Add semantic tag for unresolved references
This is a quick way to implement unresolved reference diagnostics.
For example, adding to VS Code config

    "editor.tokenColorCustomizationsExperimental": {
        "unresolvedReference": "#FF0000"
    },

will highlight all unresolved refs in red.

matklad: ![red](https://user-images.githubusercontent.com/1711539/79669258-944a0300-81ba-11ea-8396-4ec605b1d406.gif)

bors r+

Still has a tonne of false-positives, but it won't change anything unless users explicitelly set the color
bors[bot]: Build failed:
  * [Rust (macos-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/598309420)

Patch:
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 214dcbb620..ccb1fc7516 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -50,12 +50,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">fn</span> <span class="function declaration">main</span>() {
     <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
 
-    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = Vec::new();
+    <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
     <span class="keyword control">if</span> <span class="keyword">true</span> {
         <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
-        <span class="variable mutable">vec</span>.push(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
+        <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
     }
-    <span class="keyword unsafe">unsafe</span> { <span class="variable mutable">vec</span>.set_len(<span class="numeric_literal">0</span>); }
+    <span class="keyword unsafe">unsafe</span> { <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>); }
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
     <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index dddbfc0dd6..3df82c45fb 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -28,11 +28,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 </style>
 <pre><code><span class="keyword">fn</span> <span class="function declaration">main</span>() {
     <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.to_string();
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
 
     <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string_literal">"other color please!"</span>;
-    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.to_string();
+    <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.<span class="unresolved_reference">to_string</span>();
 }
 
 <span class="keyword">fn</span> <span class="function declaration">bar</span>() {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 7b15b82bdb..93d5028756 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -239,20 +239,21 @@ fn highlight_element(
         NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None,
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
-            let name_kind = classify_name_ref(sema, &name_ref)?;
-
-            match name_kind {
-                NameRefClass::Definition(def) => {
-                    if let Definition::Local(local) = &def {
-                        if let Some(name) = local.name(db) {
-                            let shadow_count =
-                                bindings_shadow_count.entry(name.clone()).or_default();
-                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
-                        }
-                    };
-                    highlight_name(db, def)
-                }
-                NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
+            match classify_name_ref(sema, &name_ref) {
+                Some(name_kind) => match name_kind {
+                    NameRefClass::Definition(def) => {
+                        if let Definition::Local(local) = &def {
+                            if let Some(name) = local.name(db) {
+                                let shadow_count =
+                                    bindings_shadow_count.entry(name.clone()).or_default();
+                                binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                            }
+                        };
+                        highlight_name(db, def)
+                    }
+                    NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
+                },
+                None => HighlightTag::UnresolvedReference.into(),
             }
         }
 
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index e8b138e1a7..f2c4216545 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -38,6 +38,7 @@ pub enum HighlightTag {
     TypeParam,
     Union,
     Local,
+    UnresolvedReference,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -79,6 +80,7 @@ impl HighlightTag {
             HighlightTag::TypeParam => "type_param",
             HighlightTag::Union => "union",
             HighlightTag::Local => "variable",
+            HighlightTag::UnresolvedReference => "unresolved_reference",
         }
     }
 }
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index b2b1cb6258..8d2360cc8d 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -24,7 +24,9 @@ use crate::{
     world::WorldSnapshot,
     Result,
 };
-use semantic_tokens::{ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION};
+use semantic_tokens::{
+    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE,
+};
 
 pub trait Conv {
     type Output;
@@ -373,6 +375,7 @@ impl Conv for Highlight {
             HighlightTag::Comment => SemanticTokenType::COMMENT,
             HighlightTag::Attribute => ATTRIBUTE,
             HighlightTag::Keyword => SemanticTokenType::KEYWORD,
+            HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE,
         };
 
         for modifier in self.modifiers.iter() {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 865fa3b1c5..10fe696f6f 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -10,6 +10,8 @@ pub(crate) const ENUM_MEMBER: SemanticTokenType = SemanticTokenType::new("enumMe
 pub(crate) const LIFETIME: SemanticTokenType = SemanticTokenType::new("lifetime");
 pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAlias");
 pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union");
+pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType =
+    SemanticTokenType::new("unresolvedReference");
 
 pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant");
 pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow");
@@ -43,6 +45,7 @@ pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
     LIFETIME,
     TYPE_ALIAS,
     UNION,
+    UNRESOLVED_REFERENCE,
 ];
 
 pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
diff --git a/editors/code/package.json b/editors/code/package.json
index 5ce59e54a9..79410ad109 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -517,6 +517,10 @@
                 "id": "union",
                 "description": "Style for C-style untagged unions",
                 "superType": "type"
+            },
+            {
+                "id": "unresolvedReference",
+                "description": "Style for names which can not be resolved due to compilation errors"
             }
         ],
         "semanticTokenModifiers": [


Test patch:
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index f2a726538c..5d1274d21f 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -214,33 +214,6 @@ SUBTREE $
     );
 }
 
-#[test]
-fn test_lifetime_split() {
-    parse_macro(
-        r#"
-macro_rules! foo {
-    ($($t:tt)*) => { $($t)*}
-}
-"#,
-    )
-    .assert_expand(
-        r#"foo!(static bar: &'static str = "hello";);"#,
-        r#"
-SUBTREE $
-  IDENT   static 17
-  IDENT   bar 18
-  PUNCH   : [alone] 19
-  PUNCH   & [alone] 20
-  PUNCH   ' [joint] 21
-  IDENT   static 22
-  IDENT   str 23
-  PUNCH   = [alone] 24
-  LITERAL "hello" 25
-  PUNCH   ; [joint] 26
-"#,
-    );
-}
-
 #[test]
 fn test_expr_order() {
     let expanded = parse_macro(