From 21d2cebcf1a417bce72da98aa638a20235c050db Mon Sep 17 00:00:00 2001
From: David Lattimore <dml@google.com>
Date: Sat, 1 Aug 2020 12:43:10 +1000
Subject: [PATCH 01/76] SSR: Matching trait associated constants, types and
 functions

This fixes matching of things like `HashMap::default()` by resolving
`HashMap` instead of `default` (which resolves to `Default::default`).

Same for associated constants and types that are part of a trait
implementation.

However, we still don't support matching calls to trait methods.
---
 crates/ra_ide/src/ssr.rs       |  4 +--
 crates/ra_ssr/src/resolving.rs | 31 +++++++++++++---
 crates/ra_ssr/src/tests.rs     | 64 ++++++++++++++++++++++++++++++++++
 3 files changed, 93 insertions(+), 6 deletions(-)

diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 4348b43beb5..8be862fd6e1 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -21,8 +21,8 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule};
 // replacement occurs. For example if our replacement template is `foo::Bar` and we match some
 // code in the `foo` module, we'll insert just `Bar`.
 //
-// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match
-// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
+// Inherent method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will
+// match `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`.
 //
 // The scope of the search / replace will be restricted to the current selection if any, otherwise
 // it will apply to the whole workspace.
diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs
index 6f62000f4a7..d5b65eaacf2 100644
--- a/crates/ra_ssr/src/resolving.rs
+++ b/crates/ra_ssr/src/resolving.rs
@@ -5,7 +5,7 @@ use crate::{parsing, SsrError};
 use parsing::Placeholder;
 use ra_db::FilePosition;
 use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken};
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashMap;
 use test_utils::mark;
 
 pub(crate) struct ResolutionScope<'db> {
@@ -111,8 +111,10 @@ impl Resolver<'_, '_> {
                     .resolution_scope
                     .resolve_path(&path)
                     .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?;
-                resolved_paths.insert(node, ResolvedPath { resolution, depth });
-                return Ok(());
+                if self.ok_to_use_path_resolution(&resolution) {
+                    resolved_paths.insert(node, ResolvedPath { resolution, depth });
+                    return Ok(());
+                }
             }
         }
         for node in node.children() {
@@ -136,6 +138,27 @@ impl Resolver<'_, '_> {
         }
         false
     }
+
+    fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool {
+        match resolution {
+            hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => {
+                if function.has_self_param(self.resolution_scope.scope.db) {
+                    // If we don't use this path resolution, then we won't be able to match method
+                    // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
+                    true
+                } else {
+                    mark::hit!(replace_associated_trait_default_function_call);
+                    false
+                }
+            }
+            hir::PathResolution::AssocItem(_) => {
+                // Not a function. Could be a constant or an associated type.
+                mark::hit!(replace_associated_trait_constant);
+                false
+            }
+            _ => true,
+        }
+    }
 }
 
 impl<'db> ResolutionScope<'db> {
@@ -176,7 +199,7 @@ impl<'db> ResolutionScope<'db> {
             adt.ty(self.scope.db).iterate_path_candidates(
                 self.scope.db,
                 self.scope.module()?.krate(),
-                &FxHashSet::default(),
+                &self.scope.traits_in_scope(),
                 Some(hir_path.segments().last()?.name),
                 |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)),
             )
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
index 2ae03c64c42..0a49a46e382 100644
--- a/crates/ra_ssr/src/tests.rs
+++ b/crates/ra_ssr/src/tests.rs
@@ -549,6 +549,70 @@ fn replace_associated_function_call() {
     );
 }
 
+#[test]
+fn replace_associated_trait_default_function_call() {
+    mark::check!(replace_associated_trait_default_function_call);
+    assert_ssr_transform(
+        "Bar2::foo() ==>> Bar2::foo2()",
+        r#"
+            trait Foo { fn foo() {} }
+            pub struct Bar {}
+            impl Foo for Bar {}
+            pub struct Bar2 {}
+            impl Foo for Bar2 {}
+            impl Bar2 { fn foo2() {} }
+            fn main() {
+                Bar::foo();
+                Bar2::foo();
+            }
+        "#,
+        expect![[r#"
+            trait Foo { fn foo() {} }
+            pub struct Bar {}
+            impl Foo for Bar {}
+            pub struct Bar2 {}
+            impl Foo for Bar2 {}
+            impl Bar2 { fn foo2() {} }
+            fn main() {
+                Bar::foo();
+                Bar2::foo2();
+            }
+        "#]],
+    );
+}
+
+#[test]
+fn replace_associated_trait_constant() {
+    mark::check!(replace_associated_trait_constant);
+    assert_ssr_transform(
+        "Bar2::VALUE ==>> Bar2::VALUE_2222",
+        r#"
+            trait Foo { const VALUE: i32; const VALUE_2222: i32; }
+            pub struct Bar {}
+            impl Foo for Bar { const VALUE: i32 = 1;  const VALUE_2222: i32 = 2; }
+            pub struct Bar2 {}
+            impl Foo for Bar2 { const VALUE: i32 = 1;  const VALUE_2222: i32 = 2; }
+            impl Bar2 { fn foo2() {} }
+            fn main() {
+                Bar::VALUE;
+                Bar2::VALUE;
+            }
+            "#,
+        expect![[r#"
+            trait Foo { const VALUE: i32; const VALUE_2222: i32; }
+            pub struct Bar {}
+            impl Foo for Bar { const VALUE: i32 = 1;  const VALUE_2222: i32 = 2; }
+            pub struct Bar2 {}
+            impl Foo for Bar2 { const VALUE: i32 = 1;  const VALUE_2222: i32 = 2; }
+            impl Bar2 { fn foo2() {} }
+            fn main() {
+                Bar::VALUE;
+                Bar2::VALUE_2222;
+            }
+        "#]],
+    );
+}
+
 #[test]
 fn replace_path_in_different_contexts() {
     // Note the <|> inside module a::b which marks the point where the rule is interpreted. We

From 85d5ed367579fb9e868517be0f0df636f6277363 Mon Sep 17 00:00:00 2001
From: Aleksei Trifonov <avrong@avrong.me>
Date: Sun, 2 Aug 2020 13:53:34 +0300
Subject: [PATCH 02/76] Fix test code lens

---
 crates/rust-analyzer/src/handlers.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index e73b3a2119e..6994e611b2d 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -896,7 +896,7 @@ pub(crate) fn handle_code_lens(
             }
 
             let action = runnable.action();
-            let range = to_proto::range(&line_index, runnable.nav.focus_or_full_range());
+            let range = to_proto::range(&line_index, runnable.nav.full_range);
             let r = to_proto::runnable(&snap, file_id, runnable)?;
             if snap.config.lens.run {
                 let lens = CodeLens {

From 4591bd458dd0d650f2288e911b598c0a571dcdfd Mon Sep 17 00:00:00 2001
From: Jeremy Kolb <kjeremy@gmail.com>
Date: Wed, 5 Aug 2020 22:24:23 -0400
Subject: [PATCH 03/76] Update chalk

---
 Cargo.lock                  | 16 ++++++++--------
 crates/ra_hir_ty/Cargo.toml |  6 +++---
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 451928c27bb..dc49fc4bdc0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -125,9 +125,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
 name = "chalk-derive"
-version = "0.19.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "654c611946ba2629c5028cb7708687af975faf2c29d731824cb294c873df4697"
+checksum = "c1df0dbb57d74b4acd20f20fa66ab2acd09776b79eaeb9d8f947b2f3e01c40bf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -137,9 +137,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.19.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a5341fbc654ca886b73b804a36aebf0e621057ccc1a68e9815b5b39b3ac9ae8"
+checksum = "44361a25dbdb1dc428f56ad7a3c21ba9ca12f3225c26a47919ff6fcb10a583d4"
 dependencies = [
  "chalk-derive",
  "lazy_static",
@@ -147,9 +147,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.19.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4484807b155b5a411e6135d330295f9ba5042e2920b8712c6574ca6ea91e9ee5"
+checksum = "dd89556b98de156d5eaf21077d297cd2198628f10f2df140798ea3a5dd84bc86"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -160,9 +160,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.19.0"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "281f82facd2538997fbe52132b1941ed213d266748215c31d15f62a8664429ad"
+checksum = "a886da37a0dc457057d86f78f026f7a09c6d8088aa13f4f4127fdb8dc80119a3"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 623ce261a05..83397d5793b 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -28,9 +28,9 @@ test_utils = { path = "../test_utils" }
 
 scoped-tls = "1"
 
-chalk-solve = { version = "0.19.0" }
-chalk-ir = { version = "0.19.0" }
-chalk-recursive = { version = "0.19.0" }
+chalk-solve = { version = "0.21.0" }
+chalk-ir = { version = "0.21.0" }
+chalk-recursive = { version = "0.21.0" }
 
 [dev-dependencies]
 expect = { path = "../expect" }

From 6be528da0de45606498a9755ad2c54f3bc41dc6c Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 6 Aug 2020 19:58:37 -0400
Subject: [PATCH 04/76] Add test for accessing static mut

---
 crates/ra_ide/src/syntax_highlighting/tests.rs | 7 +++++++
 crates/ra_ide/test_data/highlight_unsafe.html  | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 2deee404ccc..b9b35802234 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -281,6 +281,12 @@ impl HasUnsafeFn {
     unsafe fn unsafe_method(&self) {}
 }
 
+struct TypeForStaticMut {
+    a: u8
+}
+
+static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
+
 fn main() {
     let x = &5 as *const usize;
     unsafe {
@@ -288,6 +294,7 @@ fn main() {
         HasUnsafeFn.unsafe_method();
         let y = *(x);
         let z = -x;
+        let a = global_mut.a;
     }
 }
 "#
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index b81b6f1c3b9..7c519ef5e50 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -43,6 +43,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
+<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span>
+    <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span>
+<span class="punctuation">}</span>
+
+<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
+
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
@@ -50,5 +56,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span></code></pre>
\ No newline at end of file

From 8e657f663d519771ac8ffcd4b52ded8cb381b918 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 6 Aug 2020 20:07:42 -0400
Subject: [PATCH 05/76] Mark static mutable names as unsafe

---
 crates/ra_ide/src/syntax_highlighting.rs      | 1 +
 crates/ra_ide/test_data/highlight_unsafe.html | 4 ++--
 crates/ra_ide/test_data/highlighting.html     | 4 ++--
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index a32ae0165e1..89efe71dabc 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -677,6 +677,7 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
                 let mut h = Highlight::new(HighlightTag::Static);
                 if s.is_mut(db) {
                     h |= HighlightModifier::Mutable;
+                    h |= HighlightModifier::Unsafe;
                 }
                 return h;
             }
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 7c519ef5e50..87d8c155685 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -47,7 +47,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span>
 <span class="punctuation">}</span>
 
-<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
+<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
@@ -56,6 +56,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 23c25ad8cee..8e0160eee5b 100644
--- a/crates/ra_ide/test_data/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -64,7 +64,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
-<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
+<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
 
 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">&lt;</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="punctuation">{</span>
     <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span>
@@ -97,7 +97,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="punctuation">}</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="variable mutable">vec</span><span class="punctuation">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="static mutable">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span>
+        <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 
     <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span>

From f089690a21357ca6d396bce98bf598f0954199b5 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 6 Aug 2020 20:55:29 -0400
Subject: [PATCH 06/76] Account for static mut in missing unsafe diagnostic

---
 .../ra_hir_ty/src/diagnostics/unsafe_check.rs | 38 +++++++++++++++++--
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
index 5cc76bdce41..61ffbf5d151 100644
--- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
@@ -6,6 +6,7 @@ use std::sync::Arc;
 use hir_def::{
     body::Body,
     expr::{Expr, ExprId, UnaryOp},
+    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
     DefWithBodyId,
 };
 use hir_expand::diagnostics::DiagnosticSink;
@@ -70,7 +71,7 @@ pub fn unsafe_expressions(
 ) -> Vec<UnsafeExpr> {
     let mut unsafe_exprs = vec![];
     let body = db.body(def);
-    walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false);
+    walk_unsafe(&mut unsafe_exprs, db, infer, def, &body, body.body_expr, false);
 
     unsafe_exprs
 }
@@ -79,6 +80,7 @@ fn walk_unsafe(
     unsafe_exprs: &mut Vec<UnsafeExpr>,
     db: &dyn HirDatabase,
     infer: &InferenceResult,
+    def: DefWithBodyId,
     body: &Body,
     current: ExprId,
     inside_unsafe_block: bool,
@@ -97,6 +99,15 @@ fn walk_unsafe(
                 }
             }
         }
+        Expr::Path(path) => {
+            let resolver = resolver_for_expr(db.upcast(), def, current);
+            let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path());
+            if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
+                if db.static_data(id).mutable {
+                    unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
+                }
+            }
+        }
         Expr::MethodCall { .. } => {
             if infer
                 .method_resolution(current)
@@ -112,13 +123,13 @@ fn walk_unsafe(
             }
         }
         Expr::Unsafe { body: child } => {
-            return walk_unsafe(unsafe_exprs, db, infer, body, *child, true);
+            return walk_unsafe(unsafe_exprs, db, infer, def, body, *child, true);
         }
         _ => {}
     }
 
     expr.walk_child_exprs(|child| {
-        walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block);
+        walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
     });
 }
 
@@ -167,6 +178,27 @@ fn main() {
         HasUnsafe.unsafe_fn();
     }
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn missing_unsafe_diagnostic_with_static_mut() {
+        check_diagnostics(
+            r#"
+struct Ty {
+    a: u8,
+}
+
+static mut static_mut: Ty = Ty { a: 0 };
+
+fn main() {
+    let x = static_mut.a;
+          //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
+    unsafe {
+        let x = static_mut.a;
+    }
+}
 "#,
         );
     }

From a6532905a983c4a3f035fde95ec288dc751f833a Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 6 Aug 2020 21:15:31 -0400
Subject: [PATCH 07/76] Add test for unsafe union field access highlighting

---
 crates/ra_ide/src/syntax_highlighting/tests.rs | 11 +++++++++++
 crates/ra_ide/test_data/highlight_unsafe.html  | 11 +++++++++++
 2 files changed, 22 insertions(+)

diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 2deee404ccc..a09422da350 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -275,6 +275,11 @@ fn test_unsafe_highlighting() {
         r#"
 unsafe fn unsafe_fn() {}
 
+union Union {
+    a: u32,
+    b: f32,
+}
+
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
@@ -283,8 +288,14 @@ impl HasUnsafeFn {
 
 fn main() {
     let x = &5 as *const usize;
+    let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
+        let b = u.b;
+        match u {
+            Union { b: 0 } => (),
+            Union { a } => (),
+        }
         HasUnsafeFn.unsafe_method();
         let y = *(x);
         let z = -x;
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index b81b6f1c3b9..39582b5bb47 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -37,6 +37,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 </style>
 <pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 
+<span class="keyword">union</span> <span class="union declaration">Union</span> <span class="punctuation">{</span>
+    <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span>
+    <span class="field declaration">b</span><span class="punctuation">:</span> <span class="builtin_type">f32</span><span class="punctuation">,</span>
+<span class="punctuation">}</span>
+
 <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
 
 <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
@@ -45,8 +50,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="unresolved_reference">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="unresolved_reference">b</span><span class="punctuation">;</span>
+        <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="unresolved_reference">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="variable declaration">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+        <span class="punctuation">}</span>
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span>

From c04b2e39dacfea5c6ba158d842fb3b7f3e0db12b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=BCdiger=20Herrmann?= <ruediger.herrmann@gmx.de>
Date: Sat, 8 Aug 2020 11:57:54 +0200
Subject: [PATCH 08/76] Fix typo in settings description

Remove a duplicate word from the description of the `warningsAsHint` setting.
---
 editors/code/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/package.json b/editors/code/package.json
index 1adf055d0c0..ee5f96bf324 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -607,7 +607,7 @@
                     "items": {
                         "type": "string"
                     },
-                    "description": "List of warnings warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in the problems panel.",
+                    "description": "List of warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in the problems panel.",
                     "default": []
                 }
             }

From b1ec08e3ffb510e4f93dbf8a93eac6c98b88f8dc Mon Sep 17 00:00:00 2001
From: Veetaha <veetaha2@gmail.com>
Date: Sat, 8 Aug 2020 17:42:50 +0300
Subject: [PATCH 09/76] Remove clone

---
 crates/rust-analyzer/src/main_loop.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ceddb2b0563..438e965e087 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -47,7 +47,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
         SetThreadPriority(thread, thread_priority_above_normal);
     }
 
-    GlobalState::new(connection.sender.clone(), config).run(connection.receiver)
+    GlobalState::new(connection.sender, config).run(connection.receiver)
 }
 
 enum Event {

From a39d503ef3ba26ec324639e22e46e1a8173b397e Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sat, 8 Aug 2020 11:26:01 -0400
Subject: [PATCH 10/76] Add additional checks for union inference tests

---
 crates/ra_hir_ty/src/tests/simple.rs | 38 ++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 3fd7d5cd4ff..a2db6944df2 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -334,16 +334,44 @@ fn infer_union() {
             bar: f32,
         }
 
+        fn test() {
+            let u = MyUnion { foo: 0 };
+            unsafe { baz(u); }
+            let u = MyUnion { bar: 0.0 };
+            unsafe { baz(u); }
+        }
+
         unsafe fn baz(u: MyUnion) {
             let inner = u.foo;
+            let inner = u.bar;
         }
         "#,
         expect![[r#"
-            61..62 'u': MyUnion
-            73..99 '{     ...foo; }': ()
-            83..88 'inner': u32
-            91..92 'u': MyUnion
-            91..96 'u.foo': u32
+            57..172 '{     ...); } }': ()
+            67..68 'u': MyUnion
+            71..89 'MyUnio...o: 0 }': MyUnion
+            86..87 '0': i32
+            95..113 'unsafe...(u); }': ()
+            102..113 '{ baz(u); }': ()
+            104..107 'baz': fn baz(MyUnion)
+            104..110 'baz(u)': ()
+            108..109 'u': MyUnion
+            122..123 'u': MyUnion
+            126..146 'MyUnio... 0.0 }': MyUnion
+            141..144 '0.0': f64
+            152..170 'unsafe...(u); }': ()
+            159..170 '{ baz(u); }': ()
+            161..164 'baz': fn baz(MyUnion)
+            161..167 'baz(u)': ()
+            165..166 'u': MyUnion
+            188..189 'u': MyUnion
+            200..249 '{     ...bar; }': ()
+            210..215 'inner': u32
+            218..219 'u': MyUnion
+            218..223 'u.foo': u32
+            233..238 'inner': f32
+            241..242 'u': MyUnion
+            241..246 'u.bar': f32
         "#]],
     );
 }

From 3bf033e54814919f2214ca4e9b73cebc5ba7d86d Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Fri, 7 Aug 2020 09:24:20 -0400
Subject: [PATCH 11/76] Add support for unions in inference and lowering

---
 crates/ra_hir_ty/src/infer.rs                 | 11 +++++++----
 crates/ra_hir_ty/src/lower.rs                 |  5 ++++-
 crates/ra_hir_ty/src/tests/simple.rs          |  4 ++--
 crates/ra_ide/test_data/highlight_unsafe.html |  8 ++++----
 4 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 28f32a0a4de..3d12039a6d2 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -440,6 +440,12 @@ impl<'a> InferenceContext<'a> {
                 let ty = self.insert_type_vars(ty.subst(&substs));
                 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
             }
+            TypeNs::AdtId(AdtId::UnionId(u)) => {
+                let substs = Ty::substs_from_path(&ctx, path, u.into(), true);
+                let ty = self.db.ty(u.into());
+                let ty = self.insert_type_vars(ty.subst(&substs));
+                forbid_unresolved_segments((ty, Some(u.into())), unresolved)
+            }
             TypeNs::EnumVariantId(var) => {
                 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
                 let ty = self.db.ty(var.parent.into());
@@ -490,10 +496,7 @@ impl<'a> InferenceContext<'a> {
                 // FIXME potentially resolve assoc type
                 (Ty::Unknown, None)
             }
-            TypeNs::AdtId(AdtId::EnumId(_))
-            | TypeNs::AdtId(AdtId::UnionId(_))
-            | TypeNs::BuiltinType(_)
-            | TypeNs::TraitId(_) => {
+            TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
                 // FIXME diagnostic
                 (Ty::Unknown, None)
             }
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 1eacc6f95ed..7638f167b5b 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -518,6 +518,7 @@ impl Ty {
         let (segment, generic_def) = match resolved {
             ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
             ValueTyDefId::StructId(it) => (last, Some(it.into())),
+            ValueTyDefId::UnionId(it) => (last, Some(it.into())),
             ValueTyDefId::ConstId(it) => (last, Some(it.into())),
             ValueTyDefId::StaticId(_) => (last, None),
             ValueTyDefId::EnumVariantId(var) => {
@@ -1148,11 +1149,12 @@ impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefI
 pub enum ValueTyDefId {
     FunctionId(FunctionId),
     StructId(StructId),
+    UnionId(UnionId),
     EnumVariantId(EnumVariantId),
     ConstId(ConstId),
     StaticId(StaticId),
 }
-impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
+impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
 
 /// Build the declared type of an item. This depends on the namespace; e.g. for
 /// `struct Foo(usize)`, we have two types: The type of the struct itself, and
@@ -1179,6 +1181,7 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders
     match def {
         ValueTyDefId::FunctionId(it) => type_for_fn(db, it),
         ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
+        ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()),
         ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
         ValueTyDefId::ConstId(it) => type_for_const(db, it),
         ValueTyDefId::StaticId(it) => type_for_static(db, it),
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index a2db6944df2..5a7cf9455b5 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -350,7 +350,7 @@ fn infer_union() {
             57..172 '{     ...); } }': ()
             67..68 'u': MyUnion
             71..89 'MyUnio...o: 0 }': MyUnion
-            86..87 '0': i32
+            86..87 '0': u32
             95..113 'unsafe...(u); }': ()
             102..113 '{ baz(u); }': ()
             104..107 'baz': fn baz(MyUnion)
@@ -358,7 +358,7 @@ fn infer_union() {
             108..109 'u': MyUnion
             122..123 'u': MyUnion
             126..146 'MyUnio... 0.0 }': MyUnion
-            141..144 '0.0': f64
+            141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
             159..170 '{ baz(u); }': ()
             161..164 'baz': fn baz(MyUnion)
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 39582b5bb47..de70363da27 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -50,13 +50,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="unresolved_reference">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="unresolved_reference">b</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field">b</span><span class="punctuation">;</span>
         <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
-            <span class="union">Union</span> <span class="punctuation">{</span> <span class="unresolved_reference">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
-            <span class="union">Union</span> <span class="punctuation">{</span> <span class="variable declaration">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
         <span class="punctuation">}</span>
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>

From be935b2b56dcbda5a5918d8c600552b0adbb3a96 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Fri, 7 Aug 2020 09:33:40 -0400
Subject: [PATCH 12/76] Apply unsafe semantic highlighting to union field
 access

---
 crates/ra_ide/src/syntax_highlighting.rs      | 66 ++++++++++++++++---
 crates/ra_ide/test_data/highlight_unsafe.html |  6 +-
 2 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index a32ae0165e1..bfe6143ca50 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -4,7 +4,7 @@ mod injection;
 #[cfg(test)]
 mod tests;
 
-use hir::{Name, Semantics};
+use hir::{Name, Semantics, VariantDef};
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
     RootDatabase,
@@ -455,6 +455,18 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
     Some(TextRange::new(range_start, range_end))
 }
 
+fn is_possibly_unsafe(name_ref: &ast::NameRef) -> bool {
+    name_ref
+        .syntax()
+        .parent()
+        .and_then(|parent| {
+            ast::FieldExpr::cast(parent.clone())
+                .map(|_| true)
+                .or_else(|| ast::RecordPatField::cast(parent).map(|_| true))
+        })
+        .unwrap_or(false)
+}
+
 fn highlight_element(
     sema: &Semantics<RootDatabase>,
     bindings_shadow_count: &mut FxHashMap<Name, u32>,
@@ -484,10 +496,19 @@ fn highlight_element(
 
             match name_kind {
                 Some(NameClass::Definition(def)) => {
-                    highlight_name(db, def) | HighlightModifier::Definition
+                    highlight_name(db, def, false) | HighlightModifier::Definition
+                }
+                Some(NameClass::ConstReference(def)) => highlight_name(db, def, false),
+                Some(NameClass::FieldShorthand { field, .. }) => {
+                    let mut h = HighlightTag::Field.into();
+                    if let Definition::Field(field) = field {
+                        if let VariantDef::Union(_) = field.parent_def(db) {
+                            h |= HighlightModifier::Unsafe;
+                        }
+                    }
+
+                    h
                 }
-                Some(NameClass::ConstReference(def)) => highlight_name(db, def),
-                Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
                 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
             }
         }
@@ -498,6 +519,7 @@ fn highlight_element(
         }
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
+            let possibly_unsafe = is_possibly_unsafe(&name_ref);
             match classify_name_ref(sema, &name_ref) {
                 Some(name_kind) => match name_kind {
                     NameRefClass::Definition(def) => {
@@ -508,11 +530,13 @@ fn highlight_element(
                                 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                             }
                         };
-                        highlight_name(db, def)
+                        highlight_name(db, def, possibly_unsafe)
                     }
                     NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
                 },
-                None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
+                None if syntactic_name_ref_highlighting => {
+                    highlight_name_ref_by_syntax(name_ref, sema)
+                }
                 None => HighlightTag::UnresolvedReference.into(),
             }
         }
@@ -652,10 +676,19 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
-fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
+fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight {
     match def {
         Definition::Macro(_) => HighlightTag::Macro,
-        Definition::Field(_) => HighlightTag::Field,
+        Definition::Field(field) => {
+            let mut h = HighlightTag::Field.into();
+            if possibly_unsafe {
+                if let VariantDef::Union(_) = field.parent_def(db) {
+                    h |= HighlightModifier::Unsafe;
+                }
+            }
+
+            return h;
+        }
         Definition::ModuleDef(def) => match def {
             hir::ModuleDef::Module(_) => HighlightTag::Module,
             hir::ModuleDef::Function(func) => {
@@ -724,7 +757,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
     tag.into()
 }
 
-fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
+fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight {
     let default = HighlightTag::UnresolvedReference;
 
     let parent = match name.syntax().parent() {
@@ -734,7 +767,20 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
 
     let tag = match parent.kind() {
         METHOD_CALL_EXPR => HighlightTag::Function,
-        FIELD_EXPR => HighlightTag::Field,
+        FIELD_EXPR => {
+            let h = HighlightTag::Field;
+            let is_union = ast::FieldExpr::cast(parent)
+                .and_then(|field_expr| {
+                    let field = sema.resolve_field(&field_expr)?;
+                    Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
+                        true
+                    } else {
+                        false
+                    })
+                })
+                .unwrap_or(false);
+            return if is_union { h | HighlightModifier::Unsafe } else { h.into() };
+        }
         PATH_SEGMENT => {
             let path = match parent.parent().and_then(ast::Path::cast) {
                 Some(it) => it,
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index de70363da27..cfc872832ae 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -53,10 +53,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field">b</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span>
         <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
-            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
-            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
+            <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
         <span class="punctuation">}</span>
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>

From 6cde0b1aa0f6b8623c6b81b2396f4a0345891233 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sat, 8 Aug 2020 14:14:18 -0400
Subject: [PATCH 13/76] Add support for extern crate

This adds syntax highlighting, hover and goto def
functionality for extern crate
---
 .../ra_assists/src/handlers/add_turbo_fish.rs |   2 +-
 crates/ra_hir/src/semantics.rs                |  25 ++++-
 crates/ra_ide/src/display/short_label.rs      |   6 ++
 crates/ra_ide/src/goto_definition.rs          |  37 +++++--
 crates/ra_ide/src/hover.rs                    |  49 ++++++++-
 crates/ra_ide/src/references.rs               |   4 +-
 crates/ra_ide/src/syntax_highlighting.rs      |   2 +
 .../ra_ide/src/syntax_highlighting/tests.rs   |  17 +++
 .../test_data/highlight_extern_crate.html     |  40 +++++++
 crates/ra_ide_db/src/defs.rs                  | 100 +++++++++++-------
 crates/ra_ide_db/src/imports_locator.rs       |   2 +-
 11 files changed, 225 insertions(+), 59 deletions(-)
 create mode 100644 crates/ra_ide/test_data/highlight_extern_crate.html

diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index 0c565e89af8..537322a72c3 100644
--- a/crates/ra_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -41,7 +41,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
     let name_ref = ast::NameRef::cast(ident.parent())?;
     let def = match classify_name_ref(&ctx.sema, &name_ref)? {
         NameRefClass::Definition(def) => def,
-        NameRefClass::FieldShorthand { .. } => return None,
+        NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
     };
     let fun = match def {
         Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 307b336f206..e392130ab6d 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -8,7 +8,7 @@ use hir_def::{
     resolver::{self, HasResolver, Resolver},
     AsMacroCall, FunctionId, TraitId, VariantId,
 };
-use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
+use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, name::AsName, ExpansionInfo};
 use hir_ty::associated_type_shorthand_candidates;
 use itertools::Itertools;
 use ra_db::{FileId, FileRange};
@@ -24,8 +24,8 @@ use crate::{
     diagnostics::Diagnostic,
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
-    AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module,
-    ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
+    Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
 };
 use resolver::TypeNs;
 
@@ -228,6 +228,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_path(path)
     }
 
+    pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
+        self.imp.resolve_extern_crate(extern_crate)
+    }
+
     pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
         self.imp.resolve_variant(record_lit).map(VariantDef::from)
     }
@@ -443,6 +447,17 @@ impl<'db> SemanticsImpl<'db> {
         self.analyze(path.syntax()).resolve_path(self.db, path)
     }
 
+    fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option<Crate> {
+        let krate = self.scope(extern_crate.syntax()).krate()?;
+        krate.dependencies(self.db).into_iter().find_map(|dep| {
+            if dep.name == extern_crate.name_ref()?.as_name() {
+                Some(dep.krate)
+            } else {
+                None
+            }
+        })
+    }
+
     fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantId> {
         self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit)
     }
@@ -612,6 +627,10 @@ impl<'a> SemanticsScope<'a> {
         Some(Module { id: self.resolver.module()? })
     }
 
+    pub fn krate(&self) -> Option<Crate> {
+        Some(Crate { id: self.resolver.krate()? })
+    }
+
     /// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
     // FIXME: rename to visible_traits to not repeat scope?
     pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index 0fdf8e9a58d..b5ff9fa2deb 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -47,6 +47,12 @@ impl ShortLabel for ast::Module {
     }
 }
 
+impl ShortLabel for ast::SourceFile {
+    fn short_label(&self) -> Option<String> {
+        None
+    }
+}
+
 impl ShortLabel for ast::TypeAlias {
     fn short_label(&self) -> Option<String> {
         short_label_from_node(self, "type ")
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 4e3f428fae7..b44b6fe22f9 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,6 +1,6 @@
 use hir::Semantics;
 use ra_ide_db::{
-    defs::{classify_name, classify_name_ref, NameClass},
+    defs::{classify_name, classify_name_ref},
     symbol_index, RootDatabase,
 };
 use ra_syntax::{
@@ -40,10 +40,7 @@ pub(crate) fn goto_definition(
                 reference_definition(&sema, &name_ref).to_vec()
             },
             ast::Name(name) => {
-                let def = match classify_name(&sema, &name)? {
-                    NameClass::Definition(def) | NameClass::ConstReference(def) => def,
-                    NameClass::FieldShorthand { local: _, field } => field,
-                };
+                let def = classify_name(&sema, &name)?.definition(sema.db)?;
                 let nav = def.try_to_nav(sema.db)?;
                 vec![nav]
             },
@@ -85,9 +82,7 @@ pub(crate) fn reference_definition(
     name_ref: &ast::NameRef,
 ) -> ReferenceResult {
     let name_kind = classify_name_ref(sema, name_ref);
-    if let Some(def) = name_kind {
-        let def = def.definition();
-
+    if let Some(def) = name_kind.and_then(|def| def.definition(sema.db)) {
         return match def.try_to_nav(sema.db) {
             Some(nav) => ReferenceResult::Exact(nav),
             None => ReferenceResult::Approximate(Vec::new()),
@@ -133,6 +128,32 @@ mod tests {
         assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
     }
 
+    #[test]
+    fn goto_def_for_extern_crate() {
+        check(
+            r#"
+            //- /main.rs
+            extern crate std<|>;
+            //- /std/lib.rs
+            // empty
+            //^ file
+            "#,
+        )
+    }
+
+    #[test]
+    fn goto_def_for_renamed_extern_crate() {
+        check(
+            r#"
+            //- /main.rs
+            extern crate std as abc<|>;
+            //- /std/lib.rs
+            // empty
+            //^ file
+            "#,
+        )
+    }
+
     #[test]
     fn goto_def_in_items() {
         check(
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index aa48cb412ff..a632ea6a208 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -85,8 +85,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     let node = token.parent();
     let definition = match_ast! {
         match node {
-            ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition()),
-            ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()),
+            ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).and_then(|d| d.definition(sema.db)),
+            ast::Name(name) => classify_name(&sema, &name).and_then(|d| d.definition(sema.db)),
             _ => None,
         }
     };
@@ -304,7 +304,10 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
                     let docs = Documentation::from_ast(&it).map(Into::into);
                     hover_markup(docs, it.short_label(), mod_path)
                 }
-                _ => None,
+                ModuleSource::SourceFile(it) => {
+                    let docs = Documentation::from_ast(&it).map(Into::into);
+                    hover_markup(docs, it.short_label(), mod_path)
+                }
             },
             ModuleDef::Function(it) => from_def_source(db, it, mod_path),
             ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path),
@@ -1106,6 +1109,46 @@ fn bar() { fo<|>o(); }
         );
     }
 
+    #[test]
+    fn test_hover_extern_crate() {
+        check(
+            r#"
+//- /main.rs
+extern crate st<|>d;
+//- /std/lib.rs
+//! Standard library for this test
+//!
+//! Printed?
+//! abc123
+            "#,
+            expect![[r#"
+            *std*
+            Standard library for this test
+
+            Printed?
+            abc123
+            "#]],
+        );
+        check(
+            r#"
+//- /main.rs
+extern crate std as ab<|>c;
+//- /std/lib.rs
+//! Standard library for this test
+//!
+//! Printed?
+//! abc123
+            "#,
+            expect![[r#"
+            *abc*
+            Standard library for this test
+
+            Printed?
+            abc123
+            "#]],
+        );
+    }
+
     #[test]
     fn test_hover_mod_with_same_name_as_function() {
         check(
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index cf456630a5d..9dd228b9c38 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -130,13 +130,13 @@ fn find_name(
     opt_name: Option<ast::Name>,
 ) -> Option<RangeInfo<Definition>> {
     if let Some(name) = opt_name {
-        let def = classify_name(sema, &name)?.definition();
+        let def = classify_name(sema, &name)?.definition(sema.db)?;
         let range = name.syntax().text_range();
         return Some(RangeInfo::new(range, def));
     }
     let name_ref =
         sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
-    let def = classify_name_ref(sema, &name_ref)?.definition();
+    let def = classify_name_ref(sema, &name_ref)?.definition(sema.db)?;
     let range = name_ref.syntax().text_range();
     Some(RangeInfo::new(range, def))
 }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 89efe71dabc..ec442bcd837 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -483,6 +483,7 @@ fn highlight_element(
             };
 
             match name_kind {
+                Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(),
                 Some(NameClass::Definition(def)) => {
                     highlight_name(db, def) | HighlightModifier::Definition
                 }
@@ -500,6 +501,7 @@ fn highlight_element(
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
             match classify_name_ref(sema, &name_ref) {
                 Some(name_kind) => match name_kind {
+                    NameRefClass::ExternCrate(_) => HighlightTag::Module.into(),
                     NameRefClass::Definition(def) => {
                         if let Definition::Local(local) = &def {
                             if let Some(name) = local.name(db) {
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index b9b35802234..b8d60bdc632 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -380,6 +380,23 @@ macro_rules! noop {
     );
 }
 
+#[test]
+fn test_extern_crate() {
+    check_highlighting(
+        r#"
+        //- /main.rs
+        extern crate std;
+        extern crate alloc as abc;
+        //- /std/lib.rs
+        pub struct S;
+        //- /alloc/lib.rs
+        pub struct A
+        "#,
+        expect_file!["crates/ra_ide/test_data/highlight_extern_crate.html"],
+        false,
+    );
+}
+
 /// Highlights the code given by the `ra_fixture` argument, renders the
 /// result as HTML, and compares it with the HTML file given as `snapshot`.
 /// Note that the `snapshot` file is overwritten by the rendered HTML.
diff --git a/crates/ra_ide/test_data/highlight_extern_crate.html b/crates/ra_ide/test_data/highlight_extern_crate.html
new file mode 100644
index 00000000000..800d894c769
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_extern_crate.html
@@ -0,0 +1,40 @@
+
+<style>
+body                { margin: 0; }
+pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+
+.lifetime           { color: #DFAF8F; font-style: italic; }
+.comment            { color: #7F9F7F; }
+.documentation      { color: #629755; }
+.injected           { opacity: 0.65 ; }
+.struct, .enum      { color: #7CB8BB; }
+.enum_variant       { color: #BDE0F3; }
+.string_literal     { color: #CC9393; }
+.field              { color: #94BFF3; }
+.function           { color: #93E0E3; }
+.function.unsafe    { color: #BC8383; }
+.operator.unsafe    { color: #BC8383; }
+.parameter          { color: #94BFF3; }
+.text               { color: #DCDCCC; }
+.type               { color: #7CB8BB; }
+.builtin_type       { color: #8CD0D3; }
+.type_param         { color: #DFAF8F; }
+.attribute          { color: #94BFF3; }
+.numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
+.macro              { color: #94BFF3; }
+.module             { color: #AFD8AF; }
+.value_param        { color: #DCDCCC; }
+.variable           { color: #DCDCCC; }
+.format_specifier   { color: #CC696B; }
+.mutable            { text-decoration: underline; }
+.escape_sequence    { color: #94BFF3; }
+.keyword            { color: #F0DFAF; font-weight: bold; }
+.keyword.unsafe     { color: #BC8383; font-weight: bold; }
+.control            { font-style: italic; }
+
+.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+</style>
+<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="punctuation">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="punctuation">;</span>
+</code></pre>
\ No newline at end of file
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index b51000b03fb..e2a4f2983cd 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -6,8 +6,8 @@
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
 use hir::{
-    Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
-    Semantics, TypeParam, Visibility,
+    db::HirDatabase, Crate, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef,
+    Name, PathResolution, Semantics, TypeParam, Visibility,
 };
 use ra_prof::profile;
 use ra_syntax::{
@@ -80,6 +80,7 @@ impl Definition {
 
 #[derive(Debug)]
 pub enum NameClass {
+    ExternCrate(Crate),
     Definition(Definition),
     /// `None` in `if let None = Some(82) {}`
     ConstReference(Definition),
@@ -90,19 +91,21 @@ pub enum NameClass {
 }
 
 impl NameClass {
-    pub fn into_definition(self) -> Option<Definition> {
-        match self {
-            NameClass::Definition(it) => Some(it),
-            NameClass::ConstReference(_) => None,
-            NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)),
-        }
+    pub fn into_definition(self, db: &dyn HirDatabase) -> Option<Definition> {
+        Some(match self {
+            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db)?.into()),
+            NameClass::Definition(it) => it,
+            NameClass::ConstReference(_) => return None,
+            NameClass::FieldShorthand { local, field: _ } => Definition::Local(local),
+        })
     }
 
-    pub fn definition(self) -> Definition {
-        match self {
+    pub fn definition(self, db: &dyn HirDatabase) -> Option<Definition> {
+        Some(match self {
+            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db)?.into()),
             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
             NameClass::FieldShorthand { local: _, field } => field,
-        }
+        })
     }
 }
 
@@ -120,32 +123,37 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
     match_ast! {
         match parent {
             ast::Rename(it) => {
-                let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?;
-                let path = use_tree.path()?;
-                let path_segment = path.segment()?;
-                let name_ref_class = path_segment
-                    .name_ref()
-                    // The rename might be from a `self` token, so fallback to the name higher
-                    // in the use tree.
-                    .or_else(||{
-                        if path_segment.self_token().is_none() {
-                            return None;
-                        }
+                if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
+                    let path = use_tree.path()?;
+                    let path_segment = path.segment()?;
+                    let name_ref_class = path_segment
+                        .name_ref()
+                        // The rename might be from a `self` token, so fallback to the name higher
+                        // in the use tree.
+                        .or_else(||{
+                            if path_segment.self_token().is_none() {
+                                return None;
+                            }
 
-                        let use_tree = use_tree
-                            .syntax()
-                            .parent()
-                            .as_ref()
-                            // Skip over UseTreeList
-                            .and_then(SyntaxNode::parent)
-                            .and_then(ast::UseTree::cast)?;
-                        let path = use_tree.path()?;
-                        let path_segment = path.segment()?;
-                        path_segment.name_ref()
-                    })
-                    .and_then(|name_ref| classify_name_ref(sema, &name_ref))?;
+                            let use_tree = use_tree
+                                .syntax()
+                                .parent()
+                                .as_ref()
+                                // Skip over UseTreeList
+                                .and_then(SyntaxNode::parent)
+                                .and_then(ast::UseTree::cast)?;
+                            let path = use_tree.path()?;
+                            let path_segment = path.segment()?;
+                            path_segment.name_ref()
+                        })
+                        .and_then(|name_ref| classify_name_ref(sema, &name_ref))?;
 
-                Some(NameClass::Definition(name_ref_class.definition()))
+                    Some(NameClass::Definition(name_ref_class.definition(sema.db)?))
+                } else {
+                    let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
+                    let resolved = sema.resolve_extern_crate(&extern_crate)?;
+                    Some(NameClass::ExternCrate(resolved))
+                }
             },
             ast::IdentPat(it) => {
                 let local = sema.to_def(&it)?;
@@ -220,16 +228,20 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
 
 #[derive(Debug)]
 pub enum NameRefClass {
+    ExternCrate(Crate),
     Definition(Definition),
     FieldShorthand { local: Local, field: Definition },
 }
 
 impl NameRefClass {
-    pub fn definition(self) -> Definition {
-        match self {
+    pub fn definition(self, db: &dyn HirDatabase) -> Option<Definition> {
+        Some(match self {
+            NameRefClass::ExternCrate(krate) => {
+                Definition::ModuleDef(krate.root_module(db)?.into())
+            }
             NameRefClass::Definition(def) => def,
             NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local),
-        }
+        })
     }
 }
 
@@ -307,9 +319,15 @@ pub fn classify_name_ref(
         }
     }
 
-    let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
-    let resolved = sema.resolve_path(&path)?;
-    Some(NameRefClass::Definition(resolved.into()))
+    if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
+        if let Some(resolved) = sema.resolve_path(&path) {
+            return Some(NameRefClass::Definition(resolved.into()));
+        }
+    }
+
+    let extern_crate = ast::ExternCrate::cast(parent)?;
+    let resolved = sema.resolve_extern_crate(&extern_crate)?;
+    Some(NameRefClass::ExternCrate(resolved))
 }
 
 impl From<PathResolution> for Definition {
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index 1fba71ff85c..9e040973b3b 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -61,5 +61,5 @@ fn get_name_definition<'a>(
         candidate_node
     };
     let name = ast::Name::cast(candidate_name_node)?;
-    classify_name(sema, &name)?.into_definition()
+    classify_name(sema, &name)?.into_definition(sema.db)
 }

From e43811c1645f78818d5d7fe0054b54a462145847 Mon Sep 17 00:00:00 2001
From: Veetaha <veetaha2@gmail.com>
Date: Sat, 8 Aug 2020 21:53:38 +0300
Subject: [PATCH 14/76] Fix no inlay hints / unresolved tokens until manual
 edit

No we return ContentModified during the workspace loading. This signifies the language
client to retry the operation (i.e. the client will
continue polling the server while it returns ContentModified).
I believe that there might be cases of overly big projects where the backoff
logic we have setup in `sendRequestWithRetry` (which we use for inlay hints)
might bail too early (currently the largest retry standby time is 10 seconds).
However, I've tried on one of my project with 500+ dependencies and it is still enough.
---
 crates/rust-analyzer/src/main_loop.rs | 10 ++++++++++
 editors/code/src/util.ts              |  6 +++---
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ceddb2b0563..d69f7941d86 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -337,6 +337,16 @@ impl GlobalState {
     fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
         self.register_request(&req, request_received);
 
+        if self.status == Status::Loading {
+            self.respond(lsp_server::Response::new_err(
+                req.id,
+                // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
+                lsp_server::ErrorCode::ContentModified as i32,
+                "Rust Analyzer is still loading...".to_owned(),
+            ));
+            return Ok(());
+        }
+
         RequestDispatcher { req: Some(req), global_state: self }
             .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
             .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 970fedb3787..49d2d1c6fbe 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -64,7 +64,8 @@ export async function sendRequestWithRetry<TParam, TRet>(
     param: TParam,
     token?: vscode.CancellationToken,
 ): Promise<TRet> {
-    for (const delay of [2, 4, 6, 8, 10, null]) {
+    // The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3...
+    for (const delay of [40, 160, 640, 2560, 10240, null]) {
         try {
             return await (token
                 ? client.sendRequest(reqType, param, token)
@@ -84,8 +85,7 @@ export async function sendRequestWithRetry<TParam, TRet>(
                 log.warn("LSP request failed", { method: reqType.method, param, error });
                 throw error;
             }
-
-            await sleep(10 * (1 << delay));
+            await sleep(delay);
         }
     }
     throw 'unreachable';

From 3f2bc813d3b91fbf8a0007eb11c192061571873b Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Sun, 9 Aug 2020 21:33:14 +0800
Subject: [PATCH 15/76] format in to_proto::markup_content

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_ide/src/hover.rs           | 31 ++++++++++++++++++++++++++++
 crates/rust-analyzer/src/to_proto.rs |  3 ++-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index aa48cb412ff..385e3e64e39 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -508,6 +508,37 @@ fn main() { }
         );
     }
 
+    #[test]
+    fn hover_shows_fn_doc() {
+        check(
+            r#"
+/// # Example
+/// ```
+/// # use std::path::Path;
+/// #
+/// foo(Path::new("hello, world!"))
+/// ```
+pub fn foo<|>(_: &Path) {}
+
+fn main() { }
+"#,
+            expect![[r#"
+                *foo*
+                ```rust
+                pub fn foo(_: &Path)
+                ```
+                ___
+
+                # Example
+                ```
+                # use std::path::Path;
+                #
+                foo(Path::new("hello, world!"))
+                ```
+            "#]],
+        );
+    }
+
     #[test]
     fn hover_shows_struct_field_info() {
         // Hovering over the field when instantiating
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 5eba1f15553..27460db78cc 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -755,7 +755,8 @@ pub(crate) fn runnable(
 }
 
 pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
-    lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value: markup.into() }
+    let value = crate::markdown::format_docs(markup.as_str());
+    lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
 }
 
 #[cfg(test)]

From dbe7ede2eebc1301a40f0e1a3a408e11a86a0e84 Mon Sep 17 00:00:00 2001
From: Veetaha <veetaha2@gmail.com>
Date: Mon, 10 Aug 2020 00:41:48 +0300
Subject: [PATCH 16/76] Let shutdown request to pass through when status ==
 Loading

---
 crates/rust-analyzer/src/main_loop.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index d69f7941d86..eb7c96933c0 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -337,7 +337,7 @@ impl GlobalState {
     fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
         self.register_request(&req, request_received);
 
-        if self.status == Status::Loading {
+        if self.status == Status::Loading && req.method != "shutdown" {
             self.respond(lsp_server::Response::new_err(
                 req.id,
                 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)

From 7252babc136b351d5011a78878607c7aaeea5ed8 Mon Sep 17 00:00:00 2001
From: Jeremy Kolb <kjeremy@gmail.com>
Date: Sun, 9 Aug 2020 17:57:27 -0400
Subject: [PATCH 17/76] Remove workaround for semantic token flickering

See: https://github.com/microsoft/vscode-languageserver-node/issues/576#issuecomment-593384479

This has been fixed since vscode 1.44
---
 editors/code/src/client.ts | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 18948cb3c4c..bd7a150f0a5 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,7 +4,7 @@ import * as ra from '../src/lsp_ext';
 import * as Is from 'vscode-languageclient/lib/utils/is';
 
 import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
-import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
+import { SemanticTokensFeature } from 'vscode-languageclient/lib/semanticTokens.proposed';
 import { assert } from './util';
 
 function renderCommand(cmd: ra.CommandLink) {
@@ -44,12 +44,6 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
         diagnosticCollectionName: "rustc",
         traceOutputChannel,
         middleware: {
-            // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576
-            async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken, next: DocumentSemanticsTokensSignature) {
-                const res = await next(document, token);
-                if (res === undefined) throw new Error('busy');
-                return res;
-            },
             async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) {
                 return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then(
                     (result) => {

From 58c97bdcbf6d7df218028f75373cfc4916043693 Mon Sep 17 00:00:00 2001
From: Jeremy Kolb <kjeremy@gmail.com>
Date: Sun, 9 Aug 2020 18:09:27 -0400
Subject: [PATCH 18/76] Remove 'as any'

---
 editors/code/src/client.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index bd7a150f0a5..f5db55b8cc3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -129,7 +129,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
                 );
             }
 
-        } as any
+        }
     };
 
     const client = new lc.LanguageClient(

From bf9b4578bbe038501ef7c337e22b448de477f61c Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sun, 9 Aug 2020 18:52:19 -0400
Subject: [PATCH 19/76] Remove Option<...> from result of Crate::root_module

There doesn't seem to be any need for it, and removing it simplies
several paths of code that depend on it.
---
 crates/ra_assists/src/utils.rs                |  2 +-
 crates/ra_hir/src/code_model.rs               |  4 ++--
 crates/ra_ide/src/goto_definition.rs          |  5 +++--
 crates/ra_ide/src/hover.rs                    |  4 ++--
 crates/ra_ide/src/references.rs               |  4 ++--
 crates/ra_ide_db/src/defs.rs                  | 22 +++++++++----------
 .../rust-analyzer/src/cli/analysis_stats.rs   |  2 +-
 crates/rust-analyzer/src/cli/diagnostics.rs   |  2 +-
 8 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 54d5678d14d..0de6fdf3fb8 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -257,7 +257,7 @@ pub use prelude::*;
             .find(|dep| &dep.name.to_string() == std_crate)?
             .krate;
 
-        let mut module = std_crate.root_module(db)?;
+        let mut module = std_crate.root_module(db);
         for segment in path {
             module = module.children(db).find_map(|child| {
                 let name = child.name(db)?;
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 27cdabea03e..44456e49e2b 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -83,9 +83,9 @@ impl Crate {
             .collect()
     }
 
-    pub fn root_module(self, db: &dyn HirDatabase) -> Option<Module> {
+    pub fn root_module(self, db: &dyn HirDatabase) -> Module {
         let module_id = db.crate_def_map(self.id).root;
-        Some(Module::new(self, module_id))
+        Module::new(self, module_id)
     }
 
     pub fn root_file(self, db: &dyn HirDatabase) -> FileId {
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index b44b6fe22f9..45389fd23fe 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -40,7 +40,7 @@ pub(crate) fn goto_definition(
                 reference_definition(&sema, &name_ref).to_vec()
             },
             ast::Name(name) => {
-                let def = classify_name(&sema, &name)?.definition(sema.db)?;
+                let def = classify_name(&sema, &name)?.definition(sema.db);
                 let nav = def.try_to_nav(sema.db)?;
                 vec![nav]
             },
@@ -82,7 +82,8 @@ pub(crate) fn reference_definition(
     name_ref: &ast::NameRef,
 ) -> ReferenceResult {
     let name_kind = classify_name_ref(sema, name_ref);
-    if let Some(def) = name_kind.and_then(|def| def.definition(sema.db)) {
+    if let Some(def) = name_kind {
+        let def = def.definition(sema.db);
         return match def.try_to_nav(sema.db) {
             Some(nav) => ReferenceResult::Exact(nav),
             None => ReferenceResult::Approximate(Vec::new()),
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index a632ea6a208..f6e493817bd 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -85,8 +85,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
     let node = token.parent();
     let definition = match_ast! {
         match node {
-            ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).and_then(|d| d.definition(sema.db)),
-            ast::Name(name) => classify_name(&sema, &name).and_then(|d| d.definition(sema.db)),
+            ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)),
+            ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition(sema.db)),
             _ => None,
         }
     };
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 9dd228b9c38..453985de3e3 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -130,13 +130,13 @@ fn find_name(
     opt_name: Option<ast::Name>,
 ) -> Option<RangeInfo<Definition>> {
     if let Some(name) = opt_name {
-        let def = classify_name(sema, &name)?.definition(sema.db)?;
+        let def = classify_name(sema, &name)?.definition(sema.db);
         let range = name.syntax().text_range();
         return Some(RangeInfo::new(range, def));
     }
     let name_ref =
         sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
-    let def = classify_name_ref(sema, &name_ref)?.definition(sema.db)?;
+    let def = classify_name_ref(sema, &name_ref)?.definition(sema.db);
     let range = name_ref.syntax().text_range();
     Some(RangeInfo::new(range, def))
 }
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index e2a4f2983cd..9bb95277d0d 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -93,19 +93,19 @@ pub enum NameClass {
 impl NameClass {
     pub fn into_definition(self, db: &dyn HirDatabase) -> Option<Definition> {
         Some(match self {
-            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db)?.into()),
+            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
             NameClass::Definition(it) => it,
             NameClass::ConstReference(_) => return None,
             NameClass::FieldShorthand { local, field: _ } => Definition::Local(local),
         })
     }
 
-    pub fn definition(self, db: &dyn HirDatabase) -> Option<Definition> {
-        Some(match self {
-            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db)?.into()),
+    pub fn definition(self, db: &dyn HirDatabase) -> Definition {
+        match self {
+            NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
             NameClass::FieldShorthand { local: _, field } => field,
-        })
+        }
     }
 }
 
@@ -148,7 +148,7 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
                         })
                         .and_then(|name_ref| classify_name_ref(sema, &name_ref))?;
 
-                    Some(NameClass::Definition(name_ref_class.definition(sema.db)?))
+                    Some(NameClass::Definition(name_ref_class.definition(sema.db)))
                 } else {
                     let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
                     let resolved = sema.resolve_extern_crate(&extern_crate)?;
@@ -234,14 +234,12 @@ pub enum NameRefClass {
 }
 
 impl NameRefClass {
-    pub fn definition(self, db: &dyn HirDatabase) -> Option<Definition> {
-        Some(match self {
-            NameRefClass::ExternCrate(krate) => {
-                Definition::ModuleDef(krate.root_module(db)?.into())
-            }
+    pub fn definition(self, db: &dyn HirDatabase) -> Definition {
+        match self {
+            NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
             NameRefClass::Definition(def) => def,
             NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local),
-        })
+        }
     }
 }
 
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 721d41a58f8..0d386841e52 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -72,7 +72,7 @@ impl AnalysisStatsCmd {
             shuffle(&mut rng, &mut krates);
         }
         for krate in krates {
-            let module = krate.root_module(db).expect("crate without root module");
+            let module = krate.root_module(db);
             let file_id = module.definition_source(db).file_id;
             let file_id = file_id.original_file(db);
             let source_root = db.file_source_root(file_id);
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 4ac8c8772eb..f17fc5dfe90 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -28,7 +28,7 @@ pub fn diagnostics(
     let mut work = Vec::new();
     let krates = Crate::all(db);
     for krate in krates {
-        let module = krate.root_module(db).expect("crate without root module");
+        let module = krate.root_module(db);
         let file_id = module.definition_source(db).file_id;
         let file_id = file_id.original_file(db);
         let source_root = db.file_source_root(file_id);

From 4d9c8821e5c328f29b77667c86cabb3689947fd2 Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Mon, 10 Aug 2020 14:02:40 +0800
Subject: [PATCH 20/76] Show const body in short_label

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_ide/src/display/short_label.rs | 10 +++++++++-
 crates/ra_ide/src/hover.rs               |  8 ++++----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index b5ff9fa2deb..d8acb3be764 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -61,7 +61,15 @@ impl ShortLabel for ast::TypeAlias {
 
 impl ShortLabel for ast::Const {
     fn short_label(&self) -> Option<String> {
-        short_label_from_ty(self, self.ty(), "const ")
+        match short_label_from_ty(self, self.ty(), "const ") {
+            Some(buf) => {
+                let mut new_buf = buf;
+                let body = self.body().unwrap();
+                format_to!(new_buf, " = {}", body.syntax());
+                Some(new_buf)
+            }
+            None => None,
+        }
     }
 }
 
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 9c7348898a1..f66f62bfb5e 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -590,16 +590,16 @@ fn main() {
     #[test]
     fn hover_const_static() {
         check(
-            r#"const foo<|>: u32 = 0;"#,
+            r#"const foo<|>: u32 = 123;"#,
             expect![[r#"
                 *foo*
                 ```rust
-                const foo: u32
+                const foo: u32 = 123
                 ```
             "#]],
         );
         check(
-            r#"static foo<|>: u32 = 0;"#,
+            r#"static foo<|>: u32 = 456;"#,
             expect![[r#"
                 *foo*
                 ```rust
@@ -834,7 +834,7 @@ fn main() {
             expect![[r#"
                 *C*
                 ```rust
-                const C: u32
+                const C: u32 = 1
                 ```
             "#]],
         )

From 958b91c1e8394129216d1b8378d726f937592d3f Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Mon, 10 Aug 2020 17:51:45 +0800
Subject: [PATCH 21/76] Better codes

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_ide/src/display/short_label.rs | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index d8acb3be764..010c34705c7 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -61,15 +61,11 @@ impl ShortLabel for ast::TypeAlias {
 
 impl ShortLabel for ast::Const {
     fn short_label(&self) -> Option<String> {
-        match short_label_from_ty(self, self.ty(), "const ") {
-            Some(buf) => {
-                let mut new_buf = buf;
-                let body = self.body().unwrap();
-                format_to!(new_buf, " = {}", body.syntax());
-                Some(new_buf)
-            }
-            None => None,
+        let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?;
+        if let Some(expr) = self.body() {
+            format_to!(new_buf, " = {}", expr.syntax());
         }
+        Some(new_buf)
     }
 }
 

From 6344a7f362b19eaf71547766135ece160aa3389e Mon Sep 17 00:00:00 2001
From: Igor Aleksanov <popzxc@yandex.ru>
Date: Mon, 10 Aug 2020 15:05:01 +0300
Subject: [PATCH 22/76] Fix clippy warnings

---
 crates/expect/src/lib.rs                      |  2 +-
 crates/flycheck/src/lib.rs                    |  6 +++---
 crates/ra_arena/src/map.rs                    | 10 +++++-----
 crates/ra_mbe/src/mbe_expander/matcher.rs     |  2 +-
 .../ra_parser/src/grammar/expressions/atom.rs |  8 +++-----
 crates/ra_proc_macro/src/process.rs           |  2 +-
 crates/ra_text_edit/src/lib.rs                | 13 ++++++++----
 crates/ra_tt/src/lib.rs                       |  2 +-
 crates/stdx/src/lib.rs                        |  4 ++--
 crates/vfs/src/file_set.rs                    |  3 +++
 xtask/src/codegen/gen_syntax.rs               | 20 +++++++++----------
 11 files changed, 38 insertions(+), 34 deletions(-)

diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs
index 21a458d477c..bd83895f782 100644
--- a/crates/expect/src/lib.rs
+++ b/crates/expect/src/lib.rs
@@ -74,7 +74,7 @@ impl fmt::Display for Position {
 impl Expect {
     pub fn assert_eq(&self, actual: &str) {
         let trimmed = self.trimmed();
-        if &trimmed == actual {
+        if trimmed == actual {
             return;
         }
         Runtime::fail_expect(self, &trimmed, actual);
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 7c38f5ef9d5..36e0e085ac5 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -108,7 +108,7 @@ struct FlycheckActor {
 
 enum Event {
     Restart(Restart),
-    CheckEvent(Option<cargo_metadata::Message>),
+    CheckEvent(Option<Box<cargo_metadata::Message>>),
 }
 
 impl FlycheckActor {
@@ -123,7 +123,7 @@ impl FlycheckActor {
         let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
         select! {
             recv(inbox) -> msg => msg.ok().map(Event::Restart),
-            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
+            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok().map(Box::new))),
         }
     }
     fn run(mut self, inbox: Receiver<Restart>) {
@@ -149,7 +149,7 @@ impl FlycheckActor {
                     let res = cargo_handle.join();
                     self.send(Message::Progress(Progress::DidFinish(res)));
                 }
-                Event::CheckEvent(Some(message)) => match message {
+                Event::CheckEvent(Some(message)) => match *message {
                     cargo_metadata::Message::CompilerArtifact(msg) => {
                         self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
                     }
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 0f33907c0ae..c1b58712c16 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -13,18 +13,18 @@ pub struct ArenaMap<ID, V> {
 
 impl<T, V> ArenaMap<Idx<T>, V> {
     pub fn insert(&mut self, id: Idx<T>, t: V) {
-        let idx = Self::to_idx(id);
+        let idx = Self::into_idx(id);
 
         self.v.resize_with((idx + 1).max(self.v.len()), || None);
         self.v[idx] = Some(t);
     }
 
     pub fn get(&self, id: Idx<T>) -> Option<&V> {
-        self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref())
+        self.v.get(Self::into_idx(id)).and_then(|it| it.as_ref())
     }
 
     pub fn get_mut(&mut self, id: Idx<T>) -> Option<&mut V> {
-        self.v.get_mut(Self::to_idx(id)).and_then(|it| it.as_mut())
+        self.v.get_mut(Self::into_idx(id)).and_then(|it| it.as_mut())
     }
 
     pub fn values(&self) -> impl Iterator<Item = &V> {
@@ -39,7 +39,7 @@ impl<T, V> ArenaMap<Idx<T>, V> {
         self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
     }
 
-    fn to_idx(id: Idx<T>) -> usize {
+    fn into_idx(id: Idx<T>) -> usize {
         u32::from(id.into_raw()) as usize
     }
 
@@ -51,7 +51,7 @@ impl<T, V> ArenaMap<Idx<T>, V> {
 impl<T, V> std::ops::Index<Idx<V>> for ArenaMap<Idx<V>, T> {
     type Output = T;
     fn index(&self, id: Idx<V>) -> &T {
-        self.v[Self::to_idx(id)].as_ref().unwrap()
+        self.v[Self::into_idx(id)].as_ref().unwrap()
     }
 }
 
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs
index f9e515b8118..933a3a3b5e3 100644
--- a/crates/ra_mbe/src/mbe_expander/matcher.rs
+++ b/crates/ra_mbe/src/mbe_expander/matcher.rs
@@ -276,7 +276,7 @@ impl<'a> TtIter<'a> {
         Ok(tt::Subtree {
             delimiter: None,
             token_trees: vec![
-                tt::Leaf::Punct(punct.clone()).into(),
+                tt::Leaf::Punct(*punct).into(),
                 tt::Leaf::Ident(ident.clone()).into(),
             ],
         }
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index 0b01d3bc646..ca6569c9f25 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -243,12 +243,10 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker {
         // test lambda_ret_block
         // fn main() { || -> i32 { 92 }(); }
         block_expr(p);
+    } else if p.at_ts(EXPR_FIRST) {
+        expr(p);
     } else {
-        if p.at_ts(EXPR_FIRST) {
-            expr(p);
-        } else {
-            p.error("expected expression");
-        }
+        p.error("expected expression");
     }
     m.complete(p, CLOSURE_EXPR)
 }
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index 5bcdacb4872..37dd3f49656 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -90,7 +90,7 @@ impl ProcMacroProcessSrv {
             }
             Some(it) => it,
         };
-        sender.send(Task { req: req.into(), result_tx }).unwrap();
+        sender.send(Task { req, result_tx }).unwrap();
         let res = result_rx
             .recv()
             .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index 25554f583ec..d68791cf1fd 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -76,10 +76,6 @@ impl TextEdit {
         self.indels.iter()
     }
 
-    pub fn into_iter(self) -> vec::IntoIter<Indel> {
-        self.indels.into_iter()
-    }
-
     pub fn apply(&self, text: &mut String) {
         match self.len() {
             0 => return,
@@ -141,6 +137,15 @@ impl TextEdit {
     }
 }
 
+impl IntoIterator for TextEdit {
+    type Item = Indel;
+    type IntoIter = vec::IntoIter<Self::Item>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.indels.into_iter()
+    }
+}
+
 impl TextEditBuilder {
     pub fn replace(&mut self, range: TextRange, replace_with: String) {
         self.indels.push(Indel::replace(range, replace_with))
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index 8faf1cc679c..20c3f5eabfb 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -107,7 +107,7 @@ fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usi
         for (idx, child) in subtree.token_trees.iter().enumerate() {
             print_debug_token(f, child, level + 1)?;
             if idx != subtree.token_trees.len() - 1 {
-                writeln!(f, "")?;
+                writeln!(f)?;
             }
         }
     }
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index b65875c96e0..00bfcd29ed0 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -10,7 +10,7 @@ pub fn is_ci() -> bool {
 
 pub trait SepBy: Sized {
     /// Returns an `impl fmt::Display`, which joins elements via a separator.
-    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
+    fn sep_by(self, sep: &str) -> SepByBuilder<'_, Self>;
 }
 
 impl<I> SepBy for I
@@ -18,7 +18,7 @@ where
     I: Iterator,
     I::Item: fmt::Display,
 {
-    fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self> {
+    fn sep_by(self, sep: &str) -> SepByBuilder<'_, Self> {
         SepByBuilder::new(sep, self)
     }
 }
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index e9196fcd2f9..9f11268eeed 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -19,6 +19,9 @@ impl FileSet {
     pub fn len(&self) -> usize {
         self.files.len()
     }
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
     pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
         let mut base = self.paths[&anchor].clone();
         base.pop();
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index cafad8070d2..af9d63b06e7 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -91,18 +91,16 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
                             support::children(&self.syntax)
                         }
                     }
-                } else {
-                    if let Some(token_kind) = field.token_kind() {
-                        quote! {
-                            pub fn #method_name(&self) -> Option<#ty> {
-                                support::token(&self.syntax, #token_kind)
-                            }
+                } else if let Some(token_kind) = field.token_kind() {
+                    quote! {
+                        pub fn #method_name(&self) -> Option<#ty> {
+                            support::token(&self.syntax, #token_kind)
                         }
-                    } else {
-                        quote! {
-                            pub fn #method_name(&self) -> Option<#ty> {
-                                support::child(&self.syntax)
-                            }
+                    }
+                } else {
+                    quote! {
+                        pub fn #method_name(&self) -> Option<#ty> {
+                            support::child(&self.syntax)
                         }
                     }
                 }

From 263f9a7f231a474dd56d02adbcd7c57d079e88fd Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Wed, 3 Jun 2020 23:38:25 -0400
Subject: [PATCH 23/76] Add tracking of packed repr, use it to highlight unsafe
 refs

Taking a reference to a misaligned field on a packed struct is an
unsafe operation. Highlight that behavior. Currently, the misaligned
part isn't tracked, so this highlight is a bit too aggressive.
---
 crates/ra_hir/src/code_model.rs               | 18 ++++++
 crates/ra_hir_def/src/adt.rs                  | 56 +++++++++++++++++--
 crates/ra_ide/src/syntax_highlighting.rs      | 24 ++++++++
 .../ra_ide/src/syntax_highlighting/tests.rs   | 11 ++++
 4 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 44456e49e2b..6f9c56d2943 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -4,6 +4,7 @@ use std::{iter, sync::Arc};
 use arrayvec::ArrayVec;
 use either::Either;
 use hir_def::{
+    adt::ReprKind,
     adt::StructKind,
     adt::VariantData,
     builtin_type::BuiltinType,
@@ -431,6 +432,10 @@ impl Struct {
         Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id)
     }
 
+    pub fn is_packed(self, db: &dyn HirDatabase) -> bool {
+        matches!(db.struct_data(self.id).repr, Some(ReprKind::Packed))
+    }
+
     fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
         db.struct_data(self.id).variant_data.clone()
     }
@@ -1253,6 +1258,19 @@ impl Type {
         )
     }
 
+    pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
+        let adt_id = match self.ty.value {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id,
+            _ => return false,
+        };
+
+        let adt = adt_id.into();
+        match adt {
+            Adt::Struct(s) => s.is_packed(db),
+            _ => false,
+        }
+    }
+
     pub fn is_raw_ptr(&self) -> bool {
         matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
     }
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 6cb56a1cd00..6d59c864280 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -9,11 +9,13 @@ use hir_expand::{
 };
 use ra_arena::{map::ArenaMap, Arena};
 use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
+use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
 
 use crate::{
+    attr::AttrInput,
     body::{CfgExpander, LowerCtx},
     db::DefDatabase,
-    item_tree::{Field, Fields, ItemTree},
+    item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
     src::HasChildSource,
     src::HasSource,
     trace::Trace,
@@ -29,6 +31,7 @@ use ra_cfg::CfgOptions;
 pub struct StructData {
     pub name: Name,
     pub variant_data: Arc<VariantData>,
+    pub repr: Option<ReprKind>,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -58,26 +61,71 @@ pub struct FieldData {
     pub visibility: RawVisibility,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ReprKind {
+    Packed,
+    Other,
+}
+
+fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
+    item_tree.attrs(of).iter().find_map(|a| {
+        if a.path.segments[0].to_string() == "repr" {
+            if let Some(AttrInput::TokenTree(subtree)) = &a.input {
+                parse_repr_tt(subtree)
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    })
+}
+
+fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
+    match tt.delimiter {
+        Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
+        _ => return None,
+    }
+
+    let mut it = tt.token_trees.iter();
+    match it.next() {
+        None => None,
+        Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => {
+            Some(ReprKind::Packed)
+        }
+        _ => Some(ReprKind::Other),
+    }
+}
+
 impl StructData {
     pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
         let loc = id.lookup(db);
         let item_tree = db.item_tree(loc.id.file_id);
+        let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
         let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
 
         let strukt = &item_tree[loc.id.value];
         let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
-
-        Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) })
+        Arc::new(StructData {
+            name: strukt.name.clone(),
+            variant_data: Arc::new(variant_data),
+            repr,
+        })
     }
     pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
         let loc = id.lookup(db);
         let item_tree = db.item_tree(loc.id.file_id);
+        let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into());
         let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
 
         let union = &item_tree[loc.id.value];
         let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
 
-        Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) })
+        Arc::new(StructData {
+            name: union.name.clone(),
+            variant_data: Arc::new(variant_data),
+            repr,
+        })
     }
 }
 
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 6b7874460a2..0cab684eb47 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -565,6 +565,30 @@ fn highlight_element(
                 _ => h,
             }
         }
+        REF_EXPR => {
+            let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?;
+            let expr = ref_expr.expr()?;
+            let field_expr = match expr {
+                ast::Expr::FieldExpr(fe) => fe,
+                _ => return None,
+            };
+
+            let expr = field_expr.expr()?;
+            let ty = match sema.type_of_expr(&expr) {
+                Some(ty) => ty,
+                None => {
+                    println!("No type :(");
+                    return None;
+                }
+            };
+            if !ty.is_packed(db) {
+                return None;
+            }
+
+            // FIXME account for alignment... somehow
+
+            Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe
+        }
         p if p.is_punct() => match p {
             T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => {
                 HighlightTag::Operator.into()
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 09062c38e75..f2c078d3479 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -292,6 +292,13 @@ struct TypeForStaticMut {
 
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
+#[repr(packed)]
+struct Packed {
+    a: u16,
+    b: u8,
+    c: u32,
+}
+
 fn main() {
     let x = &5 as *const usize;
     let u = Union { b: 0 };
@@ -306,6 +313,10 @@ fn main() {
         let y = *(x);
         let z = -x;
         let a = global_mut.a;
+        let packed = Packed { a: 0, b: 0, c: 0 };
+        let a = &packed.a;
+        let b = &packed.b;
+        let c = &packed.c;
     }
 }
 "#

From fd30134cf84b134259fe8140e513b152e37f3f88 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <nashenas88@users.noreply.github.com>
Date: Mon, 15 Jun 2020 07:19:45 -0400
Subject: [PATCH 24/76] Remove token tree from ReprKind::Other variant, expose
 ReprKind higher, remove debug println.

---
 crates/ra_hir/src/code_model.rs          | 6 +++---
 crates/ra_ide/src/syntax_highlighting.rs | 8 +-------
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 6f9c56d2943..0007d7fa881 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -432,8 +432,8 @@ impl Struct {
         Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id)
     }
 
-    pub fn is_packed(self, db: &dyn HirDatabase) -> bool {
-        matches!(db.struct_data(self.id).repr, Some(ReprKind::Packed))
+    pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
+        db.struct_data(self.id).repr.clone()
     }
 
     fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
@@ -1266,7 +1266,7 @@ impl Type {
 
         let adt = adt_id.into();
         match adt {
-            Adt::Struct(s) => s.is_packed(db),
+            Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
             _ => false,
         }
     }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0cab684eb47..b82b51efda5 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -574,13 +574,7 @@ fn highlight_element(
             };
 
             let expr = field_expr.expr()?;
-            let ty = match sema.type_of_expr(&expr) {
-                Some(ty) => ty,
-                None => {
-                    println!("No type :(");
-                    return None;
-                }
-            };
+            let ty = sema.type_of_expr(&expr)?;
             if !ty.is_packed(db) {
                 return None;
             }

From 4a4b1f48efeff4ebe578eb92b7bb8338d0181a83 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <nashenas88@users.noreply.github.com>
Date: Mon, 15 Jun 2020 07:41:13 -0400
Subject: [PATCH 25/76] Limit scope of unsafe to & instead of all ref exprs,
 add test showing missing support for autoref behavior

---
 crates/ra_ide/src/syntax_highlighting.rs      |  2 +-
 .../ra_ide/src/syntax_highlighting/tests.rs   | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index b82b51efda5..c5098189b39 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -565,7 +565,7 @@ fn highlight_element(
                 _ => h,
             }
         }
-        REF_EXPR => {
+        T![&] => {
             let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?;
             let expr = ref_expr.expr()?;
             let field_expr = match expr {
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index f2c078d3479..c4080585011 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -299,6 +299,23 @@ struct Packed {
     c: u32,
 }
 
+trait DoTheAutoref {
+    fn calls_autoref(&self);
+}
+
+struct NeedsAlign {
+    a: u16
+}
+
+#[repr(packed)]
+struct HasAligned {
+    a: NeedsAlign
+}
+
+impl DoTheAutoref for NeedsAlign {
+    fn calls_autored(&self) {}
+}
+
 fn main() {
     let x = &5 as *const usize;
     let u = Union { b: 0 };
@@ -317,6 +334,8 @@ fn main() {
         let a = &packed.a;
         let b = &packed.b;
         let c = &packed.c;
+        let h = HasAligned{ a: NeedsAlign { a: 1 } };
+        h.a.calls_autoref();
     }
 }
 "#

From c9e670b8754b8262b5071a96c32cbcd22ff968f4 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <nashenas88@users.noreply.github.com>
Date: Mon, 15 Jun 2020 08:21:32 -0400
Subject: [PATCH 26/76] Update FIXME comment to be more useful

---
 crates/ra_ide/src/syntax_highlighting.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index c5098189b39..9e8419c5f84 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -579,7 +579,8 @@ fn highlight_element(
                 return None;
             }
 
-            // FIXME account for alignment... somehow
+            // FIXME This needs layout computation to be correct. It will highlight
+            // more than it should with the current implementation.
 
             Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe
         }

From 38440d53d8329ac9f3f2013c6e32b3f69b069c72 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sat, 27 Jun 2020 14:42:42 -0400
Subject: [PATCH 27/76] Cleanup repr check, fix packed repr check and test

---
 crates/ra_hir_def/src/adt.rs                   | 11 ++++++++---
 crates/ra_ide/src/syntax_highlighting.rs       |  4 ++--
 crates/ra_ide/src/syntax_highlighting/tests.rs | 14 +++++---------
 3 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 6d59c864280..4ba6944805d 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -12,10 +12,11 @@ use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
 
 use crate::{
-    attr::AttrInput,
+    attr::{Attr, AttrInput},
     body::{CfgExpander, LowerCtx},
     db::DefDatabase,
     item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
+    path::{ModPath, PathKind},
     src::HasChildSource,
     src::HasSource,
     trace::Trace,
@@ -69,8 +70,12 @@ pub enum ReprKind {
 
 fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
     item_tree.attrs(of).iter().find_map(|a| {
-        if a.path.segments[0].to_string() == "repr" {
-            if let Some(AttrInput::TokenTree(subtree)) = &a.input {
+        if let Attr {
+            path: ModPath { kind: PathKind::Plain, segments },
+            input: Some(AttrInput::TokenTree(subtree)),
+        } = a
+        {
+            if segments.len() == 1 && segments[0].to_string() == "repr" {
                 parse_repr_tt(subtree)
             } else {
                 None
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 9e8419c5f84..a4a7aa2280e 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -565,7 +565,7 @@ fn highlight_element(
                 _ => h,
             }
         }
-        T![&] => {
+        REF_EXPR => {
             let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?;
             let expr = ref_expr.expr()?;
             let field_expr = match expr {
@@ -582,7 +582,7 @@ fn highlight_element(
             // FIXME This needs layout computation to be correct. It will highlight
             // more than it should with the current implementation.
 
-            Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe
+            HighlightTag::Operator | HighlightModifier::Unsafe
         }
         p if p.is_punct() => match p {
             T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => {
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index c4080585011..a7f5ad86225 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -295,8 +295,6 @@ static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 #[repr(packed)]
 struct Packed {
     a: u16,
-    b: u8,
-    c: u32,
 }
 
 trait DoTheAutoref {
@@ -313,11 +311,11 @@ struct HasAligned {
 }
 
 impl DoTheAutoref for NeedsAlign {
-    fn calls_autored(&self) {}
+    fn calls_autoref(&self) {}
 }
 
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
@@ -327,13 +325,11 @@ fn main() {
             Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
-        let y = *(x);
+        let _y = *(x);
         let z = -x;
         let a = global_mut.a;
-        let packed = Packed { a: 0, b: 0, c: 0 };
-        let a = &packed.a;
-        let b = &packed.b;
-        let c = &packed.c;
+        let packed = Packed { a: 0 };
+        let _a = &packed.a;
         let h = HasAligned{ a: NeedsAlign { a: 1 } };
         h.a.calls_autoref();
     }

From d5f11e530dbf6edbdd0ca32d6cd5fafe634c8c4a Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sat, 27 Jun 2020 17:11:43 -0400
Subject: [PATCH 28/76] Unsafe borrow of packed fields: account for borrow
 through ref binding, auto ref function calls

---
 crates/ra_hir/src/code_model.rs               |   5 +-
 crates/ra_hir_def/src/data.rs                 |   6 +-
 crates/ra_hir_def/src/item_tree.rs            |   8 +-
 crates/ra_hir_def/src/item_tree/lower.rs      |   9 +-
 crates/ra_hir_ty/src/method_resolution.rs     |   2 +-
 crates/ra_ide/src/completion/complete_dot.rs  |   2 +-
 .../src/completion/complete_trait_impl.rs     |   2 +-
 crates/ra_ide/src/completion/presentation.rs  |   2 +-
 crates/ra_ide/src/syntax_highlighting.rs      | 137 +++++++++++++++---
 .../ra_ide/src/syntax_highlighting/tests.rs   |  31 ++--
 10 files changed, 156 insertions(+), 48 deletions(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 0007d7fa881..a880fa6713d 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -11,6 +11,7 @@ use hir_def::{
     docs::Documentation,
     expr::{BindingAnnotation, Pat, PatId},
     import_map,
+    item_tree::SelfParam,
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
@@ -670,8 +671,8 @@ impl Function {
         db.function_data(self.id).name.clone()
     }
 
-    pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
-        db.function_data(self.id).has_self_param
+    pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
+        db.function_data(self.id).self_param
     }
 
     pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 88a8ef9bffe..2a26b0183e2 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -10,7 +10,7 @@ use crate::{
     attr::Attrs,
     body::Expander,
     db::DefDatabase,
-    item_tree::{AssocItem, ItemTreeId, ModItem},
+    item_tree::{AssocItem, ItemTreeId, ModItem, SelfParam},
     type_ref::{TypeBound, TypeRef},
     visibility::RawVisibility,
     AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -25,7 +25,7 @@ pub struct FunctionData {
     pub attrs: Attrs,
     /// True if the first param is `self`. This is relevant to decide whether this
     /// can be called as a method.
-    pub has_self_param: bool,
+    pub self_param: Option<SelfParam>,
     pub is_unsafe: bool,
     pub is_varargs: bool,
     pub visibility: RawVisibility,
@@ -42,7 +42,7 @@ impl FunctionData {
             params: func.params.to_vec(),
             ret_type: func.ret_type.clone(),
             attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
-            has_self_param: func.has_self_param,
+            self_param: func.self_param,
             is_unsafe: func.is_unsafe,
             is_varargs: func.is_varargs,
             visibility: item_tree[func.visibility].clone(),
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs
index a67e75dac06..1eaea66e4a3 100644
--- a/crates/ra_hir_def/src/item_tree.rs
+++ b/crates/ra_hir_def/src/item_tree.rs
@@ -500,7 +500,7 @@ pub struct Function {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub generic_params: GenericParamsId,
-    pub has_self_param: bool,
+    pub self_param: Option<SelfParam>,
     pub is_unsafe: bool,
     pub params: Box<[TypeRef]>,
     pub is_varargs: bool,
@@ -508,6 +508,12 @@ pub struct Function {
     pub ast_id: FileAstId<ast::Fn>,
 }
 
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct SelfParam {
+    pub is_ref: bool,
+    pub is_mut: bool,
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Struct {
     pub name: Name,
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
index 450ef879814..89ad91d376f 100644
--- a/crates/ra_hir_def/src/item_tree/lower.rs
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -283,7 +283,7 @@ impl Ctx {
         let name = func.name()?.as_name();
 
         let mut params = Vec::new();
-        let mut has_self_param = false;
+        let mut func_self_param = None;
         if let Some(param_list) = func.param_list() {
             if let Some(self_param) = param_list.self_param() {
                 let self_type = match self_param.ty() {
@@ -302,7 +302,10 @@ impl Ctx {
                     }
                 };
                 params.push(self_type);
-                has_self_param = true;
+                func_self_param = Some(SelfParam {
+                    is_ref: self_param.amp_token().is_some(),
+                    is_mut: self_param.mut_token().is_some(),
+                });
             }
             for param in param_list.params() {
                 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
@@ -335,7 +338,7 @@ impl Ctx {
             name,
             visibility,
             generic_params: GenericParamsId::EMPTY,
-            has_self_param,
+            self_param: func_self_param,
             is_unsafe: func.unsafe_token().is_some(),
             params: params.into_boxed_slice(),
             is_varargs,
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index fb4b30a131f..79c5adf0f1b 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -640,7 +640,7 @@ fn is_valid_candidate(
                 }
             }
             if let Some(receiver_ty) = receiver_ty {
-                if !data.has_self_param {
+                if data.self_param.is_none() {
                     return false;
                 }
                 let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 5326652852f..5488db43f72 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -48,7 +48,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
         let mut seen_methods = FxHashSet::default();
         let traits_in_scope = ctx.scope.traits_in_scope();
         receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
-            if func.has_self_param(ctx.db)
+            if func.self_param(ctx.db).is_some()
                 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
                 && seen_methods.insert(func.name(ctx.db))
             {
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index d9a0ef167db..e3ba7ebc47f 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -136,7 +136,7 @@ fn add_function_impl(
         .lookup_by(fn_name)
         .set_documentation(func.docs(ctx.db));
 
-    let completion_kind = if func.has_self_param(ctx.db) {
+    let completion_kind = if func.self_param(ctx.db).is_some() {
         CompletionItemKind::Method
     } else {
         CompletionItemKind::Function
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 9a94ff47671..fc3d1a4bda4 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -191,7 +191,7 @@ impl Completions {
         func: hir::Function,
         local_name: Option<String>,
     ) {
-        let has_self_param = func.has_self_param(ctx.db);
+        let has_self_param = func.self_param(ctx.db).is_some();
 
         let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
         let ast_node = func.source(ctx.db).value;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index a4a7aa2280e..454fef39c66 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -497,9 +497,9 @@ fn highlight_element(
             match name_kind {
                 Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(),
                 Some(NameClass::Definition(def)) => {
-                    highlight_name(db, def, false) | HighlightModifier::Definition
+                    highlight_name(sema, db, def, None, false) | HighlightModifier::Definition
                 }
-                Some(NameClass::ConstReference(def)) => highlight_name(db, def, false),
+                Some(NameClass::ConstReference(def)) => highlight_name(sema, db, def, None, false),
                 Some(NameClass::FieldShorthand { field, .. }) => {
                     let mut h = HighlightTag::Field.into();
                     if let Definition::Field(field) = field {
@@ -532,7 +532,7 @@ fn highlight_element(
                                 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                             }
                         };
-                        highlight_name(db, def, possibly_unsafe)
+                        highlight_name(sema, db, def, Some(name_ref), possibly_unsafe)
                     }
                     NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
                 },
@@ -565,8 +565,8 @@ fn highlight_element(
                 _ => h,
             }
         }
-        REF_EXPR => {
-            let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?;
+        T![&] => {
+            let ref_expr = element.parent().and_then(ast::RefExpr::cast)?;
             let expr = ref_expr.expr()?;
             let field_expr = match expr {
                 ast::Expr::FieldExpr(fe) => fe,
@@ -668,6 +668,52 @@ fn highlight_element(
                         HighlightTag::SelfKeyword.into()
                     }
                 }
+                T![ref] => {
+                    let modifier: Option<HighlightModifier> = (|| {
+                        let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
+                        let parent = bind_pat.syntax().parent()?;
+
+                        let ty = if let Some(pat_list) =
+                            ast::RecordFieldPatList::cast(parent.clone())
+                        {
+                            let record_pat =
+                                pat_list.syntax().parent().and_then(ast::RecordPat::cast)?;
+                            sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                        } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) {
+                            let field_expr =
+                                if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? {
+                                    field_expr
+                                } else {
+                                    return None;
+                                };
+
+                            sema.type_of_expr(&field_expr.expr()?)
+                        } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) {
+                            let record_pat = record_field_pat
+                                .syntax()
+                                .parent()
+                                .and_then(ast::RecordFieldPatList::cast)?
+                                .syntax()
+                                .parent()
+                                .and_then(ast::RecordPat::cast)?;
+                            sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                        } else {
+                            None
+                        }?;
+
+                        if !ty.is_packed(db) {
+                            return None;
+                        }
+
+                        Some(HighlightModifier::Unsafe)
+                    })();
+
+                    if let Some(modifier) = modifier {
+                        h | modifier
+                    } else {
+                        h
+                    }
+                }
                 _ => h,
             }
         }
@@ -697,7 +743,13 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
-fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight {
+fn highlight_name(
+    sema: &Semantics<RootDatabase>,
+    db: &RootDatabase,
+    def: Definition,
+    name_ref: Option<ast::NameRef>,
+    possibly_unsafe: bool,
+) -> Highlight {
     match def {
         Definition::Macro(_) => HighlightTag::Macro,
         Definition::Field(field) => {
@@ -716,6 +768,29 @@ fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) ->
                 let mut h = HighlightTag::Function.into();
                 if func.is_unsafe(db) {
                     h |= HighlightModifier::Unsafe;
+                } else {
+                    (|| {
+                        let method_call_expr =
+                            name_ref?.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
+                        let expr = method_call_expr.expr()?;
+                        let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                            Some(field_expr)
+                        } else {
+                            None
+                        }?;
+                        let ty = sema.type_of_expr(&field_expr.expr()?)?;
+                        if !ty.is_packed(db) {
+                            return None;
+                        }
+
+                        let func = sema.resolve_method_call(&method_call_expr)?;
+                        if func.self_param(db)?.is_ref {
+                            Some(HighlightModifier::Unsafe)
+                        } else {
+                            None
+                        }
+                    })()
+                    .map(|modifier| h |= modifier);
                 }
                 return h;
             }
@@ -787,8 +862,33 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
         _ => return default.into(),
     };
 
-    let tag = match parent.kind() {
-        METHOD_CALL_EXPR => HighlightTag::Function,
+    match parent.kind() {
+        METHOD_CALL_EXPR => {
+            let mut h = Highlight::new(HighlightTag::Function);
+            let modifier: Option<HighlightModifier> = (|| {
+                let method_call_expr = ast::MethodCallExpr::cast(parent)?;
+                let expr = method_call_expr.expr()?;
+                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                    field_expr
+                } else {
+                    return None;
+                };
+
+                let expr = field_expr.expr()?;
+                let ty = sema.type_of_expr(&expr)?;
+                if ty.is_packed(sema.db) {
+                    Some(HighlightModifier::Unsafe)
+                } else {
+                    None
+                }
+            })();
+
+            if let Some(modifier) = modifier {
+                h |= modifier;
+            }
+
+            h
+        }
         FIELD_EXPR => {
             let h = HighlightTag::Field;
             let is_union = ast::FieldExpr::cast(parent)
@@ -801,7 +901,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
                     })
                 })
                 .unwrap_or(false);
-            return if is_union { h | HighlightModifier::Unsafe } else { h.into() };
+            if is_union { h | HighlightModifier::Unsafe } else { h.into() }
         }
         PATH_SEGMENT => {
             let path = match parent.parent().and_then(ast::Path::cast) {
@@ -826,18 +926,15 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
             };
 
             match parent.kind() {
-                CALL_EXPR => HighlightTag::Function,
-                _ => {
-                    if name.text().chars().next().unwrap_or_default().is_uppercase() {
-                        HighlightTag::Struct
-                    } else {
-                        HighlightTag::Constant
-                    }
+                CALL_EXPR => HighlightTag::Function.into(),
+                _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
+                    HighlightTag::Struct.into()
+                } else {
+                    HighlightTag::Constant
                 }
+                .into(),
             }
         }
-        _ => default,
-    };
-
-    tag.into()
+        _ => default.into(),
+    }
 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index a7f5ad86225..a8087635a87 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -301,16 +301,7 @@ trait DoTheAutoref {
     fn calls_autoref(&self);
 }
 
-struct NeedsAlign {
-    a: u16
-}
-
-#[repr(packed)]
-struct HasAligned {
-    a: NeedsAlign
-}
-
-impl DoTheAutoref for NeedsAlign {
+impl DoTheAutoref for u16 {
     fn calls_autoref(&self) {}
 }
 
@@ -318,6 +309,7 @@ fn main() {
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
     unsafe {
+        // unsafe fn and method calls
         unsafe_fn();
         let b = u.b;
         match u {
@@ -325,13 +317,22 @@ fn main() {
             Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
-        let _y = *(x);
-        let z = -x;
+
+        // unsafe deref
+        let y = *x;
+
+        // unsafe access to a static mut
         let a = global_mut.a;
+
+        // unsafe ref of packed fields
         let packed = Packed { a: 0 };
-        let _a = &packed.a;
-        let h = HasAligned{ a: NeedsAlign { a: 1 } };
-        h.a.calls_autoref();
+        let a = &packed.a;
+        let ref a = packed.a;
+        let Packed { ref a } = packed;
+        let Packed { a: ref _a } = packed;
+
+        // unsafe auto ref of packed field
+        packed.a.calls_autoref();
     }
 }
 "#

From aca3d6c57ec2c668cdb51eca34d6f7bc8fa7412b Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sat, 27 Jun 2020 17:28:07 -0400
Subject: [PATCH 29/76] Deduplicate unsafe method call into a single function

---
 crates/ra_ide/src/syntax_highlighting.rs | 72 ++++++++++--------------
 1 file changed, 31 insertions(+), 41 deletions(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 454fef39c66..02b16b13c04 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -743,6 +743,26 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
+fn is_method_call_unsafe(
+    sema: &Semantics<RootDatabase>,
+    method_call_expr: ast::MethodCallExpr,
+) -> Option<()> {
+    let expr = method_call_expr.expr()?;
+    let field_expr =
+        if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
+    let ty = sema.type_of_expr(&field_expr.expr()?)?;
+    if !ty.is_packed(sema.db) {
+        return None;
+    }
+
+    let func = sema.resolve_method_call(&method_call_expr)?;
+    if func.self_param(sema.db)?.is_ref {
+        Some(())
+    } else {
+        None
+    }
+}
+
 fn highlight_name(
     sema: &Semantics<RootDatabase>,
     db: &RootDatabase,
@@ -769,28 +789,13 @@ fn highlight_name(
                 if func.is_unsafe(db) {
                     h |= HighlightModifier::Unsafe;
                 } else {
-                    (|| {
-                        let method_call_expr =
-                            name_ref?.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
-                        let expr = method_call_expr.expr()?;
-                        let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
-                            Some(field_expr)
-                        } else {
-                            None
-                        }?;
-                        let ty = sema.type_of_expr(&field_expr.expr()?)?;
-                        if !ty.is_packed(db) {
-                            return None;
-                        }
-
-                        let func = sema.resolve_method_call(&method_call_expr)?;
-                        if func.self_param(db)?.is_ref {
-                            Some(HighlightModifier::Unsafe)
-                        } else {
-                            None
-                        }
-                    })()
-                    .map(|modifier| h |= modifier);
+                    let is_unsafe = name_ref
+                        .and_then(|name_ref| name_ref.syntax().parent())
+                        .and_then(ast::MethodCallExpr::cast)
+                        .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
+                    if is_unsafe.is_some() {
+                        h |= HighlightModifier::Unsafe;
+                    }
                 }
                 return h;
             }
@@ -865,26 +870,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
     match parent.kind() {
         METHOD_CALL_EXPR => {
             let mut h = Highlight::new(HighlightTag::Function);
-            let modifier: Option<HighlightModifier> = (|| {
-                let method_call_expr = ast::MethodCallExpr::cast(parent)?;
-                let expr = method_call_expr.expr()?;
-                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
-                    field_expr
-                } else {
-                    return None;
-                };
+            let is_unsafe = ast::MethodCallExpr::cast(parent)
+                .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
 
-                let expr = field_expr.expr()?;
-                let ty = sema.type_of_expr(&expr)?;
-                if ty.is_packed(sema.db) {
-                    Some(HighlightModifier::Unsafe)
-                } else {
-                    None
-                }
-            })();
-
-            if let Some(modifier) = modifier {
-                h |= modifier;
+            if is_unsafe.is_some() {
+                h |= HighlightModifier::Unsafe;
             }
 
             h

From c5cc24cb312c70159e63315ea49769b575e8cb65 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sun, 28 Jun 2020 16:04:00 -0400
Subject: [PATCH 30/76] Revert function structs back to using bool to track
 self param, use first param for self information in syntax highlighting
 instead

---
 crates/ra_hir/src/code_model.rs                     |  5 ++---
 crates/ra_hir/src/lib.rs                            |  2 +-
 crates/ra_hir_def/src/data.rs                       |  6 +++---
 crates/ra_hir_def/src/item_tree.rs                  |  8 +-------
 crates/ra_hir_def/src/item_tree/lower.rs            |  9 +++------
 crates/ra_hir_ty/src/method_resolution.rs           |  2 +-
 crates/ra_ide/src/completion/complete_dot.rs        |  2 +-
 crates/ra_ide/src/completion/complete_trait_impl.rs |  2 +-
 crates/ra_ide/src/completion/presentation.rs        |  2 +-
 crates/ra_ide/src/syntax_highlighting.rs            | 11 ++++++++---
 10 files changed, 22 insertions(+), 27 deletions(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index a880fa6713d..0007d7fa881 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -11,7 +11,6 @@ use hir_def::{
     docs::Documentation,
     expr::{BindingAnnotation, Pat, PatId},
     import_map,
-    item_tree::SelfParam,
     per_ns::PerNs,
     resolver::{HasResolver, Resolver},
     src::HasSource as _,
@@ -671,8 +670,8 @@ impl Function {
         db.function_data(self.id).name.clone()
     }
 
-    pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
-        db.function_data(self.id).self_param
+    pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
+        db.function_data(self.id).has_self_param
     }
 
     pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 31f3241c9ed..34b02c5365f 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -49,7 +49,7 @@ pub use hir_def::{
     docs::Documentation,
     nameres::ModuleSource,
     path::{ModPath, Path, PathKind},
-    type_ref::Mutability,
+    type_ref::{Mutability, TypeRef},
 };
 pub use hir_expand::{
     hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 2a26b0183e2..88a8ef9bffe 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -10,7 +10,7 @@ use crate::{
     attr::Attrs,
     body::Expander,
     db::DefDatabase,
-    item_tree::{AssocItem, ItemTreeId, ModItem, SelfParam},
+    item_tree::{AssocItem, ItemTreeId, ModItem},
     type_ref::{TypeBound, TypeRef},
     visibility::RawVisibility,
     AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -25,7 +25,7 @@ pub struct FunctionData {
     pub attrs: Attrs,
     /// True if the first param is `self`. This is relevant to decide whether this
     /// can be called as a method.
-    pub self_param: Option<SelfParam>,
+    pub has_self_param: bool,
     pub is_unsafe: bool,
     pub is_varargs: bool,
     pub visibility: RawVisibility,
@@ -42,7 +42,7 @@ impl FunctionData {
             params: func.params.to_vec(),
             ret_type: func.ret_type.clone(),
             attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
-            self_param: func.self_param,
+            has_self_param: func.has_self_param,
             is_unsafe: func.is_unsafe,
             is_varargs: func.is_varargs,
             visibility: item_tree[func.visibility].clone(),
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs
index 1eaea66e4a3..a67e75dac06 100644
--- a/crates/ra_hir_def/src/item_tree.rs
+++ b/crates/ra_hir_def/src/item_tree.rs
@@ -500,7 +500,7 @@ pub struct Function {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub generic_params: GenericParamsId,
-    pub self_param: Option<SelfParam>,
+    pub has_self_param: bool,
     pub is_unsafe: bool,
     pub params: Box<[TypeRef]>,
     pub is_varargs: bool,
@@ -508,12 +508,6 @@ pub struct Function {
     pub ast_id: FileAstId<ast::Fn>,
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct SelfParam {
-    pub is_ref: bool,
-    pub is_mut: bool,
-}
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Struct {
     pub name: Name,
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
index 89ad91d376f..450ef879814 100644
--- a/crates/ra_hir_def/src/item_tree/lower.rs
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -283,7 +283,7 @@ impl Ctx {
         let name = func.name()?.as_name();
 
         let mut params = Vec::new();
-        let mut func_self_param = None;
+        let mut has_self_param = false;
         if let Some(param_list) = func.param_list() {
             if let Some(self_param) = param_list.self_param() {
                 let self_type = match self_param.ty() {
@@ -302,10 +302,7 @@ impl Ctx {
                     }
                 };
                 params.push(self_type);
-                func_self_param = Some(SelfParam {
-                    is_ref: self_param.amp_token().is_some(),
-                    is_mut: self_param.mut_token().is_some(),
-                });
+                has_self_param = true;
             }
             for param in param_list.params() {
                 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
@@ -338,7 +335,7 @@ impl Ctx {
             name,
             visibility,
             generic_params: GenericParamsId::EMPTY,
-            self_param: func_self_param,
+            has_self_param,
             is_unsafe: func.unsafe_token().is_some(),
             params: params.into_boxed_slice(),
             is_varargs,
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 79c5adf0f1b..fb4b30a131f 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -640,7 +640,7 @@ fn is_valid_candidate(
                 }
             }
             if let Some(receiver_ty) = receiver_ty {
-                if data.self_param.is_none() {
+                if !data.has_self_param {
                     return false;
                 }
                 let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 5488db43f72..5326652852f 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -48,7 +48,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
         let mut seen_methods = FxHashSet::default();
         let traits_in_scope = ctx.scope.traits_in_scope();
         receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
-            if func.self_param(ctx.db).is_some()
+            if func.has_self_param(ctx.db)
                 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
                 && seen_methods.insert(func.name(ctx.db))
             {
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index e3ba7ebc47f..d9a0ef167db 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -136,7 +136,7 @@ fn add_function_impl(
         .lookup_by(fn_name)
         .set_documentation(func.docs(ctx.db));
 
-    let completion_kind = if func.self_param(ctx.db).is_some() {
+    let completion_kind = if func.has_self_param(ctx.db) {
         CompletionItemKind::Method
     } else {
         CompletionItemKind::Function
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index fc3d1a4bda4..9a94ff47671 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -191,7 +191,7 @@ impl Completions {
         func: hir::Function,
         local_name: Option<String>,
     ) {
-        let has_self_param = func.self_param(ctx.db).is_some();
+        let has_self_param = func.has_self_param(ctx.db);
 
         let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
         let ast_node = func.source(ctx.db).value;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 02b16b13c04..d5a5f69cca3 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -4,7 +4,7 @@ mod injection;
 #[cfg(test)]
 mod tests;
 
-use hir::{Name, Semantics, VariantDef};
+use hir::{Name, Semantics, TypeRef, VariantDef};
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
     RootDatabase,
@@ -756,8 +756,13 @@ fn is_method_call_unsafe(
     }
 
     let func = sema.resolve_method_call(&method_call_expr)?;
-    if func.self_param(sema.db)?.is_ref {
-        Some(())
+    if func.has_self_param(sema.db) {
+        let params = func.params(sema.db);
+        if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
+            Some(())
+        } else {
+            None
+        }
     } else {
         None
     }

From 08182aa9fad4021e60cdc80ee0a578929507e115 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <paulf@pop-os.localdomain>
Date: Sun, 19 Jul 2020 11:45:46 -0400
Subject: [PATCH 31/76] Move unsafe packed ref logic to Semantics, use
 `Attrs::by_key` to simplify repr attr lookup

---
 crates/ra_hir/src/semantics.rs           |  41 ++
 crates/ra_hir_def/src/adt.rs             |  25 +-
 crates/ra_ide/src/call_info.rs.orig      | 769 +++++++++++++++++++++++
 crates/ra_ide/src/syntax_highlighting.rs |  34 +-
 4 files changed, 815 insertions(+), 54 deletions(-)
 create mode 100644 crates/ra_ide/src/call_info.rs.orig

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index e392130ab6d..1072b397185 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -279,6 +279,47 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     pub fn assert_contains_node(&self, node: &SyntaxNode) {
         self.imp.assert_contains_node(node)
     }
+
+    pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool {
+        let ty = (|| {
+            let parent = match pat {
+                ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?,
+                _ => return None,
+            };
+
+            // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
+            // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
+            // so this tries to lookup the `BindPat` anywhere along that structure to the
+            // `RecordPat` so we can get the containing type.
+            let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                .and_then(|record_pat| record_pat.syntax().parent())
+                .or_else(|| Some(parent.clone()))
+                .and_then(|parent| {
+                    ast::RecordFieldPatList::cast(parent)?
+                        .syntax()
+                        .parent()
+                        .and_then(ast::RecordPat::cast)
+                });
+
+            // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
+            // this is initialized from a `FieldExpr`.
+            if let Some(record_pat) = record_pat {
+                self.type_of_pat(&ast::Pat::RecordPat(record_pat))
+            } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
+                let field_expr = match let_stmt.initializer()? {
+                    ast::Expr::FieldExpr(field_expr) => field_expr,
+                    _ => return None,
+                };
+
+                self.type_of_expr(&field_expr.expr()?)
+            } else {
+                None
+            }
+        })();
+
+        // Binding a reference to a packed type is possibly unsafe.
+        ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false)
+    }
 }
 
 impl<'db> SemanticsImpl<'db> {
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 4ba6944805d..35c3a914025 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -12,11 +12,9 @@ use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
 use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
 
 use crate::{
-    attr::{Attr, AttrInput},
     body::{CfgExpander, LowerCtx},
     db::DefDatabase,
     item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
-    path::{ModPath, PathKind},
     src::HasChildSource,
     src::HasSource,
     trace::Trace,
@@ -69,21 +67,7 @@ pub enum ReprKind {
 }
 
 fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> {
-    item_tree.attrs(of).iter().find_map(|a| {
-        if let Attr {
-            path: ModPath { kind: PathKind::Plain, segments },
-            input: Some(AttrInput::TokenTree(subtree)),
-        } = a
-        {
-            if segments.len() == 1 && segments[0].to_string() == "repr" {
-                parse_repr_tt(subtree)
-            } else {
-                None
-            }
-        } else {
-            None
-        }
-    })
+    item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt)
 }
 
 fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
@@ -93,11 +77,8 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
     }
 
     let mut it = tt.token_trees.iter();
-    match it.next() {
-        None => None,
-        Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => {
-            Some(ReprKind::Packed)
-        }
+    match it.next()? {
+        TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
         _ => Some(ReprKind::Other),
     }
 }
diff --git a/crates/ra_ide/src/call_info.rs.orig b/crates/ra_ide/src/call_info.rs.orig
new file mode 100644
index 00000000000..0e04c0b6076
--- /dev/null
+++ b/crates/ra_ide/src/call_info.rs.orig
@@ -0,0 +1,769 @@
+//! FIXME: write short doc here
+use either::Either;
+use hir::{Docs, HirDisplay, Semantics, Type};
+use ra_ide_db::RootDatabase;
+use ra_syntax::{
+    ast::{self, ArgListOwner},
+    match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
+};
+use stdx::format_to;
+use test_utils::mark;
+
+use crate::FilePosition;
+
+/// Contains information about a call site. Specifically the
+/// `FunctionSignature`and current parameter.
+#[derive(Debug)]
+pub struct CallInfo {
+    pub doc: Option<String>,
+    pub signature: String,
+    pub active_parameter: Option<usize>,
+    parameters: Vec<TextRange>,
+}
+
+impl CallInfo {
+    pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
+        self.parameters.iter().map(move |&it| &self.signature[it])
+    }
+    pub fn parameter_ranges(&self) -> &[TextRange] {
+        &self.parameters
+    }
+    fn push_param(&mut self, param: &str) {
+        if !self.signature.ends_with('(') {
+            self.signature.push_str(", ");
+        }
+        let start = TextSize::of(&self.signature);
+        self.signature.push_str(param);
+        let end = TextSize::of(&self.signature);
+        self.parameters.push(TextRange::new(start, end))
+    }
+}
+
+/// Computes parameter information for the given call expression.
+pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
+    let sema = Semantics::new(db);
+    let file = sema.parse(position.file_id);
+    let file = file.syntax();
+    let token = file.token_at_offset(position.offset).next()?;
+    let token = sema.descend_into_macros(token);
+
+    let (callable, active_parameter) = call_info_impl(&sema, token)?;
+
+    let mut res =
+        CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
+
+    match callable.kind() {
+        hir::CallableKind::Function(func) => {
+            res.doc = func.docs(db).map(|it| it.as_str().to_string());
+            format_to!(res.signature, "fn {}", func.name(db));
+        }
+        hir::CallableKind::TupleStruct(strukt) => {
+            res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
+            format_to!(res.signature, "struct {}", strukt.name(db));
+        }
+        hir::CallableKind::TupleEnumVariant(variant) => {
+            res.doc = variant.docs(db).map(|it| it.as_str().to_string());
+            format_to!(
+                res.signature,
+                "enum {}::{}",
+                variant.parent_enum(db).name(db),
+                variant.name(db)
+            );
+        }
+        hir::CallableKind::Closure => (),
+    }
+
+    res.signature.push('(');
+    {
+        if let Some(self_param) = callable.receiver_param(db) {
+            format_to!(res.signature, "{}", self_param)
+        }
+        let mut buf = String::new();
+        for (pat, ty) in callable.params(db) {
+            buf.clear();
+            if let Some(pat) = pat {
+                match pat {
+                    Either::Left(_self) => format_to!(buf, "self: "),
+                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
+                }
+            }
+            format_to!(buf, "{}", ty.display(db));
+            res.push_param(&buf);
+        }
+    }
+    res.signature.push(')');
+
+    match callable.kind() {
+        hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
+            let ret_type = callable.return_type();
+            if !ret_type.is_unit() {
+                format_to!(res.signature, " -> {}", ret_type.display(db));
+            }
+        }
+        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
+    }
+    Some(res)
+}
+
+fn call_info_impl(
+    sema: &Semantics<RootDatabase>,
+    token: SyntaxToken,
+) -> Option<(hir::Callable, Option<usize>)> {
+    // Find the calling expression and it's NameRef
+    let calling_node = FnCallNode::with_node(&token.parent())?;
+
+<<<<<<< HEAD
+    let callable = match &calling_node {
+        FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
+        FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
+    };
+    let active_param = if let Some(arg_list) = calling_node.arg_list() {
+        // Number of arguments specified at the call site
+        let num_args_at_callsite = arg_list.args().count();
+
+        let arg_list_range = arg_list.syntax().text_range();
+        if !arg_list_range.contains_inclusive(token.text_range().start()) {
+            mark::hit!(call_info_bad_offset);
+            return None;
+=======
+    let (mut call_info, has_self) = match &calling_node {
+        FnCallNode::CallExpr(call) => {
+            //FIXME: Type::as_callable is broken
+            let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
+            match callable_def {
+                hir::CallableDef::FunctionId(it) => {
+                    let fn_def = it.into();
+                    (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
+                }
+                hir::CallableDef::StructId(it) => {
+                    (CallInfo::with_struct(sema.db, it.into())?, false)
+                }
+                hir::CallableDef::EnumVariantId(it) => {
+                    (CallInfo::with_enum_variant(sema.db, it.into())?, false)
+                }
+            }
+        }
+        FnCallNode::MethodCallExpr(method_call) => {
+            let function = sema.resolve_method_call(&method_call)?;
+            (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
+        }
+        FnCallNode::MacroCallExpr(macro_call) => {
+            let macro_def = sema.resolve_macro_call(&macro_call)?;
+            (CallInfo::with_macro(sema.db, macro_def)?, false)
+>>>>>>> Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead
+        }
+        let param = std::cmp::min(
+            num_args_at_callsite,
+            arg_list
+                .args()
+                .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
+                .count(),
+        );
+
+        Some(param)
+    } else {
+        None
+    };
+    Some((callable, active_param))
+}
+
+#[derive(Debug)]
+pub(crate) struct ActiveParameter {
+    pub(crate) ty: Type,
+    pub(crate) name: String,
+}
+
+impl ActiveParameter {
+    pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
+        let sema = Semantics::new(db);
+        let file = sema.parse(position.file_id);
+        let file = file.syntax();
+        let token = file.token_at_offset(position.offset).next()?;
+        let token = sema.descend_into_macros(token);
+        Self::at_token(&sema, token)
+    }
+
+    pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
+        let (signature, active_parameter) = call_info_impl(&sema, token)?;
+
+        let idx = active_parameter?;
+        let mut params = signature.params(sema.db);
+        if !(idx < params.len()) {
+            mark::hit!(too_many_arguments);
+            return None;
+        }
+        let (pat, ty) = params.swap_remove(idx);
+        let name = pat?.to_string();
+        Some(ActiveParameter { ty, name })
+    }
+}
+
+#[derive(Debug)]
+pub(crate) enum FnCallNode {
+    CallExpr(ast::CallExpr),
+    MethodCallExpr(ast::MethodCallExpr),
+}
+
+impl FnCallNode {
+    fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
+        syntax.ancestors().find_map(|node| {
+            match_ast! {
+                match node {
+                    ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
+                    ast::MethodCallExpr(it) => {
+                        let arg_list = it.arg_list()?;
+                        if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
+                            return None;
+                        }
+                        Some(FnCallNode::MethodCallExpr(it))
+                    },
+                    _ => None,
+                }
+            }
+        })
+    }
+
+    pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
+        match_ast! {
+            match node {
+                ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
+                ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
+                _ => None,
+            }
+        }
+    }
+
+    pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
+        match self {
+            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
+                ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
+                _ => return None,
+            }),
+
+            FnCallNode::MethodCallExpr(call_expr) => {
+                call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
+            }
+        }
+    }
+
+    fn arg_list(&self) -> Option<ast::ArgList> {
+        match self {
+            FnCallNode::CallExpr(expr) => expr.arg_list(),
+            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use expect::{expect, Expect};
+    use test_utils::mark;
+
+    use crate::mock_analysis::analysis_and_position;
+
+    fn check(ra_fixture: &str, expect: Expect) {
+        let (analysis, position) = analysis_and_position(ra_fixture);
+        let call_info = analysis.call_info(position).unwrap();
+        let actual = match call_info {
+            Some(call_info) => {
+                let docs = match &call_info.doc {
+                    None => "".to_string(),
+                    Some(docs) => format!("{}\n------\n", docs.as_str()),
+                };
+                let params = call_info
+                    .parameter_labels()
+                    .enumerate()
+                    .map(|(i, param)| {
+                        if Some(i) == call_info.active_parameter {
+                            format!("<{}>", param)
+                        } else {
+                            param.to_string()
+                        }
+                    })
+                    .collect::<Vec<_>>()
+                    .join(", ");
+                format!("{}{}\n({})\n", docs, call_info.signature, params)
+            }
+            None => String::new(),
+        };
+        expect.assert_eq(&actual);
+    }
+
+    #[test]
+    fn test_fn_signature_two_args() {
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(<|>3, ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3<|>, ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3,<|> ); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (x: u32, <y: u32>)
+            "#]],
+        );
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(3, <|>); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (x: u32, <y: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_two_args_empty() {
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(x: u32, y: u32) -> u32
+                (<x: u32>, y: u32)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_two_args_first_generics() {
+        check(
+            r#"
+fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
+    where T: Copy + Display, U: Debug
+{ x + y }
+
+fn bar() { foo(<|>3, ); }
+"#,
+            expect![[r#"
+                fn foo(x: i32, y: {unknown}) -> u32
+                (<x: i32>, y: {unknown})
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_no_params() {
+        check(
+            r#"
+fn foo<T>() -> T where T: Copy + Display {}
+fn bar() { foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo() -> {unknown}
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_impl() {
+        check(
+            r#"
+struct F;
+impl F { pub fn new() { } }
+fn bar() {
+    let _ : F = F::new(<|>);
+}
+"#,
+            expect![[r#"
+                fn new()
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_self() {
+        check(
+            r#"
+struct S;
+impl S { pub fn do_it(&self) {} }
+
+fn bar() {
+    let s: S = S;
+    s.do_it(<|>);
+}
+"#,
+            expect![[r#"
+                fn do_it(&self)
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_with_arg() {
+        check(
+            r#"
+struct S;
+impl S {
+    fn foo(&self, x: i32) {}
+}
+
+fn main() { S.foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(&self, x: i32)
+                (<x: i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
+        check(
+            r#"
+struct S;
+impl S {
+    fn foo(&self, x: i32) {}
+}
+
+fn main() { S::foo(<|>); }
+"#,
+            expect![[r#"
+                fn foo(self: &S, x: i32)
+                (<self: &S>, x: i32)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_simple() {
+        check(
+            r#"
+/// test
+// non-doc-comment
+fn foo(j: u32) -> u32 {
+    j
+}
+
+fn bar() {
+    let _ = foo(<|>);
+}
+"#,
+            expect![[r#"
+                test
+                ------
+                fn foo(j: u32) -> u32
+                (<j: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs() {
+        check(
+            r#"
+/// Adds one to the number given.
+///
+/// # Examples
+///
+/// ```
+/// let five = 5;
+///
+/// assert_eq!(6, my_crate::add_one(5));
+/// ```
+pub fn add_one(x: i32) -> i32 {
+    x + 1
+}
+
+pub fn do() {
+    add_one(<|>
+}"#,
+            expect![[r##"
+                Adds one to the number given.
+
+                # Examples
+
+                ```
+                let five = 5;
+
+                assert_eq!(6, my_crate::add_one(5));
+                ```
+                ------
+                fn add_one(x: i32) -> i32
+                (<x: i32>)
+            "##]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_impl() {
+        check(
+            r#"
+struct addr;
+impl addr {
+    /// Adds one to the number given.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// let five = 5;
+    ///
+    /// assert_eq!(6, my_crate::add_one(5));
+    /// ```
+    pub fn add_one(x: i32) -> i32 {
+        x + 1
+    }
+}
+
+pub fn do_it() {
+    addr {};
+    addr::add_one(<|>);
+}
+"#,
+            expect![[r##"
+                Adds one to the number given.
+
+                # Examples
+
+                ```
+                let five = 5;
+
+                assert_eq!(6, my_crate::add_one(5));
+                ```
+                ------
+                fn add_one(x: i32) -> i32
+                (<x: i32>)
+            "##]],
+        );
+    }
+
+    #[test]
+    fn test_fn_signature_with_docs_from_actix() {
+        check(
+            r#"
+struct WriteHandler<E>;
+
+impl<E> WriteHandler<E> {
+    /// Method is called when writer emits error.
+    ///
+    /// If this method returns `ErrorAction::Continue` writer processing
+    /// continues otherwise stream processing stops.
+    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
+        Running::Stop
+    }
+
+    /// Method is called when writer finishes.
+    ///
+    /// By default this method stops actor's `Context`.
+    fn finished(&mut self, ctx: &mut Self::Context) {
+        ctx.stop()
+    }
+}
+
+pub fn foo(mut r: WriteHandler<()>) {
+    r.finished(<|>);
+}
+"#,
+            expect![[r#"
+                Method is called when writer finishes.
+
+                By default this method stops actor's `Context`.
+                ------
+                fn finished(&mut self, ctx: &mut {unknown})
+                (<ctx: &mut {unknown}>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn call_info_bad_offset() {
+        mark::check!(call_info_bad_offset);
+        check(
+            r#"
+fn foo(x: u32, y: u32) -> u32 {x + y}
+fn bar() { foo <|> (3, ); }
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn test_nested_method_in_lambda() {
+        check(
+            r#"
+struct Foo;
+impl Foo { fn bar(&self, _: u32) { } }
+
+fn bar(_: u32) { }
+
+fn main() {
+    let foo = Foo;
+    std::thread::spawn(move || foo.bar(<|>));
+}
+"#,
+            expect![[r#"
+                fn bar(&self, _: u32)
+                (<_: u32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn works_for_tuple_structs() {
+        check(
+            r#"
+/// A cool tuple struct
+struct S(u32, i32);
+fn main() {
+    let s = S(0, <|>);
+}
+"#,
+            expect![[r#"
+                A cool tuple struct
+                ------
+                struct S(u32, i32)
+                (u32, <i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn generic_struct() {
+        check(
+            r#"
+struct S<T>(T);
+fn main() {
+    let s = S(<|>);
+}
+"#,
+            expect![[r#"
+                struct S({unknown})
+                (<{unknown}>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn works_for_enum_variants() {
+        check(
+            r#"
+enum E {
+    /// A Variant
+    A(i32),
+    /// Another
+    B,
+    /// And C
+    C { a: i32, b: i32 }
+}
+
+fn main() {
+    let a = E::A(<|>);
+}
+"#,
+            expect![[r#"
+                A Variant
+                ------
+                enum E::A(i32)
+                (<i32>)
+            "#]],
+        );
+    }
+
+    #[test]
+    fn cant_call_struct_record() {
+        check(
+            r#"
+struct S { x: u32, y: i32 }
+fn main() {
+    let s = S(<|>);
+}
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn cant_call_enum_record() {
+        check(
+            r#"
+enum E {
+    /// A Variant
+    A(i32),
+    /// Another
+    B,
+    /// And C
+    C { a: i32, b: i32 }
+}
+
+fn main() {
+    let a = E::C(<|>);
+}
+"#,
+            expect![[""]],
+        );
+    }
+
+    #[test]
+    fn fn_signature_for_call_in_macro() {
+        check(
+            r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+fn foo() { }
+id! {
+    fn bar() { foo(<|>); }
+}
+"#,
+            expect![[r#"
+                fn foo()
+                ()
+            "#]],
+        );
+    }
+
+    #[test]
+    fn call_info_for_lambdas() {
+        check(
+            r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+    (|s| foo(s))(<|>)
+}
+        "#,
+            expect![[r#"
+                (S) -> i32
+                (<S>)
+            "#]],
+        )
+    }
+
+    #[test]
+    fn call_info_for_fn_ptr() {
+        check(
+            r#"
+fn main(f: fn(i32, f64) -> char) {
+    f(0, <|>)
+}
+        "#,
+            expect![[r#"
+                (i32, f64) -> char
+                (i32, <f64>)
+            "#]],
+        )
+    }
+}
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index d5a5f69cca3..cf93205b6e3 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -671,41 +671,11 @@ fn highlight_element(
                 T![ref] => {
                     let modifier: Option<HighlightModifier> = (|| {
                         let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
-                        let parent = bind_pat.syntax().parent()?;
-
-                        let ty = if let Some(pat_list) =
-                            ast::RecordFieldPatList::cast(parent.clone())
-                        {
-                            let record_pat =
-                                pat_list.syntax().parent().and_then(ast::RecordPat::cast)?;
-                            sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
-                        } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) {
-                            let field_expr =
-                                if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? {
-                                    field_expr
-                                } else {
-                                    return None;
-                                };
-
-                            sema.type_of_expr(&field_expr.expr()?)
-                        } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) {
-                            let record_pat = record_field_pat
-                                .syntax()
-                                .parent()
-                                .and_then(ast::RecordFieldPatList::cast)?
-                                .syntax()
-                                .parent()
-                                .and_then(ast::RecordPat::cast)?;
-                            sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                        if sema.is_unsafe_pat(&ast::Pat::BindPat(bind_pat)) {
+                            Some(HighlightModifier::Unsafe)
                         } else {
                             None
-                        }?;
-
-                        if !ty.is_packed(db) {
-                            return None;
                         }
-
-                        Some(HighlightModifier::Unsafe)
                     })();
 
                     if let Some(modifier) = modifier {

From 55633f34048434de18d54b4300bca186db052cf5 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 23 Jul 2020 09:31:07 -0400
Subject: [PATCH 32/76] Fix rebase errors

---
 crates/ra_ide/test_data/highlight_doctest.html   |  2 +-
 crates/ra_ide/test_data/highlight_injection.html |  2 +-
 crates/ra_ide/test_data/highlight_unsafe.html    |  5 +++--
 crates/ra_ide/test_data/highlighting.html        | 10 +++++-----
 4 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/crates/ra_ide/test_data/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html
index 6322d404fba..46c1e0a11fd 100644
--- a/crates/ra_ide/test_data/highlight_doctest.html
+++ b/crates/ra_ide/test_data/highlight_doctest.html
@@ -87,7 +87,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// ```sh</span>
     <span class="comment documentation">/// echo 1</span>
     <span class="comment documentation">/// ```</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span>
         <span class="bool_literal">true</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
index 18addd00d28..60c39439944 100644
--- a/crates/ra_ide/test_data/highlight_injection.html
+++ b/crates/ra_ide/test_data/highlight_injection.html
@@ -35,7 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> &<span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 79409fe816b..454ff6d5f8d 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
 
 <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
-    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span>
@@ -55,9 +55,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
-    <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> &<span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
+        <span class="comment">// unsafe fn and method calls</span>
         <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
         <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="punctuation">.</span><span class="field unsafe">b</span><span class="punctuation">;</span>
         <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span>
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 8e0160eee5b..678cf9bd335 100644
--- a/crates/ra_ide/test_data/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -45,11 +45,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span>
+    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
         <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span>&<span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
         <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">noop!</span><span class="punctuation">(</span><span class="macro">noop</span><span class="macro">!</span><span class="punctuation">(</span><span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> &<span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> &<span class="variable mutable">y</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable declaration">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 

From 87cb09365cf841b559e76951eedb826f2d4d3dfd Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 23 Jul 2020 09:39:53 -0400
Subject: [PATCH 33/76] Remove merge backup

---
 crates/ra_ide/src/call_info.rs.orig | 769 ----------------------------
 1 file changed, 769 deletions(-)
 delete mode 100644 crates/ra_ide/src/call_info.rs.orig

diff --git a/crates/ra_ide/src/call_info.rs.orig b/crates/ra_ide/src/call_info.rs.orig
deleted file mode 100644
index 0e04c0b6076..00000000000
--- a/crates/ra_ide/src/call_info.rs.orig
+++ /dev/null
@@ -1,769 +0,0 @@
-//! FIXME: write short doc here
-use either::Either;
-use hir::{Docs, HirDisplay, Semantics, Type};
-use ra_ide_db::RootDatabase;
-use ra_syntax::{
-    ast::{self, ArgListOwner},
-    match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
-};
-use stdx::format_to;
-use test_utils::mark;
-
-use crate::FilePosition;
-
-/// Contains information about a call site. Specifically the
-/// `FunctionSignature`and current parameter.
-#[derive(Debug)]
-pub struct CallInfo {
-    pub doc: Option<String>,
-    pub signature: String,
-    pub active_parameter: Option<usize>,
-    parameters: Vec<TextRange>,
-}
-
-impl CallInfo {
-    pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
-        self.parameters.iter().map(move |&it| &self.signature[it])
-    }
-    pub fn parameter_ranges(&self) -> &[TextRange] {
-        &self.parameters
-    }
-    fn push_param(&mut self, param: &str) {
-        if !self.signature.ends_with('(') {
-            self.signature.push_str(", ");
-        }
-        let start = TextSize::of(&self.signature);
-        self.signature.push_str(param);
-        let end = TextSize::of(&self.signature);
-        self.parameters.push(TextRange::new(start, end))
-    }
-}
-
-/// Computes parameter information for the given call expression.
-pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
-    let file = file.syntax();
-    let token = file.token_at_offset(position.offset).next()?;
-    let token = sema.descend_into_macros(token);
-
-    let (callable, active_parameter) = call_info_impl(&sema, token)?;
-
-    let mut res =
-        CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
-
-    match callable.kind() {
-        hir::CallableKind::Function(func) => {
-            res.doc = func.docs(db).map(|it| it.as_str().to_string());
-            format_to!(res.signature, "fn {}", func.name(db));
-        }
-        hir::CallableKind::TupleStruct(strukt) => {
-            res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
-            format_to!(res.signature, "struct {}", strukt.name(db));
-        }
-        hir::CallableKind::TupleEnumVariant(variant) => {
-            res.doc = variant.docs(db).map(|it| it.as_str().to_string());
-            format_to!(
-                res.signature,
-                "enum {}::{}",
-                variant.parent_enum(db).name(db),
-                variant.name(db)
-            );
-        }
-        hir::CallableKind::Closure => (),
-    }
-
-    res.signature.push('(');
-    {
-        if let Some(self_param) = callable.receiver_param(db) {
-            format_to!(res.signature, "{}", self_param)
-        }
-        let mut buf = String::new();
-        for (pat, ty) in callable.params(db) {
-            buf.clear();
-            if let Some(pat) = pat {
-                match pat {
-                    Either::Left(_self) => format_to!(buf, "self: "),
-                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
-                }
-            }
-            format_to!(buf, "{}", ty.display(db));
-            res.push_param(&buf);
-        }
-    }
-    res.signature.push(')');
-
-    match callable.kind() {
-        hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
-            let ret_type = callable.return_type();
-            if !ret_type.is_unit() {
-                format_to!(res.signature, " -> {}", ret_type.display(db));
-            }
-        }
-        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
-    }
-    Some(res)
-}
-
-fn call_info_impl(
-    sema: &Semantics<RootDatabase>,
-    token: SyntaxToken,
-) -> Option<(hir::Callable, Option<usize>)> {
-    // Find the calling expression and it's NameRef
-    let calling_node = FnCallNode::with_node(&token.parent())?;
-
-<<<<<<< HEAD
-    let callable = match &calling_node {
-        FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
-        FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
-    };
-    let active_param = if let Some(arg_list) = calling_node.arg_list() {
-        // Number of arguments specified at the call site
-        let num_args_at_callsite = arg_list.args().count();
-
-        let arg_list_range = arg_list.syntax().text_range();
-        if !arg_list_range.contains_inclusive(token.text_range().start()) {
-            mark::hit!(call_info_bad_offset);
-            return None;
-=======
-    let (mut call_info, has_self) = match &calling_node {
-        FnCallNode::CallExpr(call) => {
-            //FIXME: Type::as_callable is broken
-            let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
-            match callable_def {
-                hir::CallableDef::FunctionId(it) => {
-                    let fn_def = it.into();
-                    (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
-                }
-                hir::CallableDef::StructId(it) => {
-                    (CallInfo::with_struct(sema.db, it.into())?, false)
-                }
-                hir::CallableDef::EnumVariantId(it) => {
-                    (CallInfo::with_enum_variant(sema.db, it.into())?, false)
-                }
-            }
-        }
-        FnCallNode::MethodCallExpr(method_call) => {
-            let function = sema.resolve_method_call(&method_call)?;
-            (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
-        }
-        FnCallNode::MacroCallExpr(macro_call) => {
-            let macro_def = sema.resolve_macro_call(&macro_call)?;
-            (CallInfo::with_macro(sema.db, macro_def)?, false)
->>>>>>> Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead
-        }
-        let param = std::cmp::min(
-            num_args_at_callsite,
-            arg_list
-                .args()
-                .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
-                .count(),
-        );
-
-        Some(param)
-    } else {
-        None
-    };
-    Some((callable, active_param))
-}
-
-#[derive(Debug)]
-pub(crate) struct ActiveParameter {
-    pub(crate) ty: Type,
-    pub(crate) name: String,
-}
-
-impl ActiveParameter {
-    pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
-        let sema = Semantics::new(db);
-        let file = sema.parse(position.file_id);
-        let file = file.syntax();
-        let token = file.token_at_offset(position.offset).next()?;
-        let token = sema.descend_into_macros(token);
-        Self::at_token(&sema, token)
-    }
-
-    pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
-        let (signature, active_parameter) = call_info_impl(&sema, token)?;
-
-        let idx = active_parameter?;
-        let mut params = signature.params(sema.db);
-        if !(idx < params.len()) {
-            mark::hit!(too_many_arguments);
-            return None;
-        }
-        let (pat, ty) = params.swap_remove(idx);
-        let name = pat?.to_string();
-        Some(ActiveParameter { ty, name })
-    }
-}
-
-#[derive(Debug)]
-pub(crate) enum FnCallNode {
-    CallExpr(ast::CallExpr),
-    MethodCallExpr(ast::MethodCallExpr),
-}
-
-impl FnCallNode {
-    fn with_node(syntax: &SyntaxNode) -> Option<FnCallNode> {
-        syntax.ancestors().find_map(|node| {
-            match_ast! {
-                match node {
-                    ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                    ast::MethodCallExpr(it) => {
-                        let arg_list = it.arg_list()?;
-                        if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
-                            return None;
-                        }
-                        Some(FnCallNode::MethodCallExpr(it))
-                    },
-                    _ => None,
-                }
-            }
-        })
-    }
-
-    pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option<FnCallNode> {
-        match_ast! {
-            match node {
-                ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
-                _ => None,
-            }
-        }
-    }
-
-    pub(crate) fn name_ref(&self) -> Option<ast::NameRef> {
-        match self {
-            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
-                ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
-                _ => return None,
-            }),
-
-            FnCallNode::MethodCallExpr(call_expr) => {
-                call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
-            }
-        }
-    }
-
-    fn arg_list(&self) -> Option<ast::ArgList> {
-        match self {
-            FnCallNode::CallExpr(expr) => expr.arg_list(),
-            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use expect::{expect, Expect};
-    use test_utils::mark;
-
-    use crate::mock_analysis::analysis_and_position;
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let (analysis, position) = analysis_and_position(ra_fixture);
-        let call_info = analysis.call_info(position).unwrap();
-        let actual = match call_info {
-            Some(call_info) => {
-                let docs = match &call_info.doc {
-                    None => "".to_string(),
-                    Some(docs) => format!("{}\n------\n", docs.as_str()),
-                };
-                let params = call_info
-                    .parameter_labels()
-                    .enumerate()
-                    .map(|(i, param)| {
-                        if Some(i) == call_info.active_parameter {
-                            format!("<{}>", param)
-                        } else {
-                            param.to_string()
-                        }
-                    })
-                    .collect::<Vec<_>>()
-                    .join(", ");
-                format!("{}{}\n({})\n", docs, call_info.signature, params)
-            }
-            None => String::new(),
-        };
-        expect.assert_eq(&actual);
-    }
-
-    #[test]
-    fn test_fn_signature_two_args() {
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>3, ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3<|>, ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3,<|> ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, <y: u32>)
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3, <|>); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, <y: u32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_empty() {
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (<x: u32>, y: u32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_first_generics() {
-        check(
-            r#"
-fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
-    where T: Copy + Display, U: Debug
-{ x + y }
-
-fn bar() { foo(<|>3, ); }
-"#,
-            expect![[r#"
-                fn foo(x: i32, y: {unknown}) -> u32
-                (<x: i32>, y: {unknown})
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_no_params() {
-        check(
-            r#"
-fn foo<T>() -> T where T: Copy + Display {}
-fn bar() { foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo() -> {unknown}
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_impl() {
-        check(
-            r#"
-struct F;
-impl F { pub fn new() { } }
-fn bar() {
-    let _ : F = F::new(<|>);
-}
-"#,
-            expect![[r#"
-                fn new()
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_self() {
-        check(
-            r#"
-struct S;
-impl S { pub fn do_it(&self) {} }
-
-fn bar() {
-    let s: S = S;
-    s.do_it(<|>);
-}
-"#,
-            expect![[r#"
-                fn do_it(&self)
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_with_arg() {
-        check(
-            r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S.foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(&self, x: i32)
-                (<x: i32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
-        check(
-            r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S::foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(self: &S, x: i32)
-                (<self: &S>, x: i32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_simple() {
-        check(
-            r#"
-/// test
-// non-doc-comment
-fn foo(j: u32) -> u32 {
-    j
-}
-
-fn bar() {
-    let _ = foo(<|>);
-}
-"#,
-            expect![[r#"
-                test
-                ------
-                fn foo(j: u32) -> u32
-                (<j: u32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs() {
-        check(
-            r#"
-/// Adds one to the number given.
-///
-/// # Examples
-///
-/// ```
-/// let five = 5;
-///
-/// assert_eq!(6, my_crate::add_one(5));
-/// ```
-pub fn add_one(x: i32) -> i32 {
-    x + 1
-}
-
-pub fn do() {
-    add_one(<|>
-}"#,
-            expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                (<x: i32>)
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_impl() {
-        check(
-            r#"
-struct addr;
-impl addr {
-    /// Adds one to the number given.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// let five = 5;
-    ///
-    /// assert_eq!(6, my_crate::add_one(5));
-    /// ```
-    pub fn add_one(x: i32) -> i32 {
-        x + 1
-    }
-}
-
-pub fn do_it() {
-    addr {};
-    addr::add_one(<|>);
-}
-"#,
-            expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                (<x: i32>)
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_from_actix() {
-        check(
-            r#"
-struct WriteHandler<E>;
-
-impl<E> WriteHandler<E> {
-    /// Method is called when writer emits error.
-    ///
-    /// If this method returns `ErrorAction::Continue` writer processing
-    /// continues otherwise stream processing stops.
-    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
-        Running::Stop
-    }
-
-    /// Method is called when writer finishes.
-    ///
-    /// By default this method stops actor's `Context`.
-    fn finished(&mut self, ctx: &mut Self::Context) {
-        ctx.stop()
-    }
-}
-
-pub fn foo(mut r: WriteHandler<()>) {
-    r.finished(<|>);
-}
-"#,
-            expect![[r#"
-                Method is called when writer finishes.
-
-                By default this method stops actor's `Context`.
-                ------
-                fn finished(&mut self, ctx: &mut {unknown})
-                (<ctx: &mut {unknown}>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn call_info_bad_offset() {
-        mark::check!(call_info_bad_offset);
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo <|> (3, ); }
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn test_nested_method_in_lambda() {
-        check(
-            r#"
-struct Foo;
-impl Foo { fn bar(&self, _: u32) { } }
-
-fn bar(_: u32) { }
-
-fn main() {
-    let foo = Foo;
-    std::thread::spawn(move || foo.bar(<|>));
-}
-"#,
-            expect![[r#"
-                fn bar(&self, _: u32)
-                (<_: u32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn works_for_tuple_structs() {
-        check(
-            r#"
-/// A cool tuple struct
-struct S(u32, i32);
-fn main() {
-    let s = S(0, <|>);
-}
-"#,
-            expect![[r#"
-                A cool tuple struct
-                ------
-                struct S(u32, i32)
-                (u32, <i32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn generic_struct() {
-        check(
-            r#"
-struct S<T>(T);
-fn main() {
-    let s = S(<|>);
-}
-"#,
-            expect![[r#"
-                struct S({unknown})
-                (<{unknown}>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn works_for_enum_variants() {
-        check(
-            r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::A(<|>);
-}
-"#,
-            expect![[r#"
-                A Variant
-                ------
-                enum E::A(i32)
-                (<i32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn cant_call_struct_record() {
-        check(
-            r#"
-struct S { x: u32, y: i32 }
-fn main() {
-    let s = S(<|>);
-}
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn cant_call_enum_record() {
-        check(
-            r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::C(<|>);
-}
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn fn_signature_for_call_in_macro() {
-        check(
-            r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo() { }
-id! {
-    fn bar() { foo(<|>); }
-}
-"#,
-            expect![[r#"
-                fn foo()
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn call_info_for_lambdas() {
-        check(
-            r#"
-struct S;
-fn foo(s: S) -> i32 { 92 }
-fn main() {
-    (|s| foo(s))(<|>)
-}
-        "#,
-            expect![[r#"
-                (S) -> i32
-                (<S>)
-            "#]],
-        )
-    }
-
-    #[test]
-    fn call_info_for_fn_ptr() {
-        check(
-            r#"
-fn main(f: fn(i32, f64) -> char) {
-    f(0, <|>)
-}
-        "#,
-            expect![[r#"
-                (i32, f64) -> char
-                (i32, <f64>)
-            "#]],
-        )
-    }
-}

From a6af0272f7bf129a3063cdd7096f685fc58438e6 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 23 Jul 2020 10:11:37 -0400
Subject: [PATCH 34/76] Move semantic logic into Semantics, fix missing tag for
 safe amp operator, using functional methods rather than clunky inline closure

---
 crates/ra_hir/src/semantics.rs                | 110 ++++++++++++------
 crates/ra_ide/src/syntax_highlighting.rs      |  89 +++++---------
 .../ra_ide/test_data/highlight_doctest.html   |   2 +-
 .../ra_ide/test_data/highlight_injection.html |   2 +-
 crates/ra_ide/test_data/highlight_unsafe.html |   4 +-
 crates/ra_ide/test_data/highlighting.html     |  10 +-
 6 files changed, 112 insertions(+), 105 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 1072b397185..f706a186e7f 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -25,7 +25,8 @@ use crate::{
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
     AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
-    Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef,
+    VariantDef,
 };
 use resolver::TypeNs;
 
@@ -280,45 +281,84 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.assert_contains_node(node)
     }
 
-    pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool {
-        let ty = (|| {
-            let parent = match pat {
-                ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?,
-                _ => return None,
-            };
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> Option<()> {
+        let expr = method_call_expr.expr()?;
+        let field_expr =
+            if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
+        let ty = self.type_of_expr(&field_expr.expr()?)?;
+        if !ty.is_packed(self.db) {
+            return None;
+        }
 
-            // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-            // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-            // so this tries to lookup the `BindPat` anywhere along that structure to the
-            // `RecordPat` so we can get the containing type.
-            let record_pat = ast::RecordFieldPat::cast(parent.clone())
-                .and_then(|record_pat| record_pat.syntax().parent())
-                .or_else(|| Some(parent.clone()))
-                .and_then(|parent| {
-                    ast::RecordFieldPatList::cast(parent)?
-                        .syntax()
-                        .parent()
-                        .and_then(ast::RecordPat::cast)
-                });
-
-            // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
-            // this is initialized from a `FieldExpr`.
-            if let Some(record_pat) = record_pat {
-                self.type_of_pat(&ast::Pat::RecordPat(record_pat))
-            } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
-                let field_expr = match let_stmt.initializer()? {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-
-                self.type_of_expr(&field_expr.expr()?)
+        let func = self.resolve_method_call(&method_call_expr)?;
+        if func.has_self_param(self.db) {
+            let params = func.params(self.db);
+            if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
+                Some(())
             } else {
                 None
             }
-        })();
+        } else {
+            None
+        }
+    }
 
-        // Binding a reference to a packed type is possibly unsafe.
-        ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false)
+    pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+        ref_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = match expr {
+                    ast::Expr::FieldExpr(field_expr) => field_expr,
+                    _ => return None,
+                };
+                let expr = field_expr.expr()?;
+                self.type_of_expr(&expr)
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+
+        // FIXME This needs layout computation to be correct. It will highlight
+        // more than it should with the current implementation.
+    }
+
+    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
+        bind_pat
+            .syntax()
+            .parent()
+            .and_then(|parent| {
+                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
+                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
+                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `RecordPat` so we can get the containing type.
+                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                    .and_then(|record_pat| record_pat.syntax().parent())
+                    .or_else(|| Some(parent.clone()))
+                    .and_then(|parent| {
+                        ast::RecordFieldPatList::cast(parent)?
+                            .syntax()
+                            .parent()
+                            .and_then(ast::RecordPat::cast)
+                    });
+
+                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
+                // this is initialized from a `FieldExpr`.
+                if let Some(record_pat) = record_pat {
+                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
+                    let field_expr = match let_stmt.initializer()? {
+                        ast::Expr::FieldExpr(field_expr) => field_expr,
+                        _ => return None,
+                    };
+
+                    self.type_of_expr(&field_expr.expr()?)
+                } else {
+                    None
+                }
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
     }
 }
 
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index cf93205b6e3..e29f65a7849 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -565,29 +565,21 @@ fn highlight_element(
                 _ => h,
             }
         }
-        T![&] => {
-            let ref_expr = element.parent().and_then(ast::RefExpr::cast)?;
-            let expr = ref_expr.expr()?;
-            let field_expr = match expr {
-                ast::Expr::FieldExpr(fe) => fe,
-                _ => return None,
-            };
-
-            let expr = field_expr.expr()?;
-            let ty = sema.type_of_expr(&expr)?;
-            if !ty.is_packed(db) {
-                return None;
-            }
-
-            // FIXME This needs layout computation to be correct. It will highlight
-            // more than it should with the current implementation.
-
-            HighlightTag::Operator | HighlightModifier::Unsafe
-        }
         p if p.is_punct() => match p {
-            T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => {
-                HighlightTag::Operator.into()
+            T![&] => {
+                let h = HighlightTag::Operator.into();
+                let is_unsafe = element
+                    .parent()
+                    .and_then(ast::RefExpr::cast)
+                    .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
+                    .unwrap_or(false);
+                if is_unsafe {
+                    h | HighlightModifier::Unsafe
+                } else {
+                    h
+                }
             }
+            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(),
             T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
                 HighlightTag::Macro.into()
             }
@@ -668,22 +660,18 @@ fn highlight_element(
                         HighlightTag::SelfKeyword.into()
                     }
                 }
-                T![ref] => {
-                    let modifier: Option<HighlightModifier> = (|| {
-                        let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
-                        if sema.is_unsafe_pat(&ast::Pat::BindPat(bind_pat)) {
+                T![ref] => element
+                    .parent()
+                    .and_then(ast::BindPat::cast)
+                    .and_then(|bind_pat| {
+                        if sema.is_unsafe_bind_pat(&bind_pat) {
                             Some(HighlightModifier::Unsafe)
                         } else {
                             None
                         }
-                    })();
-
-                    if let Some(modifier) = modifier {
-                        h | modifier
-                    } else {
-                        h
-                    }
-                }
+                    })
+                    .map(|modifier| h | modifier)
+                    .unwrap_or(h),
                 _ => h,
             }
         }
@@ -713,31 +701,6 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
-fn is_method_call_unsafe(
-    sema: &Semantics<RootDatabase>,
-    method_call_expr: ast::MethodCallExpr,
-) -> Option<()> {
-    let expr = method_call_expr.expr()?;
-    let field_expr =
-        if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
-    let ty = sema.type_of_expr(&field_expr.expr()?)?;
-    if !ty.is_packed(sema.db) {
-        return None;
-    }
-
-    let func = sema.resolve_method_call(&method_call_expr)?;
-    if func.has_self_param(sema.db) {
-        let params = func.params(sema.db);
-        if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
-            Some(())
-        } else {
-            None
-        }
-    } else {
-        None
-    }
-}
-
 fn highlight_name(
     sema: &Semantics<RootDatabase>,
     db: &RootDatabase,
@@ -767,7 +730,7 @@ fn highlight_name(
                     let is_unsafe = name_ref
                         .and_then(|name_ref| name_ref.syntax().parent())
                         .and_then(ast::MethodCallExpr::cast)
-                        .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
+                        .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
                     if is_unsafe.is_some() {
                         h |= HighlightModifier::Unsafe;
                     }
@@ -846,7 +809,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
         METHOD_CALL_EXPR => {
             let mut h = Highlight::new(HighlightTag::Function);
             let is_unsafe = ast::MethodCallExpr::cast(parent)
-                .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
+                .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
 
             if is_unsafe.is_some() {
                 h |= HighlightModifier::Unsafe;
@@ -866,7 +829,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
                     })
                 })
                 .unwrap_or(false);
-            if is_union { h | HighlightModifier::Unsafe } else { h.into() }
+            if is_union {
+                h | HighlightModifier::Unsafe
+            } else {
+                h.into()
+            }
         }
         PATH_SEGMENT => {
             let path = match parent.parent().and_then(ast::Path::cast) {
diff --git a/crates/ra_ide/test_data/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html
index 46c1e0a11fd..6322d404fba 100644
--- a/crates/ra_ide/test_data/highlight_doctest.html
+++ b/crates/ra_ide/test_data/highlight_doctest.html
@@ -87,7 +87,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// ```sh</span>
     <span class="comment documentation">/// echo 1</span>
     <span class="comment documentation">/// ```</span>
-    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span>
+    <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span>
         <span class="bool_literal">true</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
index 60c39439944..18addd00d28 100644
--- a/crates/ra_ide/test_data/highlight_injection.html
+++ b/crates/ra_ide/test_data/highlight_injection.html
@@ -35,7 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 </style>
-<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> &<span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 454ff6d5f8d..a2df2c27e6b 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
 
 <span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
-    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+    <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span>
@@ -55,7 +55,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
-    <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> &<span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
     <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
         <span class="comment">// unsafe fn and method calls</span>
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 678cf9bd335..8e0160eee5b 100644
--- a/crates/ra_ide/test_data/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -45,11 +45,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="punctuation">}</span>
 
 <span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span>
+    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span>
 <span class="punctuation">}</span>
 
 <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
-    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span>&<span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
         <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
     <span class="punctuation">}</span>
 
-    <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span>&<span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
         <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span>
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">noop!</span><span class="punctuation">(</span><span class="macro">noop</span><span class="macro">!</span><span class="punctuation">(</span><span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> &<span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> &<span class="variable mutable">y</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="punctuation">;</span>
 
     <span class="keyword">let</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable declaration">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 

From 39fdd41df4052cef5da4876067ae28615012476b Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 23 Jul 2020 18:31:28 -0400
Subject: [PATCH 35/76] Return bool from is_unsafe_method_call and cleanup
 usages

---
 crates/ra_hir/src/semantics.rs           | 38 ++++++++++++------------
 crates/ra_ide/src/syntax_highlighting.rs | 11 +++----
 2 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index f706a186e7f..9697c7082b5 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -281,26 +281,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.assert_contains_node(node)
     }
 
-    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> Option<()> {
-        let expr = method_call_expr.expr()?;
-        let field_expr =
-            if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
-        let ty = self.type_of_expr(&field_expr.expr()?)?;
-        if !ty.is_packed(self.db) {
-            return None;
-        }
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
+        method_call_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                    field_expr
+                } else {
+                    return None;
+                };
+                let ty = self.type_of_expr(&field_expr.expr()?)?;
+                if !ty.is_packed(self.db) {
+                    return None;
+                }
 
-        let func = self.resolve_method_call(&method_call_expr)?;
-        if func.has_self_param(self.db) {
-            let params = func.params(self.db);
-            if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
-                Some(())
-            } else {
-                None
-            }
-        } else {
-            None
-        }
+                let func = self.resolve_method_call(&method_call_expr)?;
+                let is_unsafe = func.has_self_param(self.db)
+                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
+                Some(is_unsafe)
+            })
+            .unwrap_or(false)
     }
 
     pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index e29f65a7849..4527885e934 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -730,8 +730,9 @@ fn highlight_name(
                     let is_unsafe = name_ref
                         .and_then(|name_ref| name_ref.syntax().parent())
                         .and_then(ast::MethodCallExpr::cast)
-                        .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
-                    if is_unsafe.is_some() {
+                        .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr))
+                        .unwrap_or(false);
+                    if is_unsafe {
                         h |= HighlightModifier::Unsafe;
                     }
                 }
@@ -809,9 +810,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
         METHOD_CALL_EXPR => {
             let mut h = Highlight::new(HighlightTag::Function);
             let is_unsafe = ast::MethodCallExpr::cast(parent)
-                .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
-
-            if is_unsafe.is_some() {
+                .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr))
+                .unwrap_or(false);
+            if is_unsafe {
                 h |= HighlightModifier::Unsafe;
             }
 

From 61dff939f909e0c53bcd3be4c3e672c794022cde Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Thu, 30 Jul 2020 09:26:40 -0400
Subject: [PATCH 36/76] Move unsafe semantics methods into `SemanticsImpl` and
 reference them in `Semantics`

---
 crates/ra_hir/src/semantics.rs | 154 ++++++++++++++++++---------------
 1 file changed, 83 insertions(+), 71 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 9697c7082b5..758d0040986 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -282,83 +282,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     }
 
     pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
-        method_call_expr
-            .expr()
-            .and_then(|expr| {
-                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
-                    field_expr
-                } else {
-                    return None;
-                };
-                let ty = self.type_of_expr(&field_expr.expr()?)?;
-                if !ty.is_packed(self.db) {
-                    return None;
-                }
-
-                let func = self.resolve_method_call(&method_call_expr)?;
-                let is_unsafe = func.has_self_param(self.db)
-                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
-                Some(is_unsafe)
-            })
-            .unwrap_or(false)
+        self.imp.is_unsafe_method_call(method_call_expr)
     }
 
     pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
-        ref_expr
-            .expr()
-            .and_then(|expr| {
-                let field_expr = match expr {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-                let expr = field_expr.expr()?;
-                self.type_of_expr(&expr)
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.is_packed(self.db))
-            .unwrap_or(false)
-
-        // FIXME This needs layout computation to be correct. It will highlight
-        // more than it should with the current implementation.
+        self.imp.is_unsafe_ref_expr(ref_expr)
     }
 
     pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        bind_pat
-            .syntax()
-            .parent()
-            .and_then(|parent| {
-                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-                // so this tries to lookup the `BindPat` anywhere along that structure to the
-                // `RecordPat` so we can get the containing type.
-                let record_pat = ast::RecordFieldPat::cast(parent.clone())
-                    .and_then(|record_pat| record_pat.syntax().parent())
-                    .or_else(|| Some(parent.clone()))
-                    .and_then(|parent| {
-                        ast::RecordFieldPatList::cast(parent)?
-                            .syntax()
-                            .parent()
-                            .and_then(ast::RecordPat::cast)
-                    });
-
-                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
-                // this is initialized from a `FieldExpr`.
-                if let Some(record_pat) = record_pat {
-                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
-                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
-                    let field_expr = match let_stmt.initializer()? {
-                        ast::Expr::FieldExpr(field_expr) => field_expr,
-                        _ => return None,
-                    };
-
-                    self.type_of_expr(&field_expr.expr()?)
-                } else {
-                    None
-                }
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.is_packed(self.db))
-            .unwrap_or(false)
+        self.imp.is_unsafe_bind_pat(bind_pat)
     }
 }
 
@@ -655,6 +587,86 @@ impl<'db> SemanticsImpl<'db> {
         });
         InFile::new(file_id, node)
     }
+
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
+        method_call_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                    field_expr
+                } else {
+                    return None;
+                };
+                let ty = self.type_of_expr(&field_expr.expr()?)?;
+                if !ty.is_packed(self.db) {
+                    return None;
+                }
+
+                let func = self.resolve_method_call(&method_call_expr).map(Function::from)?;
+                let is_unsafe = func.has_self_param(self.db)
+                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
+                Some(is_unsafe)
+            })
+            .unwrap_or(false)
+    }
+
+    pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+        ref_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = match expr {
+                    ast::Expr::FieldExpr(field_expr) => field_expr,
+                    _ => return None,
+                };
+                let expr = field_expr.expr()?;
+                self.type_of_expr(&expr)
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+
+        // FIXME This needs layout computation to be correct. It will highlight
+        // more than it should with the current implementation.
+    }
+
+    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
+        bind_pat
+            .syntax()
+            .parent()
+            .and_then(|parent| {
+                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
+                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
+                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `RecordPat` so we can get the containing type.
+                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                    .and_then(|record_pat| record_pat.syntax().parent())
+                    .or_else(|| Some(parent.clone()))
+                    .and_then(|parent| {
+                        ast::RecordFieldPatList::cast(parent)?
+                            .syntax()
+                            .parent()
+                            .and_then(ast::RecordPat::cast)
+                    });
+
+                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
+                // this is initialized from a `FieldExpr`.
+                if let Some(record_pat) = record_pat {
+                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
+                    let field_expr = match let_stmt.initializer()? {
+                        ast::Expr::FieldExpr(field_expr) => field_expr,
+                        _ => return None,
+                    };
+
+                    self.type_of_expr(&field_expr.expr()?)
+                } else {
+                    None
+                }
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+    }
 }
 
 pub trait ToDef: AstNode + Clone {

From 2199d0cda9c745ecb460dd987b8da982d02bc130 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Fri, 7 Aug 2020 10:40:09 -0400
Subject: [PATCH 37/76] Fix type names broken by rebase, redo expected test
 because of rebase

---
 crates/ra_hir/src/semantics.rs                | 22 ++++++++------
 crates/ra_ide/src/syntax_highlighting.rs      |  6 ++--
 crates/ra_ide/test_data/highlight_unsafe.html | 30 +++++++++++++++++--
 3 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 758d0040986..872f5fa4c79 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -289,8 +289,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.is_unsafe_ref_expr(ref_expr)
     }
 
-    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        self.imp.is_unsafe_bind_pat(bind_pat)
+    pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+        self.imp.is_unsafe_ident_pat(ident_pat)
     }
 }
 
@@ -629,20 +629,24 @@ impl<'db> SemanticsImpl<'db> {
         // more than it should with the current implementation.
     }
 
-    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        bind_pat
+    pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+        if !ident_pat.ref_token().is_some() {
+            return false;
+        }
+
+        ident_pat
             .syntax()
             .parent()
             .and_then(|parent| {
-                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
+                // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
+                // so this tries to lookup the `IdentPat` anywhere along that structure to the
                 // `RecordPat` so we can get the containing type.
-                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                let record_pat = ast::RecordPatField::cast(parent.clone())
                     .and_then(|record_pat| record_pat.syntax().parent())
                     .or_else(|| Some(parent.clone()))
                     .and_then(|parent| {
-                        ast::RecordFieldPatList::cast(parent)?
+                        ast::RecordPatFieldList::cast(parent)?
                             .syntax()
                             .parent()
                             .and_then(ast::RecordPat::cast)
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 4527885e934..c62bb3f1abc 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -662,9 +662,9 @@ fn highlight_element(
                 }
                 T![ref] => element
                     .parent()
-                    .and_then(ast::BindPat::cast)
-                    .and_then(|bind_pat| {
-                        if sema.is_unsafe_bind_pat(&bind_pat) {
+                    .and_then(ast::IdentPat::cast)
+                    .and_then(|ident_pat| {
+                        if sema.is_unsafe_ident_pat(&ident_pat) {
                             Some(HighlightModifier::Unsafe)
                         } else {
                             None
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index a2df2c27e6b..552fea66892 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -54,6 +54,19 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
 
+<span class="attribute">#</span><span class="attribute">[</span><span class="function attribute">repr</span><span class="punctuation">(</span><span class="attribute">packed</span><span class="punctuation">)</span><span class="attribute">]</span>
+<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span>
+    <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span>
+<span class="punctuation">}</span>
+
+<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span>
+<span class="punctuation">}</span>
+
+<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="punctuation">{</span>
+    <span class="keyword">fn</span> <span class="function declaration">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
+<span class="punctuation">}</span>
+
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
     <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
     <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
@@ -66,8 +79,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
         <span class="punctuation">}</span>
         <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>
-        <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="variable">x</span><span class="punctuation">;</span>
+
+        <span class="comment">// unsafe deref</span>
+        <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span>
+
+        <span class="comment">// unsafe access to a static mut</span>
         <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
+
+        <span class="comment">// unsafe ref of packed fields</span>
+        <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
+        <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span>
+
+        <span class="comment">// unsafe auto ref of packed field</span>
+        <span class="variable">packed</span><span class="punctuation">.</span><span class="field">a</span><span class="punctuation">.</span><span class="function unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
     <span class="punctuation">}</span>
 <span class="punctuation">}</span></code></pre>
\ No newline at end of file

From 72baf1acdd544c645fd69c16967b91be9e75371b Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria <Nashenas88@users.noreply.github.com>
Date: Sun, 9 Aug 2020 18:54:04 -0400
Subject: [PATCH 38/76] Remove unused import left behind after rebasing

---
 crates/ra_ide/src/syntax_highlighting.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index c62bb3f1abc..c10e15db8b6 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -4,7 +4,7 @@ mod injection;
 #[cfg(test)]
 mod tests;
 
-use hir::{Name, Semantics, TypeRef, VariantDef};
+use hir::{Name, Semantics, VariantDef};
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
     RootDatabase,

From d180b8bbe8c9e31953069bae387b5214fbb51d64 Mon Sep 17 00:00:00 2001
From: Igor Aleksanov <popzxc@yandex.ru>
Date: Mon, 10 Aug 2020 15:50:27 +0300
Subject: [PATCH 39/76] Revert boxing for large enum variant

---
 crates/flycheck/src/lib.rs | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 36e0e085ac5..ec769459c15 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -106,9 +106,10 @@ struct FlycheckActor {
     cargo_handle: Option<CargoHandle>,
 }
 
+#[allow(clippy::large_enum_variant)]
 enum Event {
     Restart(Restart),
-    CheckEvent(Option<Box<cargo_metadata::Message>>),
+    CheckEvent(Option<cargo_metadata::Message>),
 }
 
 impl FlycheckActor {
@@ -123,7 +124,7 @@ impl FlycheckActor {
         let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
         select! {
             recv(inbox) -> msg => msg.ok().map(Event::Restart),
-            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok().map(Box::new))),
+            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
         }
     }
     fn run(mut self, inbox: Receiver<Restart>) {
@@ -149,7 +150,7 @@ impl FlycheckActor {
                     let res = cargo_handle.join();
                     self.send(Message::Progress(Progress::DidFinish(res)));
                 }
-                Event::CheckEvent(Some(message)) => match *message {
+                Event::CheckEvent(Some(message)) => match message {
                     cargo_metadata::Message::CompilerArtifact(msg) => {
                         self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
                     }

From 9842bbfd275a65072bbfe05405daa73f7c27b2de Mon Sep 17 00:00:00 2001
From: kjeremy <kjeremy@gmail.com>
Date: Mon, 10 Aug 2020 10:56:16 -0400
Subject: [PATCH 40/76] cargo update

---
 Cargo.lock | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index dc49fc4bdc0..a094ec4f7ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -418,9 +418,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.8.1"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
 dependencies = [
  "autocfg",
 ]
@@ -465,9 +465,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7"
+checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -871,9 +871,9 @@ dependencies = [
 
 [[package]]
 name = "pico-args"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1eee8b1f4966c8343d7ca0f5a8452cd35d5610a2e0efbe2a68cae44bef2046"
+checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
 
 [[package]]
 name = "plain"
@@ -1694,9 +1694,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-tree"
-version = "0.1.4"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ee7f0f53ed2093971a698db799ef56a2dfd89b32e3aeb5165f0e637a02be04"
+checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f"
 dependencies = [
  "ansi_term",
  "atty",

From cf6d14cee7fee17ed668f502de5042136446945b Mon Sep 17 00:00:00 2001
From: Jeremy Kolb <kjeremy@gmail.com>
Date: Sun, 9 Aug 2020 16:27:48 -0400
Subject: [PATCH 41/76] Return InvalidRequest if Shutdown has been requested

From the LSP 3.16 spec: "If a server receives requests after a shutdown request those requests should error with InvalidRequest."
---
 crates/rust-analyzer/src/global_state.rs |  2 ++
 crates/rust-analyzer/src/main_loop.rs    | 15 ++++++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 0e592ac1bea..658a50d1507 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -73,6 +73,7 @@ pub(crate) struct GlobalState {
     pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
     pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
     pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
+    pub(crate) shutdown_requested: bool,
     pub(crate) status: Status,
     pub(crate) source_root_config: SourceRootConfig,
     pub(crate) proc_macro_client: ProcMacroClient,
@@ -124,6 +125,7 @@ impl GlobalState {
             mem_docs: FxHashMap::default(),
             semantic_tokens_cache: Arc::new(Default::default()),
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
+            shutdown_requested: false,
             status: Status::default(),
             source_root_config: SourceRootConfig::default(),
             proc_macro_client: ProcMacroClient::dummy(),
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 0ac6434dd79..e6cf46df23e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -337,6 +337,16 @@ impl GlobalState {
     fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
         self.register_request(&req, request_received);
 
+        if self.shutdown_requested {
+            self.respond(Response::new_err(
+                req.id,
+                lsp_server::ErrorCode::InvalidRequest as i32,
+                "Shutdown already requested.".to_owned(),
+            ));
+
+            return Ok(());
+        }
+
         if self.status == Status::Loading && req.method != "shutdown" {
             self.respond(lsp_server::Response::new_err(
                 req.id,
@@ -351,7 +361,10 @@ impl GlobalState {
             .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
             .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
             .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
-            .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))?
+            .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
+                s.shutdown_requested = true;
+                Ok(())
+            })?
             .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| {
                 handlers::handle_selection_range(s.snapshot(), p)
             })?

From ff60fdc315e324843589554f45e2e3c8f7cd1bf4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro>
Date: Mon, 10 Aug 2020 21:31:38 +0300
Subject: [PATCH 42/76] Fix typos in syntax.md

---
 docs/dev/syntax.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index d4bc4b07c4e..f1bcdc4aff8 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -74,7 +74,7 @@ Points of note:
 * The original text can be recovered by concatenating the texts of all tokens in order.
 * Accessing a child of particular type (for example, parameter list of a function) generally involves linerary traversing the children, looking for a specific `kind`.
 * Modifying the tree is roughly `O(depth)`.
-  We don't make special efforts to guarantree that the depth is not liner, but, in practice, syntax trees are branchy and shallow.
+  We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow.
 * If mandatory (grammar wise) node is missing from the input, it's just missing from the tree.
 * If an extra erroneous input is present, it is wrapped into a node with `ERROR` kind, and treated just like any other node.
 * Parser errors are not a part of syntax tree.

From a8beb79a160326ab04a74dfd18e4853b8cecf2cb Mon Sep 17 00:00:00 2001
From: Tim Weis <tim-weis@users.noreply.github.com>
Date: Tue, 11 Aug 2020 01:01:25 +0200
Subject: [PATCH 43/76] Update README.md

Fixed formatting.
---
 docs/dev/README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/dev/README.md b/docs/dev/README.md
index 67813a9c07f..51cf716b3df 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -256,9 +256,9 @@ Release steps:
    * checkout the `release` branch
    * reset it to `upstream/nightly`
    * push it to `upstream`. This triggers GitHub Actions which:
-    ** runs `cargo xtask dist` to package binaries and VS Code extension
-    ** makes a GitHub release
-    ** pushes VS Code extension to the marketplace
+     * runs `cargo xtask dist` to package binaries and VS Code extension
+     * makes a GitHub release
+     * pushes VS Code extension to the marketplace
    * create new changelog in `rust-analyzer.github.io`
    * create `rust-analyzer.github.io/git.log` file with the log of merge commits since last release
 2. While the release is in progress, fill-in the changelog using `git.log`

From 4f386afb16297f588484ff24e2b4693218893a94 Mon Sep 17 00:00:00 2001
From: Veetaha <veetaha2@gmail.com>
Date: Tue, 11 Aug 2020 03:12:09 +0300
Subject: [PATCH 44/76] Log the command flycheck runs to debug
 misconfigurations

Without this users have no clue why flycheck fails to run.
This is what is printed to the output channel:
```
[ERROR rust_analyzer::main_loop] cargo check failed: Cargo watcher failed,the command produced no valid metadata (exit code: ExitStatus(ExitStatus(25856)))
```

I stumbled with this figuring out that rust-analyzer adds `--all-features` which is not intended
for some crates in the workspace (e.g. they have mutually-exclusive features.
Having the command rust-analyzer ran should help a lot
---
 crates/flycheck/src/lib.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 7c38f5ef9d5..31e14246de8 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -1,4 +1,4 @@
-//! cargo_check provides the functionality needed to run `cargo check` or
+//! Flycheck provides the functionality needed to run `cargo check` or
 //! another compatible command (f.x. clippy) in a background thread and provide
 //! LSP diagnostics based on the output of the command.
 
@@ -147,6 +147,12 @@ impl FlycheckActor {
                     // avoid busy-waiting.
                     let cargo_handle = self.cargo_handle.take().unwrap();
                     let res = cargo_handle.join();
+                    if res.is_err() {
+                        log::error!(
+                            "Flycheck failed to run the following command: {:?}",
+                            self.check_command()
+                        )
+                    }
                     self.send(Message::Progress(Progress::DidFinish(res)));
                 }
                 Event::CheckEvent(Some(message)) => match message {
@@ -253,7 +259,7 @@ impl CargoHandle {
             return Err(io::Error::new(
                 io::ErrorKind::Other,
                 format!(
-                    "Cargo watcher failed,the command produced no valid metadata (exit code: {:?})",
+                    "Cargo watcher failed, the command produced no valid metadata (exit code: {:?})",
                     exit_status
                 ),
             ));

From dc6e1e0dac318b36ec43ffced3d4059a9b8652e5 Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Tue, 11 Aug 2020 10:55:26 +0800
Subject: [PATCH 45/76] Address some FIXMEs

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 Cargo.lock                             |  1 +
 crates/ra_assists/Cargo.toml           |  1 +
 crates/ra_assists/src/ast_transform.rs | 13 +++++--------
 crates/ra_assists/src/lib.rs           | 25 ++++++++++++++++++++-----
 crates/rust-analyzer/src/handlers.rs   |  2 +-
 crates/rust-analyzer/src/to_proto.rs   |  8 ++++----
 6 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a094ec4f7ae..936e863f52f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -912,6 +912,7 @@ dependencies = [
  "ra_db",
  "ra_fmt",
  "ra_hir",
+ "ra_hir_expand",
  "ra_ide_db",
  "ra_prof",
  "ra_syntax",
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index bd2905f080a..a436e861d7e 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -22,4 +22,5 @@ ra_prof = { path = "../ra_prof" }
 ra_db = { path = "../ra_db" }
 ra_ide_db = { path = "../ra_ide_db" }
 hir = { path = "../ra_hir", package = "ra_hir" }
+hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
 test_utils = { path = "../test_utils" }
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 15ec75c956a..02c4a4baeb0 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -2,6 +2,7 @@
 use rustc_hash::FxHashMap;
 
 use hir::{HirDisplay, PathResolution, SemanticsScope};
+use hir_expand::hygiene::Hygiene;
 use ra_syntax::{
     algo::SyntaxRewriter,
     ast::{self, AstNode},
@@ -51,7 +52,7 @@ impl<'a> SubstituteTypeParams<'a> {
             // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
             .skip(1)
             // The actual list of trait type parameters may be longer than the one
-            // used in the `impl` block due to trailing default type parametrs.
+            // used in the `impl` block due to trailing default type parameters.
             // For that case we extend the `substs` with an empty iterator so we
             // can still hit those trailing values and check if they actually have
             // a default type. If they do, go for that type from `hir` to `ast` so
@@ -110,9 +111,7 @@ impl<'a> SubstituteTypeParams<'a> {
             ast::Type::PathType(path_type) => path_type.path()?,
             _ => return None,
         };
-        // FIXME: use `hir::Path::from_src` instead.
-        #[allow(deprecated)]
-        let path = hir::Path::from_ast(path)?;
+        let path = hir::Path::from_src(path, &Hygiene::new_unhygienic())?;
         let resolution = self.source_scope.resolve_hir_path(&path)?;
         match resolution {
             hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
@@ -152,10 +151,8 @@ impl<'a> QualifyPaths<'a> {
             // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
             return None;
         }
-        // FIXME: use `hir::Path::from_src` instead.
-        #[allow(deprecated)]
-        let hir_path = hir::Path::from_ast(p.clone());
-        let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
+        let hir_path = hir::Path::from_src(p.clone(), &Hygiene::new_unhygienic())?;
+        let resolution = self.source_scope.resolve_hir_path(&hir_path)?;
         match resolution {
             PathResolution::Def(def) => {
                 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 507646cc802..890996a68d9 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -66,13 +66,13 @@ pub struct GroupLabel(pub String);
 
 #[derive(Debug, Clone)]
 pub struct Assist {
-    pub id: AssistId,
+    id: AssistId,
     /// Short description of the assist, as shown in the UI.
-    pub label: String,
-    pub group: Option<GroupLabel>,
+    label: String,
+    group: Option<GroupLabel>,
     /// Target ranges are used to sort assists: the smaller the target range,
     /// the more specific assist is, and so it should be sorted first.
-    pub target: TextRange,
+    target: TextRange,
 }
 
 #[derive(Debug, Clone)]
@@ -120,10 +120,25 @@ impl Assist {
         group: Option<GroupLabel>,
         target: TextRange,
     ) -> Assist {
-        // FIXME: make fields private, so that this invariant can't be broken
         assert!(label.starts_with(|c: char| c.is_uppercase()));
         Assist { id, label, group, target }
     }
+
+    pub fn id(&self) -> AssistId {
+        self.id
+    }
+
+    pub fn label(&self) -> String {
+        self.label.clone()
+    }
+
+    pub fn group(&self) -> Option<GroupLabel> {
+        self.group.clone()
+    }
+
+    pub fn target(&self) -> TextRange {
+        self.target
+    }
 }
 
 mod handlers {
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 895af1dd78e..c2afcf192de 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -864,7 +864,7 @@ pub(crate) fn handle_resolve_code_action(
     let (id_string, index) = split_once(&params.id, ':').unwrap();
     let index = index.parse::<usize>().unwrap();
     let assist = &assists[index];
-    assert!(assist.assist.id.0 == id_string);
+    assert!(assist.assist.id().0 == id_string);
     Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
 }
 
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 27460db78cc..62fda8a1f20 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -704,10 +704,10 @@ pub(crate) fn unresolved_code_action(
     index: usize,
 ) -> Result<lsp_ext::CodeAction> {
     let res = lsp_ext::CodeAction {
-        title: assist.label,
-        id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
-        group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
-        kind: Some(code_action_kind(assist.id.1)),
+        title: assist.label(),
+        id: Some(format!("{}:{}", assist.id().0.to_owned(), index.to_string())),
+        group: assist.group().filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
+        kind: Some(code_action_kind(assist.id().1)),
         edit: None,
         is_preferred: None,
     };

From ace75f95905564a34f46be57ead51828844da745 Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Tue, 11 Aug 2020 12:09:11 +0800
Subject: [PATCH 46/76] Typo fix

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_assists/src/tests.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 18fcb904987..e738364220d 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -20,7 +20,7 @@ pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_
 
 // FIXME: instead of having a separate function here, maybe use
 // `extract_ranges` and mark the target as `<target> </target>` in the
-// fixuture?
+// fixture?
 pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
     check(assist, ra_fixture, ExpectedResult::Target(target));
 }

From b69dfddb572b9182f4880065ca5034aba8b15ce3 Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Tue, 11 Aug 2020 14:35:15 +0800
Subject: [PATCH 47/76] Remove redundant dependencies

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 Cargo.lock                             | 1 -
 crates/ra_assists/Cargo.toml           | 1 -
 crates/ra_assists/src/ast_transform.rs | 5 ++---
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 936e863f52f..a094ec4f7ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -912,7 +912,6 @@ dependencies = [
  "ra_db",
  "ra_fmt",
  "ra_hir",
- "ra_hir_expand",
  "ra_ide_db",
  "ra_prof",
  "ra_syntax",
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index a436e861d7e..bd2905f080a 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -22,5 +22,4 @@ ra_prof = { path = "../ra_prof" }
 ra_db = { path = "../ra_db" }
 ra_ide_db = { path = "../ra_ide_db" }
 hir = { path = "../ra_hir", package = "ra_hir" }
-hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
 test_utils = { path = "../test_utils" }
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 02c4a4baeb0..6c92124eda1 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -2,7 +2,6 @@
 use rustc_hash::FxHashMap;
 
 use hir::{HirDisplay, PathResolution, SemanticsScope};
-use hir_expand::hygiene::Hygiene;
 use ra_syntax::{
     algo::SyntaxRewriter,
     ast::{self, AstNode},
@@ -111,7 +110,7 @@ impl<'a> SubstituteTypeParams<'a> {
             ast::Type::PathType(path_type) => path_type.path()?,
             _ => return None,
         };
-        let path = hir::Path::from_src(path, &Hygiene::new_unhygienic())?;
+        let path = hir::Path::from_src(path, &hir::Hygiene::new_unhygienic())?;
         let resolution = self.source_scope.resolve_hir_path(&path)?;
         match resolution {
             hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
@@ -151,7 +150,7 @@ impl<'a> QualifyPaths<'a> {
             // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
             return None;
         }
-        let hir_path = hir::Path::from_src(p.clone(), &Hygiene::new_unhygienic())?;
+        let hir_path = hir::Path::from_src(p.clone(), &hir::Hygiene::new_unhygienic())?;
         let resolution = self.source_scope.resolve_hir_path(&hir_path)?;
         match resolution {
             PathResolution::Def(def) => {

From fc01c7846d5c6970e194dd223e49b863b3189432 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro>
Date: Tue, 11 Aug 2020 09:54:33 +0300
Subject: [PATCH 48/76] Use Hygiene in completion

---
 crates/ra_ide/src/completion/completion_context.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 6b03b30bb5d..4aa761148d8 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -27,7 +27,7 @@ pub(crate) struct CompletionContext<'a> {
     pub(super) scope: SemanticsScope<'a>,
     pub(super) db: &'a RootDatabase,
     pub(super) config: &'a CompletionConfig,
-    pub(super) offset: TextSize,
+    pub(super) position: FilePosition,
     /// The token before the cursor, in the original file.
     pub(super) original_token: SyntaxToken,
     /// The token before the cursor, in the macro-expanded file.
@@ -117,7 +117,7 @@ impl<'a> CompletionContext<'a> {
             config,
             original_token,
             token,
-            offset: position.offset,
+            position,
             krate,
             expected_type: None,
             name_ref_syntax: None,
@@ -209,7 +209,7 @@ impl<'a> CompletionContext<'a> {
             mark::hit!(completes_if_prefix_is_keyword);
             self.original_token.text_range()
         } else {
-            TextRange::empty(self.offset)
+            TextRange::empty(self.position.offset)
         }
     }
 
@@ -379,8 +379,8 @@ impl<'a> CompletionContext<'a> {
             self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
             self.has_type_args = segment.generic_arg_list().is_some();
 
-            #[allow(deprecated)]
-            if let Some(path) = hir::Path::from_ast(path.clone()) {
+            let hygiene = hir::Hygiene::new(self.db, self.position.file_id.into());
+            if let Some(path) = hir::Path::from_src(path.clone(), &hygiene) {
                 if let Some(path_prefix) = path.qualifier() {
                     self.path_prefix = Some(path_prefix);
                     return;

From 7fbc9afca48240cf16c82a996ac7b14c554bade6 Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Tue, 11 Aug 2020 16:50:45 +0800
Subject: [PATCH 49/76] Typo fix

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_hir_expand/src/hygiene.rs | 2 +-
 crates/ra_hir_expand/src/lib.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index 6b482a60c54..aefe47bd32a 100644
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ b/crates/ra_hir_expand/src/hygiene.rs
@@ -17,7 +17,7 @@ pub struct Hygiene {
     // This is what `$crate` expands to
     def_crate: Option<CrateId>,
 
-    // Indiciate this is a local inner macro
+    // Indicate this is a local inner macro
     local_inner: bool,
 }
 
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 2e8d6369171..abae498d826 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -44,7 +44,7 @@ mod test_db;
 /// containing the call plus the offset of the macro call in the file. Note that
 /// this is a recursive definition! However, the size_of of `HirFileId` is
 /// finite (because everything bottoms out at the real `FileId`) and small
-/// (`MacroCallId` uses the location interner).
+/// (`MacroCallId` uses the location internal).
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HirFileId(HirFileIdRepr);
 

From 6ef019bd4601b9dc36325e096d066a4ddbda1bdf Mon Sep 17 00:00:00 2001
From: JmPotato <ghzpotato@gmail.com>
Date: Tue, 11 Aug 2020 17:19:02 +0800
Subject: [PATCH 50/76] Revert some FIXMEs

Signed-off-by: JmPotato <ghzpotato@gmail.com>
---
 crates/ra_assists/src/ast_transform.rs | 10 +++++++---
 crates/ra_hir_expand/src/lib.rs        |  3 ++-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 6c92124eda1..07c978378a6 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -110,7 +110,9 @@ impl<'a> SubstituteTypeParams<'a> {
             ast::Type::PathType(path_type) => path_type.path()?,
             _ => return None,
         };
-        let path = hir::Path::from_src(path, &hir::Hygiene::new_unhygienic())?;
+        // FIXME: use `hir::Path::from_src` instead.
+        #[allow(deprecated)]
+        let path = hir::Path::from_ast(path)?;
         let resolution = self.source_scope.resolve_hir_path(&path)?;
         match resolution {
             hir::PathResolution::TypeParam(tp) => Some(self.substs.get(&tp)?.syntax().clone()),
@@ -150,8 +152,10 @@ impl<'a> QualifyPaths<'a> {
             // don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
             return None;
         }
-        let hir_path = hir::Path::from_src(p.clone(), &hir::Hygiene::new_unhygienic())?;
-        let resolution = self.source_scope.resolve_hir_path(&hir_path)?;
+        // FIXME: use `hir::Path::from_src` instead.
+        #[allow(deprecated)]
+        let hir_path = hir::Path::from_ast(p.clone());
+        let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
         match resolution {
             PathResolution::Def(def) => {
                 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index abae498d826..8bb735fc625 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -44,7 +44,8 @@ mod test_db;
 /// containing the call plus the offset of the macro call in the file. Note that
 /// this is a recursive definition! However, the size_of of `HirFileId` is
 /// finite (because everything bottoms out at the real `FileId`) and small
-/// (`MacroCallId` uses the location internal).
+/// (`MacroCallId` uses the location interning. You can check details here:
+/// https://en.wikipedia.org/wiki/String_interning).
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HirFileId(HirFileIdRepr);
 

From 26e102a567aadcf86f2e5b575cb6b915991ba088 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 15:53:57 +0300
Subject: [PATCH 51/76] Separate diagnostics and diagnostics fix ranges

---
 crates/ra_ide/src/diagnostics.rs     | 98 ++++++++++++++++------------
 crates/ra_ide/src/lib.rs             |  2 +-
 crates/rust-analyzer/src/handlers.rs |  6 +-
 3 files changed, 59 insertions(+), 47 deletions(-)

diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 73c0b82754e..5c8ea46abb1 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -60,14 +60,16 @@ pub(crate) fn diagnostics(
                 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
                     .into(),
             );
+            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some(fix),
+                fix: Some((fix, range)),
             })
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
+            let range = sema.diagnostics_range(d).range;
             // Note that although we could add a diagnostics to
             // fill the missing tuple field, e.g :
             // `struct A(usize);`
@@ -91,11 +93,15 @@ pub(crate) fn diagnostics(
                         .into_text_edit(&mut builder);
                     builder.finish()
                 };
-                Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()))
+                Some((
+                    Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
+                    range,
+                ))
             };
 
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                // TODO kb use a smaller range here
+                range,
                 message: d.message(),
                 severity: Severity::Error,
                 fix,
@@ -106,20 +112,21 @@ pub(crate) fn diagnostics(
             let replacement = format!("Ok({})", node.syntax());
             let edit = TextEdit::replace(node.syntax().text_range(), replacement);
             let source_change = SourceFileEdit { file_id, edit }.into();
-            let fix = Fix::new("Wrap with ok", source_change);
+            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some(fix),
+                fix: Some((Fix::new("Wrap with ok", source_change), range)),
             })
         })
         .on::<hir::diagnostics::NoSuchField, _>(|d| {
+            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: missing_struct_field_fix(&sema, file_id, d),
+                fix: missing_struct_field_fix(&sema, file_id, d).map(|fix| (fix, range)),
             })
         })
         // Only collect experimental diagnostics when they're enabled.
@@ -222,24 +229,24 @@ fn check_unnecessary_braces_in_use_statement(
 ) -> Option<()> {
     let use_tree_list = ast::UseTreeList::cast(node.clone())?;
     if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
-        let range = use_tree_list.syntax().text_range();
+        let use_range = use_tree_list.syntax().text_range();
         let edit =
             text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
                 .unwrap_or_else(|| {
                     let to_replace = single_use_tree.syntax().text().to_string();
                     let mut edit_builder = TextEditBuilder::default();
-                    edit_builder.delete(range);
-                    edit_builder.insert(range.start(), to_replace);
+                    edit_builder.delete(use_range);
+                    edit_builder.insert(use_range.start(), to_replace);
                     edit_builder.finish()
                 });
 
         acc.push(Diagnostic {
-            range,
+            range: use_range,
             message: "Unnecessary braces in use statement".to_string(),
             severity: Severity::WeakWarning,
-            fix: Some(Fix::new(
-                "Remove unnecessary braces",
-                SourceFileEdit { file_id, edit }.into(),
+            fix: Some((
+                Fix::new("Remove unnecessary braces", SourceFileEdit { file_id, edit }.into()),
+                use_range,
             )),
         });
     }
@@ -254,8 +261,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
     if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] {
         let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
         let end = use_tree_list_node.text_range().end();
-        let range = TextRange::new(start, end);
-        return Some(TextEdit::delete(range));
+        return Some(TextEdit::delete(TextRange::new(start, end)));
     }
     None
 }
@@ -278,13 +284,17 @@ fn check_struct_shorthand_initialization(
                 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
                 let edit = edit_builder.finish();
 
+                let field_range = record_field.syntax().text_range();
                 acc.push(Diagnostic {
-                    range: record_field.syntax().text_range(),
+                    range: field_range,
                     message: "Shorthand struct initialization".to_string(),
                     severity: Severity::WeakWarning,
-                    fix: Some(Fix::new(
-                        "Use struct shorthand initialization",
-                        SourceFileEdit { file_id, edit }.into(),
+                    fix: Some((
+                        Fix::new(
+                            "Use struct shorthand initialization",
+                            SourceFileEdit { file_id, edit }.into(),
+                        ),
+                        field_range,
                     )),
                 });
             }
@@ -304,14 +314,14 @@ mod tests {
     /// Takes a multi-file input fixture with annotated cursor positions,
     /// and checks that:
     ///  * a diagnostic is produced
-    ///  * this diagnostic touches the input cursor position
+    ///  * this diagnostic fix touches the input cursor position
     ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
     fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
         let after = trim_indent(ra_fixture_after);
 
         let (analysis, file_position) = analysis_and_position(ra_fixture_before);
         let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
-        let mut fix = diagnostic.fix.unwrap();
+        let (mut fix, fix_range) = diagnostic.fix.unwrap();
         let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
         let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
         let actual = {
@@ -322,10 +332,9 @@ mod tests {
 
         assert_eq_text!(&after, &actual);
         assert!(
-            diagnostic.range.start() <= file_position.offset
-                && diagnostic.range.end() >= file_position.offset,
-            "diagnostic range {:?} does not touch cursor position {:?}",
-            diagnostic.range,
+            fix_range.start() <= file_position.offset && fix_range.end() >= file_position.offset,
+            "diagnostic fix range {:?} does not touch cursor position {:?}",
+            fix_range,
             file_position.offset
         );
     }
@@ -337,7 +346,7 @@ mod tests {
         let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
         let current_file_id = file_pos.file_id;
         let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
-        let mut fix = diagnostic.fix.unwrap();
+        let mut fix = diagnostic.fix.unwrap().0;
         let edit = fix.source_change.source_file_edits.pop().unwrap();
         let changed_file_id = edit.file_id;
         let before = analysis.file_text(changed_file_id).unwrap();
@@ -628,21 +637,24 @@ fn test_fn() {
                         range: 0..8,
                         severity: Error,
                         fix: Some(
-                            Fix {
-                                label: "Create module",
-                                source_change: SourceChange {
-                                    source_file_edits: [],
-                                    file_system_edits: [
-                                        CreateFile {
-                                            anchor: FileId(
-                                                1,
-                                            ),
-                                            dst: "foo.rs",
-                                        },
-                                    ],
-                                    is_snippet: false,
+                            (
+                                Fix {
+                                    label: "Create module",
+                                    source_change: SourceChange {
+                                        source_file_edits: [],
+                                        file_system_edits: [
+                                            CreateFile {
+                                                anchor: FileId(
+                                                    1,
+                                                ),
+                                                dst: "foo.rs",
+                                            },
+                                        ],
+                                        is_snippet: false,
+                                    },
                                 },
-                            },
+                                0..8,
+                            ),
                         ),
                     },
                 ]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 0fede0d8795..45a4b2421e7 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -105,7 +105,7 @@ pub struct Diagnostic {
     pub message: String,
     pub range: TextRange,
     pub severity: Severity,
-    pub fix: Option<Fix>,
+    pub fix: Option<(Fix, TextRange)>,
 }
 
 #[derive(Debug)]
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index c2afcf192de..144c641b2a8 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -775,9 +775,9 @@ fn handle_fixes(
 
     let fixes_from_diagnostics = diagnostics
         .into_iter()
-        .filter_map(|d| Some((d.range, d.fix?)))
-        .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
-        .map(|(_range, fix)| fix);
+        .filter_map(|d| d.fix)
+        .filter(|(_fix, fix_range)| fix_range.intersect(range).is_some())
+        .map(|(fix, _range)| fix);
     for fix in fixes_from_diagnostics {
         let title = fix.label;
         let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;

From 21e5224484b9214648826e1b15aa9150c79a407c Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 18:45:08 +0300
Subject: [PATCH 52/76] Custom ranges for missing fields

---
 crates/ra_hir_expand/src/diagnostics.rs  |  5 ++-
 crates/ra_hir_ty/src/diagnostics.rs      | 52 ++++++++++++++++++++++--
 crates/ra_hir_ty/src/diagnostics/expr.rs |  1 +
 crates/ra_ide/src/diagnostics.rs         |  6 ++-
 4 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 84ba97b14a1..e889f070fad 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -36,8 +36,9 @@ pub trait AstDiagnostic {
 
 impl dyn Diagnostic {
     pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
-        let node = db.parse_or_expand(self.source().file_id).unwrap();
-        self.source().value.to_node(&node)
+        let source = self.source();
+        let node = db.parse_or_expand(source.file_id).unwrap();
+        source.value.to_node(&node)
     }
 
     pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 977c0525b52..a5b00ed4859 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -9,7 +9,7 @@ use hir_def::DefWithBodyId;
 use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
 use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
 use ra_prof::profile;
-use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
+use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
 use stdx::format_to;
 
 use crate::db::HirDatabase;
@@ -61,6 +61,17 @@ pub struct MissingFields {
     pub file: HirFileId,
     pub field_list: AstPtr<ast::RecordExprFieldList>,
     pub missed_fields: Vec<Name>,
+    pub list_parent_path: Option<AstPtr<ast::Path>>,
+}
+
+impl MissingFields {
+    fn root(&self, db: &dyn AstDatabase) -> SyntaxNode {
+        db.parse_or_expand(self.file).unwrap()
+    }
+
+    pub fn list_parent_ast(&self, db: &dyn AstDatabase) -> Option<ast::Path> {
+        self.list_parent_path.as_ref().map(|path| path.to_node(&self.root(db)))
+    }
 }
 
 impl Diagnostic for MissingFields {
@@ -83,9 +94,7 @@ impl AstDiagnostic for MissingFields {
     type AST = ast::RecordExprFieldList;
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.source().file_id).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::RecordExprFieldList::cast(node).unwrap()
+        self.field_list.to_node(&self.root(db))
     }
 }
 
@@ -318,6 +327,41 @@ mod tests {
         assert_eq!(annotations, actual);
     }
 
+    #[test]
+    fn structure_name_highlighted_for_missing_fields() {
+        check_diagnostics(
+            r#"
+struct Beefy {
+    one: i32,
+    two: i32,
+    three: i32,
+    four: i32,
+    five: i32,
+    six: i32,
+    seven: i32,
+    eight: i32,
+    nine: i32,
+    ten: i32,
+}
+fn baz() {
+    let zz = Beefy {
+           //^^^^^... Missing structure fields:
+           //    |    - seven
+        one: (),
+        two: (),
+        three: (),
+        four: (),
+        five: (),
+        six: (),
+        eight: (),
+        nine: (),
+        ten: (),
+    };
+}
+"#,
+        );
+    }
+
     #[test]
     fn no_such_field_diagnostics() {
         check_diagnostics(
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs
index 95bbf2d955e..3c37fc58e92 100644
--- a/crates/ra_hir_ty/src/diagnostics/expr.rs
+++ b/crates/ra_hir_ty/src/diagnostics/expr.rs
@@ -111,6 +111,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                         file: source_ptr.file_id,
                         field_list: AstPtr::new(&field_list),
                         missed_fields,
+                        list_parent_path: record_lit.path().map(|path| AstPtr::new(&path)),
                     })
                 }
             }
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 5c8ea46abb1..7ae4bda0b8e 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -100,8 +100,10 @@ pub(crate) fn diagnostics(
             };
 
             res.borrow_mut().push(Diagnostic {
-                // TODO kb use a smaller range here
-                range,
+                range: d
+                    .list_parent_ast(db)
+                    .map(|path| path.syntax().text_range())
+                    .unwrap_or(range),
                 message: d.message(),
                 severity: Severity::Error,
                 fix,

From a61f2445cba2a48bb7ea6c8477e3198b297f3c67 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 22:30:55 +0300
Subject: [PATCH 53/76] Less stubs

---
 .../ra_assists/src/handlers/fix_visibility.rs |  2 +-
 crates/ra_hir_expand/src/diagnostics.rs       | 11 ++----
 crates/ra_hir_ty/src/diagnostics.rs           | 38 ++++++++++---------
 crates/ra_ide/src/diagnostics.rs              |  8 +---
 4 files changed, 28 insertions(+), 31 deletions(-)

diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 1aefa79cc30..a19dbf33f6b 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -121,7 +121,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
             Some(cap) => match current_visibility {
                 Some(current_visibility) => builder.replace_snippet(
                     cap,
-                    dbg!(current_visibility.syntax()).text_range(),
+                    current_visibility.syntax().text_range(),
                     format!("$0{}", missing_visibility),
                 ),
                 None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index e889f070fad..ffeca5e8272 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -16,13 +16,16 @@
 
 use std::{any::Any, fmt};
 
-use ra_syntax::{SyntaxNode, SyntaxNodePtr};
+use ra_syntax::SyntaxNodePtr;
 
 use crate::{db::AstDatabase, InFile};
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
     fn source(&self) -> InFile<SyntaxNodePtr>;
+    fn highlighting_source(&self) -> InFile<SyntaxNodePtr> {
+        self.source()
+    }
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
         false
@@ -35,12 +38,6 @@ pub trait AstDiagnostic {
 }
 
 impl dyn Diagnostic {
-    pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
-        let source = self.source();
-        let node = db.parse_or_expand(source.file_id).unwrap();
-        source.value.to_node(&node)
-    }
-
     pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
         self.as_any().downcast_ref()
     }
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index a5b00ed4859..73d2414343d 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -9,7 +9,7 @@ use hir_def::DefWithBodyId;
 use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
 use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
 use ra_prof::profile;
-use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
+use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
 use stdx::format_to;
 
 use crate::db::HirDatabase;
@@ -64,16 +64,6 @@ pub struct MissingFields {
     pub list_parent_path: Option<AstPtr<ast::Path>>,
 }
 
-impl MissingFields {
-    fn root(&self, db: &dyn AstDatabase) -> SyntaxNode {
-        db.parse_or_expand(self.file).unwrap()
-    }
-
-    pub fn list_parent_ast(&self, db: &dyn AstDatabase) -> Option<ast::Path> {
-        self.list_parent_path.as_ref().map(|path| path.to_node(&self.root(db)))
-    }
-}
-
 impl Diagnostic for MissingFields {
     fn message(&self) -> String {
         let mut buf = String::from("Missing structure fields:\n");
@@ -85,16 +75,25 @@ impl Diagnostic for MissingFields {
     fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.field_list.clone().into() }
     }
+
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
     }
+
+    fn highlighting_source(&self) -> InFile<SyntaxNodePtr> {
+        self.list_parent_path
+            .clone()
+            .map(|path| InFile { file_id: self.file, value: path.into() })
+            .unwrap_or_else(|| self.source())
+    }
 }
 
 impl AstDiagnostic for MissingFields {
     type AST = ast::RecordExprFieldList;
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        self.field_list.to_node(&self.root(db))
+        let root = db.parse_or_expand(self.file).unwrap();
+        self.field_list.to_node(&root)
     }
 }
 
@@ -260,7 +259,10 @@ impl AstDiagnostic for MismatchedArgCount {
 #[cfg(test)]
 mod tests {
     use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
-    use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
+    use hir_expand::{
+        db::AstDatabase,
+        diagnostics::{Diagnostic, DiagnosticSinkBuilder},
+    };
     use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
     use ra_syntax::{TextRange, TextSize};
     use rustc_hash::FxHashMap;
@@ -307,7 +309,9 @@ mod tests {
         db.diagnostics(|d| {
             // FXIME: macros...
             let file_id = d.source().file_id.original_file(&db);
-            let range = d.syntax_node(&db).text_range();
+            let highlighting_source = d.highlighting_source();
+            let node = db.parse_or_expand(highlighting_source.file_id).unwrap();
+            let range = highlighting_source.value.to_node(&node).text_range();
             let message = d.message().to_owned();
             actual.entry(file_id).or_default().push((range, message));
         });
@@ -345,7 +349,7 @@ struct Beefy {
 }
 fn baz() {
     let zz = Beefy {
-           //^^^^^... Missing structure fields:
+           //^^^^^ Missing structure fields:
            //    |    - seven
         one: (),
         two: (),
@@ -370,8 +374,8 @@ struct S { foo: i32, bar: () }
 impl S {
     fn new() -> S {
         S {
-        //^... Missing structure fields:
-        //|    - bar
+      //^ Missing structure fields:
+      //|    - bar
             foo: 92,
             baz: 62,
           //^^^^^^^ no such field
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 7ae4bda0b8e..e847df6ea9c 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -69,7 +69,6 @@ pub(crate) fn diagnostics(
             })
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
-            let range = sema.diagnostics_range(d).range;
             // Note that although we could add a diagnostics to
             // fill the missing tuple field, e.g :
             // `struct A(usize);`
@@ -95,15 +94,12 @@ pub(crate) fn diagnostics(
                 };
                 Some((
                     Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
-                    range,
+                    sema.diagnostics_range(d).range,
                 ))
             };
 
             res.borrow_mut().push(Diagnostic {
-                range: d
-                    .list_parent_ast(db)
-                    .map(|path| path.syntax().text_range())
-                    .unwrap_or(range),
+                range: d.highlighting_source().file_syntax(db).text_range(),
                 message: d.message(),
                 severity: Severity::Error,
                 fix,

From ee1586c1ed058ff0f090b552d52fe6bbe2dd7f7f Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 22:46:25 +0300
Subject: [PATCH 54/76] Better naming

---
 crates/ra_hir/src/semantics.rs          | 11 +++++
 crates/ra_hir_def/src/diagnostics.rs    |  2 +-
 crates/ra_hir_expand/src/diagnostics.rs |  6 +--
 crates/ra_hir_ty/src/diagnostics.rs     | 57 +++++++++++--------------
 crates/ra_ide/src/diagnostics.rs        | 31 ++++++++------
 5 files changed, 58 insertions(+), 49 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index e392130ab6d..1c5dc3d5102 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -145,6 +145,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.original_range(node)
     }
 
+    pub fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        self.imp.diagnostics_fix_range(diagnostics)
+    }
+
     pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
         self.imp.diagnostics_range(diagnostics)
     }
@@ -376,6 +380,13 @@ impl<'db> SemanticsImpl<'db> {
         original_range(self.db, node.as_ref())
     }
 
+    fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        let src = diagnostics.fix_source();
+        let root = self.db.parse_or_expand(src.file_id).unwrap();
+        let node = src.value.to_node(&root);
+        original_range(self.db, src.with_value(&node))
+    }
+
     fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
         let src = diagnostics.source();
         let root = self.db.parse_or_expand(src.file_id).unwrap();
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 30db48f8682..e532695895e 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -18,7 +18,7 @@ impl Diagnostic for UnresolvedModule {
     fn message(&self) -> String {
         "unresolved module".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.decl.clone().into())
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index ffeca5e8272..074a8c45e85 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -22,9 +22,9 @@ use crate::{db::AstDatabase, InFile};
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
-    fn source(&self) -> InFile<SyntaxNodePtr>;
-    fn highlighting_source(&self) -> InFile<SyntaxNodePtr> {
-        self.source()
+    fn fix_source(&self) -> InFile<SyntaxNodePtr>;
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        self.fix_source()
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 73d2414343d..a4cede81dc7 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -37,7 +37,7 @@ impl Diagnostic for NoSuchField {
         "no such field".to_string()
     }
 
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.field.clone().into())
     }
 
@@ -50,9 +50,8 @@ impl AstDiagnostic for NoSuchField {
     type AST = ast::RecordExprField;
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.source().file_id).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::RecordExprField::cast(node).unwrap()
+        let root = db.parse_or_expand(self.file).unwrap();
+        self.field.to_node(&root)
     }
 }
 
@@ -72,20 +71,20 @@ impl Diagnostic for MissingFields {
         }
         buf
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.field_list.clone().into() }
     }
 
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        self.list_parent_path
+            .clone()
+            .map(|path| InFile { file_id: self.file, value: path.into() })
+            .unwrap_or_else(|| self.fix_source())
+    }
+
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
     }
-
-    fn highlighting_source(&self) -> InFile<SyntaxNodePtr> {
-        self.list_parent_path
-            .clone()
-            .map(|path| InFile { file_id: self.file, value: path.into() })
-            .unwrap_or_else(|| self.source())
-    }
 }
 
 impl AstDiagnostic for MissingFields {
@@ -112,7 +111,7 @@ impl Diagnostic for MissingPatFields {
         }
         buf
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.field_list.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -131,7 +130,7 @@ impl Diagnostic for MissingMatchArms {
     fn message(&self) -> String {
         String::from("Missing match arm")
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.match_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -149,7 +148,7 @@ impl Diagnostic for MissingOkInTailExpr {
     fn message(&self) -> String {
         "wrap return expression in Ok".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -162,8 +161,7 @@ impl AstDiagnostic for MissingOkInTailExpr {
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
         let root = db.parse_or_expand(self.file).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::Expr::cast(node).unwrap()
+        self.expr.to_node(&root)
     }
 }
 
@@ -177,7 +175,7 @@ impl Diagnostic for BreakOutsideOfLoop {
     fn message(&self) -> String {
         "break outside of loop".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -190,8 +188,7 @@ impl AstDiagnostic for BreakOutsideOfLoop {
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
         let root = db.parse_or_expand(self.file).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::Expr::cast(node).unwrap()
+        self.expr.to_node(&root)
     }
 }
 
@@ -205,7 +202,7 @@ impl Diagnostic for MissingUnsafe {
     fn message(&self) -> String {
         format!("This operation is unsafe and requires an unsafe function or block")
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -217,9 +214,8 @@ impl AstDiagnostic for MissingUnsafe {
     type AST = ast::Expr;
 
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.source().file_id).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::Expr::cast(node).unwrap()
+        let root = db.parse_or_expand(self.file).unwrap();
+        self.expr.to_node(&root)
     }
 }
 
@@ -236,7 +232,7 @@ impl Diagnostic for MismatchedArgCount {
         let s = if self.expected == 1 { "" } else { "s" };
         format!("Expected {} argument{}, found {}", self.expected, s, self.found)
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.call_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -250,7 +246,7 @@ impl Diagnostic for MismatchedArgCount {
 impl AstDiagnostic for MismatchedArgCount {
     type AST = ast::CallExpr;
     fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.source().file_id).unwrap();
+        let root = db.parse_or_expand(self.file).unwrap();
         let node = self.source().value.to_node(&root);
         ast::CallExpr::cast(node).unwrap()
     }
@@ -308,12 +304,11 @@ mod tests {
         let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
         db.diagnostics(|d| {
             // FXIME: macros...
-            let file_id = d.source().file_id.original_file(&db);
-            let highlighting_source = d.highlighting_source();
-            let node = db.parse_or_expand(highlighting_source.file_id).unwrap();
-            let range = highlighting_source.value.to_node(&node).text_range();
+            let source = d.source();
+            let root = db.parse_or_expand(source.file_id).unwrap();
+            let range = source.value.to_node(&root).text_range();
             let message = d.message().to_owned();
-            actual.entry(file_id).or_default().push((range, message));
+            actual.entry(source.file_id.original_file(&db)).or_default().push((range, message));
         });
 
         for (file_id, diags) in actual.iter_mut() {
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index e847df6ea9c..0d2ff17e1f1 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -54,18 +54,19 @@ pub(crate) fn diagnostics(
     let res = RefCell::new(res);
     let mut sink = DiagnosticSinkBuilder::new()
         .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
-            let original_file = d.source().file_id.original_file(db);
             let fix = Fix::new(
                 "Create module",
-                FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }
-                    .into(),
+                FileSystemEdit::CreateFile {
+                    anchor: d.file.original_file(db),
+                    dst: d.candidate.clone(),
+                }
+                .into(),
             );
-            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range,
+                range: sema.diagnostics_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((fix, range)),
+                fix: Some((fix, sema.diagnostics_fix_range(d).range)),
             })
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
@@ -94,12 +95,12 @@ pub(crate) fn diagnostics(
                 };
                 Some((
                     Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
-                    sema.diagnostics_range(d).range,
+                    sema.diagnostics_fix_range(d).range,
                 ))
             };
 
             res.borrow_mut().push(Diagnostic {
-                range: d.highlighting_source().file_syntax(db).text_range(),
+                range: sema.diagnostics_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
                 fix,
@@ -110,21 +111,23 @@ pub(crate) fn diagnostics(
             let replacement = format!("Ok({})", node.syntax());
             let edit = TextEdit::replace(node.syntax().text_range(), replacement);
             let source_change = SourceFileEdit { file_id, edit }.into();
-            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range,
+                range: sema.diagnostics_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((Fix::new("Wrap with ok", source_change), range)),
+                fix: Some((
+                    Fix::new("Wrap with ok", source_change),
+                    sema.diagnostics_fix_range(d).range,
+                )),
             })
         })
         .on::<hir::diagnostics::NoSuchField, _>(|d| {
-            let range = sema.diagnostics_range(d).range;
             res.borrow_mut().push(Diagnostic {
-                range,
+                range: sema.diagnostics_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: missing_struct_field_fix(&sema, file_id, d).map(|fix| (fix, range)),
+                fix: missing_struct_field_fix(&sema, file_id, d)
+                    .map(|fix| (fix, sema.diagnostics_fix_range(d).range)),
             })
         })
         // Only collect experimental diagnostics when they're enabled.

From cb0b13a583c0c20b57fd3529e2c01ab42bd8f04d Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 23:32:16 +0300
Subject: [PATCH 55/76] Fix another missing fields diagnostics

---
 crates/ra_hir_ty/src/diagnostics.rs           | 50 ++++---------------
 crates/ra_hir_ty/src/diagnostics/expr.rs      |  5 +-
 .../ra_hir_ty/src/diagnostics/match_check.rs  |  8 +--
 3 files changed, 19 insertions(+), 44 deletions(-)

diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index a4cede81dc7..48b578fb0ee 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -59,8 +59,8 @@ impl AstDiagnostic for NoSuchField {
 pub struct MissingFields {
     pub file: HirFileId,
     pub field_list: AstPtr<ast::RecordExprFieldList>,
+    pub field_list_parent_path: Option<AstPtr<ast::Path>>,
     pub missed_fields: Vec<Name>,
-    pub list_parent_path: Option<AstPtr<ast::Path>>,
 }
 
 impl Diagnostic for MissingFields {
@@ -76,7 +76,7 @@ impl Diagnostic for MissingFields {
     }
 
     fn source(&self) -> InFile<SyntaxNodePtr> {
-        self.list_parent_path
+        self.field_list_parent_path
             .clone()
             .map(|path| InFile { file_id: self.file, value: path.into() })
             .unwrap_or_else(|| self.fix_source())
@@ -100,6 +100,7 @@ impl AstDiagnostic for MissingFields {
 pub struct MissingPatFields {
     pub file: HirFileId,
     pub field_list: AstPtr<ast::RecordPatFieldList>,
+    pub field_list_parent_path: Option<AstPtr<ast::Path>>,
     pub missed_fields: Vec<Name>,
 }
 
@@ -114,6 +115,12 @@ impl Diagnostic for MissingPatFields {
     fn fix_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.field_list.clone().into() }
     }
+    fn source(&self) -> InFile<SyntaxNodePtr> {
+        self.field_list_parent_path
+            .clone()
+            .map(|path| InFile { file_id: self.file, value: path.into() })
+            .unwrap_or_else(|| self.fix_source())
+    }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
     }
@@ -326,41 +333,6 @@ mod tests {
         assert_eq!(annotations, actual);
     }
 
-    #[test]
-    fn structure_name_highlighted_for_missing_fields() {
-        check_diagnostics(
-            r#"
-struct Beefy {
-    one: i32,
-    two: i32,
-    three: i32,
-    four: i32,
-    five: i32,
-    six: i32,
-    seven: i32,
-    eight: i32,
-    nine: i32,
-    ten: i32,
-}
-fn baz() {
-    let zz = Beefy {
-           //^^^^^ Missing structure fields:
-           //    |    - seven
-        one: (),
-        two: (),
-        three: (),
-        four: (),
-        five: (),
-        six: (),
-        eight: (),
-        nine: (),
-        ten: (),
-    };
-}
-"#,
-        );
-    }
-
     #[test]
     fn no_such_field_diagnostics() {
         check_diagnostics(
@@ -491,8 +463,8 @@ impl Foo {
 struct S { foo: i32, bar: () }
 fn baz(s: S) {
     let S { foo: _ } = s;
-        //^^^^^^^^^^ Missing structure fields:
-        //         | - bar
+      //^ Missing structure fields:
+      //| - bar
 }
 "#,
         );
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs
index 3c37fc58e92..98959ab684a 100644
--- a/crates/ra_hir_ty/src/diagnostics/expr.rs
+++ b/crates/ra_hir_ty/src/diagnostics/expr.rs
@@ -110,8 +110,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                     self.sink.push(MissingFields {
                         file: source_ptr.file_id,
                         field_list: AstPtr::new(&field_list),
+                        field_list_parent_path: record_lit.path().map(|path| AstPtr::new(&path)),
                         missed_fields,
-                        list_parent_path: record_lit.path().map(|path| AstPtr::new(&path)),
                     })
                 }
             }
@@ -141,6 +141,9 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                         self.sink.push(MissingPatFields {
                             file: source_ptr.file_id,
                             field_list: AstPtr::new(&field_list),
+                            field_list_parent_path: record_pat
+                                .path()
+                                .map(|path| AstPtr::new(&path)),
                             missed_fields,
                         })
                     }
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs
index 507edcb7def..deca244dbba 100644
--- a/crates/ra_hir_ty/src/diagnostics/match_check.rs
+++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs
@@ -1161,15 +1161,15 @@ fn main() {
         //^ Missing match arm
     match a {
         Either::A { } => (),
-                //^^^ Missing structure fields:
-                //  | - foo
+      //^^^^^^^^^ Missing structure fields:
+      //        | - foo
         Either::B => (),
     }
     match a {
         //^ Missing match arm
         Either::A { } => (),
-    }           //^^^ Missing structure fields:
-                //  | - foo
+    } //^^^^^^^^^ Missing structure fields:
+      //        | - foo
 
     match a {
         Either::A { foo: true } => (),

From 21184a1b2a4bea57a7666432749b171414136c60 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 27 Jul 2020 23:56:57 +0300
Subject: [PATCH 56/76] Restore accidentally removed public method

---
 crates/ra_hir_expand/src/diagnostics.rs |  7 ++++++-
 crates/ra_hir_ty/src/diagnostics.rs     | 14 +++++---------
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 074a8c45e85..23f28a7f71d 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -16,7 +16,7 @@
 
 use std::{any::Any, fmt};
 
-use ra_syntax::SyntaxNodePtr;
+use ra_syntax::{SyntaxNode, SyntaxNodePtr};
 
 use crate::{db::AstDatabase, InFile};
 
@@ -38,6 +38,11 @@ pub trait AstDiagnostic {
 }
 
 impl dyn Diagnostic {
+    pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
+        let node = db.parse_or_expand(self.source().file_id).unwrap();
+        self.source().value.to_node(&node)
+    }
+
     pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
         self.as_any().downcast_ref()
     }
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 48b578fb0ee..9d29f307166 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -262,10 +262,7 @@ impl AstDiagnostic for MismatchedArgCount {
 #[cfg(test)]
 mod tests {
     use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
-    use hir_expand::{
-        db::AstDatabase,
-        diagnostics::{Diagnostic, DiagnosticSinkBuilder},
-    };
+    use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
     use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
     use ra_syntax::{TextRange, TextSize};
     use rustc_hash::FxHashMap;
@@ -310,12 +307,11 @@ mod tests {
 
         let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
         db.diagnostics(|d| {
-            // FXIME: macros...
-            let source = d.source();
-            let root = db.parse_or_expand(source.file_id).unwrap();
-            let range = source.value.to_node(&root).text_range();
+            // FIXME: macros...
+            let file_id = d.source().file_id.original_file(&db);
+            let range = d.syntax_node(&db).text_range();
             let message = d.message().to_owned();
-            actual.entry(source.file_id.original_file(&db)).or_default().push((range, message));
+            actual.entry(file_id).or_default().push((range, message));
         });
 
         for (file_id, diags) in actual.iter_mut() {

From cfbbd91a886e2394e7411f9d7f4966dcbd454764 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 28 Jul 2020 10:24:59 +0300
Subject: [PATCH 57/76] Require source implementations for Diagnostic

---
 crates/ra_hir_def/src/diagnostics.rs    |  2 +-
 crates/ra_hir_expand/src/diagnostics.rs | 13 ++++++++-----
 crates/ra_hir_ty/src/diagnostics.rs     | 12 ++++++------
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index e532695895e..30db48f8682 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -18,7 +18,7 @@ impl Diagnostic for UnresolvedModule {
     fn message(&self) -> String {
         "unresolved module".to_string()
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.decl.clone().into())
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 23f28a7f71d..90a3b87f96a 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -22,9 +22,11 @@ use crate::{db::AstDatabase, InFile};
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
-    fn fix_source(&self) -> InFile<SyntaxNodePtr>;
-    fn source(&self) -> InFile<SyntaxNodePtr> {
-        self.fix_source()
+    /// A source to be used in highlighting and other visual representations
+    fn source(&self) -> InFile<SyntaxNodePtr>;
+    /// A source to be used during the fix application
+    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+        self.source()
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
@@ -39,8 +41,9 @@ pub trait AstDiagnostic {
 
 impl dyn Diagnostic {
     pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
-        let node = db.parse_or_expand(self.source().file_id).unwrap();
-        self.source().value.to_node(&node)
+        let source = self.source();
+        let node = db.parse_or_expand(source.file_id).unwrap();
+        source.value.to_node(&node)
     }
 
     pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 9d29f307166..efca0961995 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -37,7 +37,7 @@ impl Diagnostic for NoSuchField {
         "no such field".to_string()
     }
 
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.field.clone().into())
     }
 
@@ -137,7 +137,7 @@ impl Diagnostic for MissingMatchArms {
     fn message(&self) -> String {
         String::from("Missing match arm")
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.match_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -155,7 +155,7 @@ impl Diagnostic for MissingOkInTailExpr {
     fn message(&self) -> String {
         "wrap return expression in Ok".to_string()
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -182,7 +182,7 @@ impl Diagnostic for BreakOutsideOfLoop {
     fn message(&self) -> String {
         "break outside of loop".to_string()
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -209,7 +209,7 @@ impl Diagnostic for MissingUnsafe {
     fn message(&self) -> String {
         format!("This operation is unsafe and requires an unsafe function or block")
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -239,7 +239,7 @@ impl Diagnostic for MismatchedArgCount {
         let s = if self.expected == 1 { "" } else { "s" };
         format!("Expected {} argument{}, found {}", self.expected, s, self.found)
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
+    fn source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.call_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {

From 9963f43d51071ea02f8f6d490b9c49882034b42c Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Sun, 9 Aug 2020 01:59:26 +0300
Subject: [PATCH 58/76] Refactor the diagnostics

---
 crates/ra_hir/src/semantics.rs           | 28 +++----
 crates/ra_hir_def/src/diagnostics.rs     | 12 ++-
 crates/ra_hir_expand/src/diagnostics.rs  | 17 +----
 crates/ra_hir_ty/src/diagnostics.rs      | 97 ++++++++++--------------
 crates/ra_hir_ty/src/diagnostics/expr.rs | 12 +--
 crates/ra_ide/src/diagnostics.rs         | 76 ++++++++++---------
 6 files changed, 106 insertions(+), 136 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 1c5dc3d5102..b4420d3785f 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -109,11 +109,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.parse(file_id)
     }
 
-    pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST {
-        let file_id = d.source().file_id;
+    pub fn diagnostic_fix_source<T: AstDiagnostic + Diagnostic>(
+        &self,
+        d: &T,
+    ) -> <T as AstDiagnostic>::AST {
+        let file_id = d.presentation().file_id;
         let root = self.db.parse_or_expand(file_id).unwrap();
         self.imp.cache(root, file_id);
-        d.ast(self.db.upcast())
+        d.fix_source(self.db.upcast())
     }
 
     pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
@@ -145,12 +148,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.original_range(node)
     }
 
-    pub fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        self.imp.diagnostics_fix_range(diagnostics)
-    }
-
-    pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        self.imp.diagnostics_range(diagnostics)
+    pub fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        self.imp.diagnostics_presentation_range(diagnostics)
     }
 
     pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
@@ -380,15 +379,8 @@ impl<'db> SemanticsImpl<'db> {
         original_range(self.db, node.as_ref())
     }
 
-    fn diagnostics_fix_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        let src = diagnostics.fix_source();
-        let root = self.db.parse_or_expand(src.file_id).unwrap();
-        let node = src.value.to_node(&root);
-        original_range(self.db, src.with_value(&node))
-    }
-
-    fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        let src = diagnostics.source();
+    fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        let src = diagnostics.presentation();
         let root = self.db.parse_or_expand(src.file_id).unwrap();
         let node = src.value.to_node(&root);
         original_range(self.db, src.with_value(&node))
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 30db48f8682..be961284655 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -2,7 +2,7 @@
 
 use std::any::Any;
 
-use hir_expand::diagnostics::Diagnostic;
+use hir_expand::diagnostics::{AstDiagnostic, Diagnostic};
 use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
 
 use hir_expand::{HirFileId, InFile};
@@ -18,10 +18,18 @@ impl Diagnostic for UnresolvedModule {
     fn message(&self) -> String {
         "unresolved module".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.decl.clone().into())
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
     }
 }
+
+impl AstDiagnostic for UnresolvedModule {
+    type AST = ast::Module;
+    fn fix_source(&self, db: &dyn hir_expand::db::AstDatabase) -> Self::AST {
+        let root = db.parse_or_expand(self.file).unwrap();
+        self.decl.to_node(&root)
+    }
+}
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 90a3b87f96a..2b74473cec9 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -16,18 +16,13 @@
 
 use std::{any::Any, fmt};
 
-use ra_syntax::{SyntaxNode, SyntaxNodePtr};
+use ra_syntax::SyntaxNodePtr;
 
 use crate::{db::AstDatabase, InFile};
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
-    /// A source to be used in highlighting and other visual representations
-    fn source(&self) -> InFile<SyntaxNodePtr>;
-    /// A source to be used during the fix application
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
-        self.source()
-    }
+    fn presentation(&self) -> InFile<SyntaxNodePtr>;
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
         false
@@ -36,16 +31,10 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
 
 pub trait AstDiagnostic {
     type AST;
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST;
+    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST;
 }
 
 impl dyn Diagnostic {
-    pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode {
-        let source = self.source();
-        let node = db.parse_or_expand(source.file_id).unwrap();
-        source.value.to_node(&node)
-    }
-
     pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
         self.as_any().downcast_ref()
     }
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index efca0961995..1e3a446375b 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -9,7 +9,7 @@ use hir_def::DefWithBodyId;
 use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
 use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
 use ra_prof::profile;
-use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
+use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
 use stdx::format_to;
 
 use crate::db::HirDatabase;
@@ -37,7 +37,7 @@ impl Diagnostic for NoSuchField {
         "no such field".to_string()
     }
 
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.field.clone().into())
     }
 
@@ -49,7 +49,7 @@ impl Diagnostic for NoSuchField {
 impl AstDiagnostic for NoSuchField {
     type AST = ast::RecordExprField;
 
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
+    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
         let root = db.parse_or_expand(self.file).unwrap();
         self.field.to_node(&root)
     }
@@ -58,7 +58,7 @@ impl AstDiagnostic for NoSuchField {
 #[derive(Debug)]
 pub struct MissingFields {
     pub file: HirFileId,
-    pub field_list: AstPtr<ast::RecordExprFieldList>,
+    pub field_list_parent: AstPtr<ast::RecordExpr>,
     pub field_list_parent_path: Option<AstPtr<ast::Path>>,
     pub missed_fields: Vec<Name>,
 }
@@ -71,15 +71,16 @@ impl Diagnostic for MissingFields {
         }
         buf
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
-        InFile { file_id: self.file, value: self.field_list.clone().into() }
-    }
 
-    fn source(&self) -> InFile<SyntaxNodePtr> {
-        self.field_list_parent_path
-            .clone()
-            .map(|path| InFile { file_id: self.file, value: path.into() })
-            .unwrap_or_else(|| self.fix_source())
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+        InFile {
+            file_id: self.file,
+            value: self
+                .field_list_parent_path
+                .clone()
+                .map(SyntaxNodePtr::from)
+                .unwrap_or_else(|| self.field_list_parent.clone().into()),
+        }
     }
 
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -88,18 +89,18 @@ impl Diagnostic for MissingFields {
 }
 
 impl AstDiagnostic for MissingFields {
-    type AST = ast::RecordExprFieldList;
+    type AST = ast::RecordExpr;
 
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
+    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
         let root = db.parse_or_expand(self.file).unwrap();
-        self.field_list.to_node(&root)
+        self.field_list_parent.to_node(&root)
     }
 }
 
 #[derive(Debug)]
 pub struct MissingPatFields {
     pub file: HirFileId,
-    pub field_list: AstPtr<ast::RecordPatFieldList>,
+    pub field_list_parent: AstPtr<ast::RecordPat>,
     pub field_list_parent_path: Option<AstPtr<ast::Path>>,
     pub missed_fields: Vec<Name>,
 }
@@ -112,14 +113,13 @@ impl Diagnostic for MissingPatFields {
         }
         buf
     }
-    fn fix_source(&self) -> InFile<SyntaxNodePtr> {
-        InFile { file_id: self.file, value: self.field_list.clone().into() }
-    }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
-        self.field_list_parent_path
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+        let value = self
+            .field_list_parent_path
             .clone()
-            .map(|path| InFile { file_id: self.file, value: path.into() })
-            .unwrap_or_else(|| self.fix_source())
+            .map(SyntaxNodePtr::from)
+            .unwrap_or_else(|| self.field_list_parent.clone().into());
+        InFile { file_id: self.file, value }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
@@ -137,7 +137,7 @@ impl Diagnostic for MissingMatchArms {
     fn message(&self) -> String {
         String::from("Missing match arm")
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.match_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -155,7 +155,7 @@ impl Diagnostic for MissingOkInTailExpr {
     fn message(&self) -> String {
         "wrap return expression in Ok".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -166,7 +166,7 @@ impl Diagnostic for MissingOkInTailExpr {
 impl AstDiagnostic for MissingOkInTailExpr {
     type AST = ast::Expr;
 
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
+    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
         let root = db.parse_or_expand(self.file).unwrap();
         self.expr.to_node(&root)
     }
@@ -182,7 +182,7 @@ impl Diagnostic for BreakOutsideOfLoop {
     fn message(&self) -> String {
         "break outside of loop".to_string()
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -190,15 +190,6 @@ impl Diagnostic for BreakOutsideOfLoop {
     }
 }
 
-impl AstDiagnostic for BreakOutsideOfLoop {
-    type AST = ast::Expr;
-
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.expr.to_node(&root)
-    }
-}
-
 #[derive(Debug)]
 pub struct MissingUnsafe {
     pub file: HirFileId,
@@ -209,7 +200,7 @@ impl Diagnostic for MissingUnsafe {
     fn message(&self) -> String {
         format!("This operation is unsafe and requires an unsafe function or block")
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -217,15 +208,6 @@ impl Diagnostic for MissingUnsafe {
     }
 }
 
-impl AstDiagnostic for MissingUnsafe {
-    type AST = ast::Expr;
-
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.expr.to_node(&root)
-    }
-}
-
 #[derive(Debug)]
 pub struct MismatchedArgCount {
     pub file: HirFileId,
@@ -239,7 +221,7 @@ impl Diagnostic for MismatchedArgCount {
         let s = if self.expected == 1 { "" } else { "s" };
         format!("Expected {} argument{}, found {}", self.expected, s, self.found)
     }
-    fn source(&self) -> InFile<SyntaxNodePtr> {
+    fn presentation(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.call_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -250,19 +232,13 @@ impl Diagnostic for MismatchedArgCount {
     }
 }
 
-impl AstDiagnostic for MismatchedArgCount {
-    type AST = ast::CallExpr;
-    fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        let node = self.source().value.to_node(&root);
-        ast::CallExpr::cast(node).unwrap()
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
-    use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder};
+    use hir_expand::{
+        db::AstDatabase,
+        diagnostics::{Diagnostic, DiagnosticSinkBuilder},
+    };
     use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
     use ra_syntax::{TextRange, TextSize};
     use rustc_hash::FxHashMap;
@@ -308,8 +284,11 @@ mod tests {
         let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
         db.diagnostics(|d| {
             // FIXME: macros...
-            let file_id = d.source().file_id.original_file(&db);
-            let range = d.syntax_node(&db).text_range();
+            let diagnostics_presentation = d.presentation();
+            let root = db.parse_or_expand(diagnostics_presentation.file_id).unwrap();
+
+            let file_id = diagnostics_presentation.file_id.original_file(&db);
+            let range = diagnostics_presentation.value.to_node(&root).text_range();
             let message = d.message().to_owned();
             actual.entry(file_id).or_default().push((range, message));
         });
diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs
index 98959ab684a..51adcecafae 100644
--- a/crates/ra_hir_ty/src/diagnostics/expr.rs
+++ b/crates/ra_hir_ty/src/diagnostics/expr.rs
@@ -100,8 +100,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
 
         if let Ok(source_ptr) = source_map.expr_syntax(id) {
             let root = source_ptr.file_syntax(db.upcast());
-            if let ast::Expr::RecordExpr(record_lit) = &source_ptr.value.to_node(&root) {
-                if let Some(field_list) = record_lit.record_expr_field_list() {
+            if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
+                if let Some(_) = record_expr.record_expr_field_list() {
                     let variant_data = variant_data(db.upcast(), variant_def);
                     let missed_fields = missed_fields
                         .into_iter()
@@ -109,8 +109,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                         .collect();
                     self.sink.push(MissingFields {
                         file: source_ptr.file_id,
-                        field_list: AstPtr::new(&field_list),
-                        field_list_parent_path: record_lit.path().map(|path| AstPtr::new(&path)),
+                        field_list_parent: AstPtr::new(&record_expr),
+                        field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)),
                         missed_fields,
                     })
                 }
@@ -132,7 +132,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             if let Some(expr) = source_ptr.value.as_ref().left() {
                 let root = source_ptr.file_syntax(db.upcast());
                 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
-                    if let Some(field_list) = record_pat.record_pat_field_list() {
+                    if let Some(_) = record_pat.record_pat_field_list() {
                         let variant_data = variant_data(db.upcast(), variant_def);
                         let missed_fields = missed_fields
                             .into_iter()
@@ -140,7 +140,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
                             .collect();
                         self.sink.push(MissingPatFields {
                             file: source_ptr.file_id,
-                            field_list: AstPtr::new(&field_list),
+                            field_list_parent: AstPtr::new(&record_pat),
                             field_list_parent_path: record_pat
                                 .path()
                                 .map(|path| AstPtr::new(&path)),
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 0d2ff17e1f1..55593a8cb80 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,7 +7,7 @@
 use std::cell::RefCell;
 
 use hir::{
-    diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder},
+    diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
     HasSource, HirDisplay, Semantics, VariantDef,
 };
 use itertools::Itertools;
@@ -63,10 +63,10 @@ pub(crate) fn diagnostics(
                 .into(),
             );
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((fix, sema.diagnostics_fix_range(d).range)),
+                fix: Some((fix, sema.diagnostic_fix_source(d).syntax().text_range())),
             })
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
@@ -78,56 +78,58 @@ pub(crate) fn diagnostics(
             let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
                 None
             } else {
-                let mut field_list = d.ast(db);
-                for f in d.missed_fields.iter() {
-                    let field = make::record_expr_field(
-                        make::name_ref(&f.to_string()),
-                        Some(make::expr_unit()),
-                    );
-                    field_list = field_list.append_field(&field);
-                }
+                let record_expr = sema.diagnostic_fix_source(d);
+                if let Some(old_field_list) = record_expr.record_expr_field_list() {
+                    let mut new_field_list = old_field_list.clone();
+                    for f in d.missed_fields.iter() {
+                        let field = make::record_expr_field(
+                            make::name_ref(&f.to_string()),
+                            Some(make::expr_unit()),
+                        );
+                        new_field_list = new_field_list.append_field(&field);
+                    }
 
-                let edit = {
-                    let mut builder = TextEditBuilder::default();
-                    algo::diff(&d.ast(db).syntax(), &field_list.syntax())
-                        .into_text_edit(&mut builder);
-                    builder.finish()
-                };
-                Some((
-                    Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
-                    sema.diagnostics_fix_range(d).range,
-                ))
+                    let edit = {
+                        let mut builder = TextEditBuilder::default();
+                        algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
+                            .into_text_edit(&mut builder);
+                        builder.finish()
+                    };
+                    Some((
+                        Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
+                        sema.original_range(&old_field_list.syntax()).range,
+                    ))
+                } else {
+                    None
+                }
             };
 
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
                 fix,
             })
         })
         .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
-            let node = d.ast(db);
-            let replacement = format!("Ok({})", node.syntax());
-            let edit = TextEdit::replace(node.syntax().text_range(), replacement);
+            let tail_expr = sema.diagnostic_fix_source(d);
+            let tail_expr_range = tail_expr.syntax().text_range();
+            let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
             let source_change = SourceFileEdit { file_id, edit }.into();
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((
-                    Fix::new("Wrap with ok", source_change),
-                    sema.diagnostics_fix_range(d).range,
-                )),
+                fix: Some((Fix::new("Wrap with ok", source_change), tail_expr_range)),
             })
         })
         .on::<hir::diagnostics::NoSuchField, _>(|d| {
             res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_range(d).range,
+                range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
                 fix: missing_struct_field_fix(&sema, file_id, d)
-                    .map(|fix| (fix, sema.diagnostics_fix_range(d).range)),
+                    .map(|fix| (fix, sema.diagnostic_fix_source(d).syntax().text_range())),
             })
         })
         // Only collect experimental diagnostics when they're enabled.
@@ -136,7 +138,7 @@ pub(crate) fn diagnostics(
         .build(|d| {
             res.borrow_mut().push(Diagnostic {
                 message: d.message(),
-                range: sema.diagnostics_range(d).range,
+                range: sema.diagnostics_presentation_range(d).range,
                 severity: Severity::Error,
                 fix: None,
             })
@@ -154,9 +156,9 @@ fn missing_struct_field_fix(
     usage_file_id: FileId,
     d: &hir::diagnostics::NoSuchField,
 ) -> Option<Fix> {
-    let record_expr = sema.ast(d);
+    let record_expr_field = sema.diagnostic_fix_source(d);
 
-    let record_lit = ast::RecordExpr::cast(record_expr.syntax().parent()?.parent()?)?;
+    let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
     let def_id = sema.resolve_variant(record_lit)?;
     let module;
     let def_file_id;
@@ -184,12 +186,12 @@ fn missing_struct_field_fix(
     };
     let def_file_id = def_file_id.original_file(sema.db);
 
-    let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
+    let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
     if new_field_type.is_unknown() {
         return None;
     }
     let new_field = make::record_field(
-        record_expr.field_name()?,
+        record_expr_field.field_name()?,
         make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
     );
 

From 936861993935d5b2c78b953e2f4b719e1992bd73 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Mon, 10 Aug 2020 22:53:10 +0300
Subject: [PATCH 59/76] Make the fix AST source Optional

---
 crates/ra_hir/src/diagnostics.rs        |  2 +-
 crates/ra_hir/src/semantics.rs          |  8 +--
 crates/ra_hir_def/src/diagnostics.rs    | 10 +--
 crates/ra_hir_expand/src/diagnostics.rs | 19 ++----
 crates/ra_hir_ty/src/diagnostics.rs     | 26 ++++----
 crates/ra_ide/src/diagnostics.rs        | 85 ++++++++++++++-----------
 6 files changed, 77 insertions(+), 73 deletions(-)

diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index 266b513dcf5..564f6a5db28 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -1,7 +1,7 @@
 //! FIXME: write short doc here
 pub use hir_def::diagnostics::UnresolvedModule;
 pub use hir_expand::diagnostics::{
-    AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder,
+    Diagnostic, DiagnosticSink, DiagnosticSinkBuilder, DiagnosticWithFix,
 };
 pub use hir_ty::diagnostics::{
     MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index b4420d3785f..c5bc2baffee 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -8,7 +8,7 @@ use hir_def::{
     resolver::{self, HasResolver, Resolver},
     AsMacroCall, FunctionId, TraitId, VariantId,
 };
-use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, name::AsName, ExpansionInfo};
+use hir_expand::{diagnostics::DiagnosticWithFix, hygiene::Hygiene, name::AsName, ExpansionInfo};
 use hir_ty::associated_type_shorthand_candidates;
 use itertools::Itertools;
 use ra_db::{FileId, FileRange};
@@ -109,12 +109,12 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.parse(file_id)
     }
 
-    pub fn diagnostic_fix_source<T: AstDiagnostic + Diagnostic>(
+    pub fn diagnostic_fix_source<T: DiagnosticWithFix + Diagnostic>(
         &self,
         d: &T,
-    ) -> <T as AstDiagnostic>::AST {
+    ) -> Option<<T as DiagnosticWithFix>::AST> {
         let file_id = d.presentation().file_id;
-        let root = self.db.parse_or_expand(file_id).unwrap();
+        let root = self.db.parse_or_expand(file_id)?;
         self.imp.cache(root, file_id);
         d.fix_source(self.db.upcast())
     }
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index be961284655..033be683c47 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -2,7 +2,7 @@
 
 use std::any::Any;
 
-use hir_expand::diagnostics::{AstDiagnostic, Diagnostic};
+use hir_expand::diagnostics::{Diagnostic, DiagnosticWithFix};
 use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
 
 use hir_expand::{HirFileId, InFile};
@@ -26,10 +26,10 @@ impl Diagnostic for UnresolvedModule {
     }
 }
 
-impl AstDiagnostic for UnresolvedModule {
+impl DiagnosticWithFix for UnresolvedModule {
     type AST = ast::Module;
-    fn fix_source(&self, db: &dyn hir_expand::db::AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.decl.to_node(&root)
+    fn fix_source(&self, db: &dyn hir_expand::db::AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.decl.to_node(&root))
     }
 }
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 2b74473cec9..62a09a73ae5 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -29,15 +29,9 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     }
 }
 
-pub trait AstDiagnostic {
+pub trait DiagnosticWithFix {
     type AST;
-    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST;
-}
-
-impl dyn Diagnostic {
-    pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
-        self.as_any().downcast_ref()
-    }
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST>;
 }
 
 pub struct DiagnosticSink<'a> {
@@ -83,12 +77,9 @@ impl<'a> DiagnosticSinkBuilder<'a> {
         self
     }
 
-    pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
-        let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::<D>() {
-            Some(d) => {
-                cb(d);
-                Ok(())
-            }
+    pub fn on<D: Diagnostic, F: FnMut(&D) -> Option<()> + 'a>(mut self, mut cb: F) -> Self {
+        let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
+            Some(d) => cb(d).ok_or(()),
             None => Err(()),
         };
         self.callbacks.push(Box::new(cb));
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 1e3a446375b..b34ba5bfc82 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -6,7 +6,7 @@ mod unsafe_check;
 use std::any::Any;
 
 use hir_def::DefWithBodyId;
-use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
+use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticWithFix};
 use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
 use ra_prof::profile;
 use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
@@ -46,12 +46,12 @@ impl Diagnostic for NoSuchField {
     }
 }
 
-impl AstDiagnostic for NoSuchField {
+impl DiagnosticWithFix for NoSuchField {
     type AST = ast::RecordExprField;
 
-    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.field.to_node(&root)
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.field.to_node(&root))
     }
 }
 
@@ -88,12 +88,12 @@ impl Diagnostic for MissingFields {
     }
 }
 
-impl AstDiagnostic for MissingFields {
+impl DiagnosticWithFix for MissingFields {
     type AST = ast::RecordExpr;
 
-    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.field_list_parent.to_node(&root)
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.field_list_parent.to_node(&root))
     }
 }
 
@@ -163,12 +163,12 @@ impl Diagnostic for MissingOkInTailExpr {
     }
 }
 
-impl AstDiagnostic for MissingOkInTailExpr {
+impl DiagnosticWithFix for MissingOkInTailExpr {
     type AST = ast::Expr;
 
-    fn fix_source(&self, db: &dyn AstDatabase) -> Self::AST {
-        let root = db.parse_or_expand(self.file).unwrap();
-        self.expr.to_node(&root)
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.expr.to_node(&root))
     }
 }
 
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 55593a8cb80..043ce357b93 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -62,12 +62,18 @@ pub(crate) fn diagnostics(
                 }
                 .into(),
             );
+            let fix = sema
+                .diagnostic_fix_source(d)
+                .map(|unresolved_module| unresolved_module.syntax().text_range())
+                .map(|fix_range| (fix, fix_range));
+
             res.borrow_mut().push(Diagnostic {
                 range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((fix, sema.diagnostic_fix_source(d).syntax().text_range())),
-            })
+                fix,
+            });
+            Some(())
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
             // Note that although we could add a diagnostics to
@@ -78,30 +84,29 @@ pub(crate) fn diagnostics(
             let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
                 None
             } else {
-                let record_expr = sema.diagnostic_fix_source(d);
-                if let Some(old_field_list) = record_expr.record_expr_field_list() {
-                    let mut new_field_list = old_field_list.clone();
-                    for f in d.missed_fields.iter() {
-                        let field = make::record_expr_field(
-                            make::name_ref(&f.to_string()),
-                            Some(make::expr_unit()),
-                        );
-                        new_field_list = new_field_list.append_field(&field);
-                    }
+                sema.diagnostic_fix_source(d)
+                    .and_then(|record_expr| record_expr.record_expr_field_list())
+                    .map(|old_field_list| {
+                        let mut new_field_list = old_field_list.clone();
+                        for f in d.missed_fields.iter() {
+                            let field = make::record_expr_field(
+                                make::name_ref(&f.to_string()),
+                                Some(make::expr_unit()),
+                            );
+                            new_field_list = new_field_list.append_field(&field);
+                        }
 
-                    let edit = {
-                        let mut builder = TextEditBuilder::default();
-                        algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
-                            .into_text_edit(&mut builder);
-                        builder.finish()
-                    };
-                    Some((
-                        Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
-                        sema.original_range(&old_field_list.syntax()).range,
-                    ))
-                } else {
-                    None
-                }
+                        let edit = {
+                            let mut builder = TextEditBuilder::default();
+                            algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
+                                .into_text_edit(&mut builder);
+                            builder.finish()
+                        };
+                        (
+                            Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
+                            sema.original_range(&old_field_list.syntax()).range,
+                        )
+                    })
             };
 
             res.borrow_mut().push(Diagnostic {
@@ -109,28 +114,36 @@ pub(crate) fn diagnostics(
                 message: d.message(),
                 severity: Severity::Error,
                 fix,
-            })
+            });
+            Some(())
         })
         .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
-            let tail_expr = sema.diagnostic_fix_source(d);
-            let tail_expr_range = tail_expr.syntax().text_range();
-            let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
-            let source_change = SourceFileEdit { file_id, edit }.into();
+            let fix = sema.diagnostic_fix_source(d).map(|tail_expr| {
+                let tail_expr_range = tail_expr.syntax().text_range();
+                let edit =
+                    TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
+                let source_change = SourceFileEdit { file_id, edit }.into();
+                (Fix::new("Wrap with ok", source_change), tail_expr_range)
+            });
+
             res.borrow_mut().push(Diagnostic {
                 range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: Some((Fix::new("Wrap with ok", source_change), tail_expr_range)),
-            })
+                fix,
+            });
+            Some(())
         })
         .on::<hir::diagnostics::NoSuchField, _>(|d| {
             res.borrow_mut().push(Diagnostic {
                 range: sema.diagnostics_presentation_range(d).range,
                 message: d.message(),
                 severity: Severity::Error,
-                fix: missing_struct_field_fix(&sema, file_id, d)
-                    .map(|fix| (fix, sema.diagnostic_fix_source(d).syntax().text_range())),
-            })
+                fix: missing_struct_field_fix(&sema, file_id, d).and_then(|fix| {
+                    Some((fix, sema.diagnostic_fix_source(d)?.syntax().text_range()))
+                }),
+            });
+            Some(())
         })
         // Only collect experimental diagnostics when they're enabled.
         .filter(|diag| !diag.is_experimental() || enable_experimental)
@@ -156,7 +169,7 @@ fn missing_struct_field_fix(
     usage_file_id: FileId,
     d: &hir::diagnostics::NoSuchField,
 ) -> Option<Fix> {
-    let record_expr_field = sema.diagnostic_fix_source(d);
+    let record_expr_field = sema.diagnostic_fix_source(d)?;
 
     let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
     let def_id = sema.resolve_variant(record_lit)?;

From 29fbc8e02180aac1f4d7819a9626206aa64028a0 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 00:37:23 +0300
Subject: [PATCH 60/76] Move the DiagnosticsWithFix trait on the ide level

---
 crates/ra_hir/src/diagnostics.rs              |  4 +-
 crates/ra_hir/src/semantics.rs                | 12 ++---
 crates/ra_hir_def/src/diagnostics.rs          | 10 +---
 crates/ra_hir_expand/src/diagnostics.rs       |  7 +--
 crates/ra_hir_ty/src/diagnostics.rs           | 31 +------------
 crates/ra_ide/src/diagnostics.rs              | 28 ++++++++---
 .../src/diagnostics/diagnostics_with_fix.rs   | 46 +++++++++++++++++++
 7 files changed, 75 insertions(+), 63 deletions(-)
 create mode 100644 crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs

diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index 564f6a5db28..363164b9b4a 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -1,8 +1,6 @@
 //! FIXME: write short doc here
 pub use hir_def::diagnostics::UnresolvedModule;
-pub use hir_expand::diagnostics::{
-    Diagnostic, DiagnosticSink, DiagnosticSinkBuilder, DiagnosticWithFix,
-};
+pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
 pub use hir_ty::diagnostics::{
     MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
 };
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index c5bc2baffee..e9f7a033c5c 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -8,7 +8,7 @@ use hir_def::{
     resolver::{self, HasResolver, Resolver},
     AsMacroCall, FunctionId, TraitId, VariantId,
 };
-use hir_expand::{diagnostics::DiagnosticWithFix, hygiene::Hygiene, name::AsName, ExpansionInfo};
+use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
 use hir_ty::associated_type_shorthand_candidates;
 use itertools::Itertools;
 use ra_db::{FileId, FileRange};
@@ -109,14 +109,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.parse(file_id)
     }
 
-    pub fn diagnostic_fix_source<T: DiagnosticWithFix + Diagnostic>(
-        &self,
-        d: &T,
-    ) -> Option<<T as DiagnosticWithFix>::AST> {
-        let file_id = d.presentation().file_id;
-        let root = self.db.parse_or_expand(file_id)?;
-        self.imp.cache(root, file_id);
-        d.fix_source(self.db.upcast())
+    pub fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
+        self.imp.cache(root_node, file_id)
     }
 
     pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 033be683c47..9435c72544b 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -2,7 +2,7 @@
 
 use std::any::Any;
 
-use hir_expand::diagnostics::{Diagnostic, DiagnosticWithFix};
+use hir_expand::diagnostics::Diagnostic;
 use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
 
 use hir_expand::{HirFileId, InFile};
@@ -25,11 +25,3 @@ impl Diagnostic for UnresolvedModule {
         self
     }
 }
-
-impl DiagnosticWithFix for UnresolvedModule {
-    type AST = ast::Module;
-    fn fix_source(&self, db: &dyn hir_expand::db::AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.decl.to_node(&root))
-    }
-}
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 62a09a73ae5..8358c488b8a 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -18,7 +18,7 @@ use std::{any::Any, fmt};
 
 use ra_syntax::SyntaxNodePtr;
 
-use crate::{db::AstDatabase, InFile};
+use crate::InFile;
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
@@ -29,11 +29,6 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     }
 }
 
-pub trait DiagnosticWithFix {
-    type AST;
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST>;
-}
-
 pub struct DiagnosticSink<'a> {
     callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>,
     filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>,
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index b34ba5bfc82..24435e8a71f 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -6,8 +6,8 @@ mod unsafe_check;
 use std::any::Any;
 
 use hir_def::DefWithBodyId;
-use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticWithFix};
-use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
+use hir_expand::diagnostics::{Diagnostic, DiagnosticSink};
+use hir_expand::{name::Name, HirFileId, InFile};
 use ra_prof::profile;
 use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
 use stdx::format_to;
@@ -46,15 +46,6 @@ impl Diagnostic for NoSuchField {
     }
 }
 
-impl DiagnosticWithFix for NoSuchField {
-    type AST = ast::RecordExprField;
-
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.field.to_node(&root))
-    }
-}
-
 #[derive(Debug)]
 pub struct MissingFields {
     pub file: HirFileId,
@@ -88,15 +79,6 @@ impl Diagnostic for MissingFields {
     }
 }
 
-impl DiagnosticWithFix for MissingFields {
-    type AST = ast::RecordExpr;
-
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.field_list_parent.to_node(&root))
-    }
-}
-
 #[derive(Debug)]
 pub struct MissingPatFields {
     pub file: HirFileId,
@@ -163,15 +145,6 @@ impl Diagnostic for MissingOkInTailExpr {
     }
 }
 
-impl DiagnosticWithFix for MissingOkInTailExpr {
-    type AST = ast::Expr;
-
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.expr.to_node(&root))
-    }
-}
-
 #[derive(Debug)]
 pub struct BreakOutsideOfLoop {
     pub file: HirFileId,
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 043ce357b93..ca1a7c1aae6 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,11 +7,12 @@
 use std::cell::RefCell;
 
 use hir::{
+    db::AstDatabase,
     diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
     HasSource, HirDisplay, Semantics, VariantDef,
 };
 use itertools::Itertools;
-use ra_db::SourceDatabase;
+use ra_db::{SourceDatabase, Upcast};
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
 use ra_syntax::{
@@ -23,6 +24,9 @@ use ra_text_edit::{TextEdit, TextEditBuilder};
 
 use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
 
+mod diagnostics_with_fix;
+use diagnostics_with_fix::DiagnosticWithFix;
+
 #[derive(Debug, Copy, Clone)]
 pub enum Severity {
     Error,
@@ -62,8 +66,7 @@ pub(crate) fn diagnostics(
                 }
                 .into(),
             );
-            let fix = sema
-                .diagnostic_fix_source(d)
+            let fix = diagnostic_fix_source(&sema, d)
                 .map(|unresolved_module| unresolved_module.syntax().text_range())
                 .map(|fix_range| (fix, fix_range));
 
@@ -84,7 +87,7 @@ pub(crate) fn diagnostics(
             let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
                 None
             } else {
-                sema.diagnostic_fix_source(d)
+                diagnostic_fix_source(&sema, d)
                     .and_then(|record_expr| record_expr.record_expr_field_list())
                     .map(|old_field_list| {
                         let mut new_field_list = old_field_list.clone();
@@ -105,6 +108,7 @@ pub(crate) fn diagnostics(
                         (
                             Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
                             sema.original_range(&old_field_list.syntax()).range,
+                            // old_field_list.syntax().text_range(),
                         )
                     })
             };
@@ -118,7 +122,7 @@ pub(crate) fn diagnostics(
             Some(())
         })
         .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
-            let fix = sema.diagnostic_fix_source(d).map(|tail_expr| {
+            let fix = diagnostic_fix_source(&sema, d).map(|tail_expr| {
                 let tail_expr_range = tail_expr.syntax().text_range();
                 let edit =
                     TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
@@ -140,7 +144,7 @@ pub(crate) fn diagnostics(
                 message: d.message(),
                 severity: Severity::Error,
                 fix: missing_struct_field_fix(&sema, file_id, d).and_then(|fix| {
-                    Some((fix, sema.diagnostic_fix_source(d)?.syntax().text_range()))
+                    Some((fix, diagnostic_fix_source(&sema, d)?.syntax().text_range()))
                 }),
             });
             Some(())
@@ -164,12 +168,22 @@ pub(crate) fn diagnostics(
     res.into_inner()
 }
 
+fn diagnostic_fix_source<T: DiagnosticWithFix + hir::diagnostics::Diagnostic>(
+    sema: &Semantics<RootDatabase>,
+    d: &T,
+) -> Option<<T as DiagnosticWithFix>::AST> {
+    let file_id = d.presentation().file_id;
+    let root = sema.db.parse_or_expand(file_id)?;
+    sema.cache(root, file_id);
+    d.fix_source(sema.db.upcast())
+}
+
 fn missing_struct_field_fix(
     sema: &Semantics<RootDatabase>,
     usage_file_id: FileId,
     d: &hir::diagnostics::NoSuchField,
 ) -> Option<Fix> {
-    let record_expr_field = sema.diagnostic_fix_source(d)?;
+    let record_expr_field = diagnostic_fix_source(&sema, d)?;
 
     let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
     let def_id = sema.resolve_variant(record_lit)?;
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
new file mode 100644
index 00000000000..8578a90ec06
--- /dev/null
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -0,0 +1,46 @@
+use hir::{
+    db::AstDatabase,
+    diagnostics::{MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
+};
+use ra_syntax::ast;
+
+// TODO kb
+pub trait DiagnosticWithFix {
+    type AST;
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST>;
+}
+
+impl DiagnosticWithFix for UnresolvedModule {
+    type AST = ast::Module;
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.decl.to_node(&root))
+    }
+}
+
+impl DiagnosticWithFix for NoSuchField {
+    type AST = ast::RecordExprField;
+
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.field.to_node(&root))
+    }
+}
+
+impl DiagnosticWithFix for MissingFields {
+    type AST = ast::RecordExpr;
+
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.field_list_parent.to_node(&root))
+    }
+}
+
+impl DiagnosticWithFix for MissingOkInTailExpr {
+    type AST = ast::Expr;
+
+    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
+        let root = db.parse_or_expand(self.file)?;
+        Some(self.expr.to_node(&root))
+    }
+}

From c8cad76d25f7fab856c9646b70122e0f9f7d7218 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 00:55:57 +0300
Subject: [PATCH 61/76] Improve the ide diagnostics trait API

---
 crates/ra_hir/src/semantics.rs                |   5 +-
 crates/ra_hir_expand/src/diagnostics.rs       |   7 +-
 crates/ra_ide/src/diagnostics.rs              | 188 ++----------------
 .../src/diagnostics/diagnostics_with_fix.rs   | 165 +++++++++++++--
 4 files changed, 167 insertions(+), 198 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index e9f7a033c5c..2dfe69039fb 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -109,10 +109,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.parse(file_id)
     }
 
-    pub fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
-        self.imp.cache(root_node, file_id)
-    }
-
     pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
         self.imp.expand(macro_call)
     }
@@ -377,6 +373,7 @@ impl<'db> SemanticsImpl<'db> {
         let src = diagnostics.presentation();
         let root = self.db.parse_or_expand(src.file_id).unwrap();
         let node = src.value.to_node(&root);
+        self.cache(root, src.file_id);
         original_range(self.db, src.with_value(&node))
     }
 
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 8358c488b8a..e58defa681e 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -72,9 +72,12 @@ impl<'a> DiagnosticSinkBuilder<'a> {
         self
     }
 
-    pub fn on<D: Diagnostic, F: FnMut(&D) -> Option<()> + 'a>(mut self, mut cb: F) -> Self {
+    pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self {
         let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() {
-            Some(d) => cb(d).ok_or(()),
+            Some(d) => {
+                cb(d);
+                Ok(())
+            }
             None => Err(()),
         };
         self.callbacks.push(Box::new(cb));
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index ca1a7c1aae6..165ff5249c6 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -7,22 +7,20 @@
 use std::cell::RefCell;
 
 use hir::{
-    db::AstDatabase,
-    diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
-    HasSource, HirDisplay, Semantics, VariantDef,
+    diagnostics::{Diagnostic as HirDiagnostics, DiagnosticSinkBuilder},
+    Semantics,
 };
 use itertools::Itertools;
-use ra_db::{SourceDatabase, Upcast};
+use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
 use ra_syntax::{
-    algo,
-    ast::{self, edit::IndentLevel, make, AstNode},
+    ast::{self, AstNode},
     SyntaxNode, TextRange, T,
 };
 use ra_text_edit::{TextEdit, TextEditBuilder};
 
-use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
+use crate::{Diagnostic, FileId, Fix, SourceFileEdit};
 
 mod diagnostics_with_fix;
 use diagnostics_with_fix::DiagnosticWithFix;
@@ -58,96 +56,16 @@ pub(crate) fn diagnostics(
     let res = RefCell::new(res);
     let mut sink = DiagnosticSinkBuilder::new()
         .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
-            let fix = Fix::new(
-                "Create module",
-                FileSystemEdit::CreateFile {
-                    anchor: d.file.original_file(db),
-                    dst: d.candidate.clone(),
-                }
-                .into(),
-            );
-            let fix = diagnostic_fix_source(&sema, d)
-                .map(|unresolved_module| unresolved_module.syntax().text_range())
-                .map(|fix_range| (fix, fix_range));
-
-            res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_presentation_range(d).range,
-                message: d.message(),
-                severity: Severity::Error,
-                fix,
-            });
-            Some(())
+            res.borrow_mut().push(diagnostic_with_fix(d, &sema));
         })
         .on::<hir::diagnostics::MissingFields, _>(|d| {
-            // Note that although we could add a diagnostics to
-            // fill the missing tuple field, e.g :
-            // `struct A(usize);`
-            // `let a = A { 0: () }`
-            // but it is uncommon usage and it should not be encouraged.
-            let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
-                None
-            } else {
-                diagnostic_fix_source(&sema, d)
-                    .and_then(|record_expr| record_expr.record_expr_field_list())
-                    .map(|old_field_list| {
-                        let mut new_field_list = old_field_list.clone();
-                        for f in d.missed_fields.iter() {
-                            let field = make::record_expr_field(
-                                make::name_ref(&f.to_string()),
-                                Some(make::expr_unit()),
-                            );
-                            new_field_list = new_field_list.append_field(&field);
-                        }
-
-                        let edit = {
-                            let mut builder = TextEditBuilder::default();
-                            algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
-                                .into_text_edit(&mut builder);
-                            builder.finish()
-                        };
-                        (
-                            Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into()),
-                            sema.original_range(&old_field_list.syntax()).range,
-                            // old_field_list.syntax().text_range(),
-                        )
-                    })
-            };
-
-            res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_presentation_range(d).range,
-                message: d.message(),
-                severity: Severity::Error,
-                fix,
-            });
-            Some(())
+            res.borrow_mut().push(diagnostic_with_fix(d, &sema));
         })
         .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
-            let fix = diagnostic_fix_source(&sema, d).map(|tail_expr| {
-                let tail_expr_range = tail_expr.syntax().text_range();
-                let edit =
-                    TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
-                let source_change = SourceFileEdit { file_id, edit }.into();
-                (Fix::new("Wrap with ok", source_change), tail_expr_range)
-            });
-
-            res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_presentation_range(d).range,
-                message: d.message(),
-                severity: Severity::Error,
-                fix,
-            });
-            Some(())
+            res.borrow_mut().push(diagnostic_with_fix(d, &sema));
         })
         .on::<hir::diagnostics::NoSuchField, _>(|d| {
-            res.borrow_mut().push(Diagnostic {
-                range: sema.diagnostics_presentation_range(d).range,
-                message: d.message(),
-                severity: Severity::Error,
-                fix: missing_struct_field_fix(&sema, file_id, d).and_then(|fix| {
-                    Some((fix, diagnostic_fix_source(&sema, d)?.syntax().text_range()))
-                }),
-            });
-            Some(())
+            res.borrow_mut().push(diagnostic_with_fix(d, &sema));
         })
         // Only collect experimental diagnostics when they're enabled.
         .filter(|diag| !diag.is_experimental() || enable_experimental)
@@ -168,87 +86,15 @@ pub(crate) fn diagnostics(
     res.into_inner()
 }
 
-fn diagnostic_fix_source<T: DiagnosticWithFix + hir::diagnostics::Diagnostic>(
+fn diagnostic_with_fix<D: HirDiagnostics + DiagnosticWithFix>(
+    d: &D,
     sema: &Semantics<RootDatabase>,
-    d: &T,
-) -> Option<<T as DiagnosticWithFix>::AST> {
-    let file_id = d.presentation().file_id;
-    let root = sema.db.parse_or_expand(file_id)?;
-    sema.cache(root, file_id);
-    d.fix_source(sema.db.upcast())
-}
-
-fn missing_struct_field_fix(
-    sema: &Semantics<RootDatabase>,
-    usage_file_id: FileId,
-    d: &hir::diagnostics::NoSuchField,
-) -> Option<Fix> {
-    let record_expr_field = diagnostic_fix_source(&sema, d)?;
-
-    let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
-    let def_id = sema.resolve_variant(record_lit)?;
-    let module;
-    let def_file_id;
-    let record_fields = match VariantDef::from(def_id) {
-        VariantDef::Struct(s) => {
-            module = s.module(sema.db);
-            let source = s.source(sema.db);
-            def_file_id = source.file_id;
-            let fields = source.value.field_list()?;
-            record_field_list(fields)?
-        }
-        VariantDef::Union(u) => {
-            module = u.module(sema.db);
-            let source = u.source(sema.db);
-            def_file_id = source.file_id;
-            source.value.record_field_list()?
-        }
-        VariantDef::EnumVariant(e) => {
-            module = e.module(sema.db);
-            let source = e.source(sema.db);
-            def_file_id = source.file_id;
-            let fields = source.value.field_list()?;
-            record_field_list(fields)?
-        }
-    };
-    let def_file_id = def_file_id.original_file(sema.db);
-
-    let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
-    if new_field_type.is_unknown() {
-        return None;
-    }
-    let new_field = make::record_field(
-        record_expr_field.field_name()?,
-        make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
-    );
-
-    let last_field = record_fields.fields().last()?;
-    let last_field_syntax = last_field.syntax();
-    let indent = IndentLevel::from_node(last_field_syntax);
-
-    let mut new_field = new_field.to_string();
-    if usage_file_id != def_file_id {
-        new_field = format!("pub(crate) {}", new_field);
-    }
-    new_field = format!("\n{}{}", indent, new_field);
-
-    let needs_comma = !last_field_syntax.to_string().ends_with(',');
-    if needs_comma {
-        new_field = format!(",{}", new_field);
-    }
-
-    let source_change = SourceFileEdit {
-        file_id: def_file_id,
-        edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
-    };
-    let fix = Fix::new("Create field", source_change.into());
-    return Some(fix);
-
-    fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
-        match field_def_list {
-            ast::FieldList::RecordFieldList(it) => Some(it),
-            ast::FieldList::TupleFieldList(_) => None,
-        }
+) -> Diagnostic {
+    Diagnostic {
+        range: sema.diagnostics_presentation_range(d).range,
+        message: d.message(),
+        severity: Severity::Error,
+        fix: d.fix(&sema),
     }
 }
 
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
index 8578a90ec06..56d454ac61f 100644
--- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -1,46 +1,169 @@
+use crate::Fix;
+use ast::{edit::IndentLevel, make};
 use hir::{
     db::AstDatabase,
     diagnostics::{MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
+    HasSource, HirDisplay, Semantics, VariantDef,
 };
-use ra_syntax::ast;
+use ra_db::FileId;
+use ra_ide_db::{
+    source_change::{FileSystemEdit, SourceFileEdit},
+    RootDatabase,
+};
+use ra_syntax::{algo, ast, AstNode, TextRange};
+use ra_text_edit::{TextEdit, TextEditBuilder};
 
 // TODO kb
 pub trait DiagnosticWithFix {
-    type AST;
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST>;
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)>;
 }
 
 impl DiagnosticWithFix for UnresolvedModule {
-    type AST = ast::Module;
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.decl.to_node(&root))
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+        let fix = Fix::new(
+            "Create module",
+            FileSystemEdit::CreateFile {
+                anchor: self.file.original_file(sema.db),
+                dst: self.candidate.clone(),
+            }
+            .into(),
+        );
+
+        let root = sema.db.parse_or_expand(self.file)?;
+        let unresolved_module = self.decl.to_node(&root);
+        Some((fix, unresolved_module.syntax().text_range()))
     }
 }
 
 impl DiagnosticWithFix for NoSuchField {
-    type AST = ast::RecordExprField;
-
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.field.to_node(&root))
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let record_expr_field = self.field.to_node(&root);
+        let fix =
+            missing_struct_field_fix(&sema, self.file.original_file(sema.db), &record_expr_field)?;
+        Some((fix, record_expr_field.syntax().text_range()))
     }
 }
 
 impl DiagnosticWithFix for MissingFields {
-    type AST = ast::RecordExpr;
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+        // Note that although we could add a diagnostics to
+        // fill the missing tuple field, e.g :
+        // `struct A(usize);`
+        // `let a = A { 0: () }`
+        // but it is uncommon usage and it should not be encouraged.
+        if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
+            None
+        } else {
+            let root = sema.db.parse_or_expand(self.file)?;
+            let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?;
+            let mut new_field_list = old_field_list.clone();
+            for f in self.missed_fields.iter() {
+                let field = make::record_expr_field(
+                    make::name_ref(&f.to_string()),
+                    Some(make::expr_unit()),
+                );
+                new_field_list = new_field_list.append_field(&field);
+            }
 
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.field_list_parent.to_node(&root))
+            let edit = {
+                let mut builder = TextEditBuilder::default();
+                algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
+                    .into_text_edit(&mut builder);
+                builder.finish()
+            };
+            Some((
+                Fix::new(
+                    "Fill struct fields",
+                    SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(),
+                ),
+                sema.original_range(&old_field_list.syntax()).range,
+                // old_field_list.syntax().text_range(),
+            ))
+        }
     }
 }
 
 impl DiagnosticWithFix for MissingOkInTailExpr {
-    type AST = ast::Expr;
-
-    fn fix_source(&self, db: &dyn AstDatabase) -> Option<Self::AST> {
-        let root = db.parse_or_expand(self.file)?;
-        Some(self.expr.to_node(&root))
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let tail_expr = self.expr.to_node(&root);
+        let tail_expr_range = tail_expr.syntax().text_range();
+        let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
+        let source_change =
+            SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
+        Some((Fix::new("Wrap with ok", source_change), tail_expr_range))
+    }
+}
+
+fn missing_struct_field_fix(
+    sema: &Semantics<RootDatabase>,
+    usage_file_id: FileId,
+    record_expr_field: &ast::RecordExprField,
+) -> Option<Fix> {
+    let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
+    let def_id = sema.resolve_variant(record_lit)?;
+    let module;
+    let def_file_id;
+    let record_fields = match VariantDef::from(def_id) {
+        VariantDef::Struct(s) => {
+            module = s.module(sema.db);
+            let source = s.source(sema.db);
+            def_file_id = source.file_id;
+            let fields = source.value.field_list()?;
+            record_field_list(fields)?
+        }
+        VariantDef::Union(u) => {
+            module = u.module(sema.db);
+            let source = u.source(sema.db);
+            def_file_id = source.file_id;
+            source.value.record_field_list()?
+        }
+        VariantDef::EnumVariant(e) => {
+            module = e.module(sema.db);
+            let source = e.source(sema.db);
+            def_file_id = source.file_id;
+            let fields = source.value.field_list()?;
+            record_field_list(fields)?
+        }
+    };
+    let def_file_id = def_file_id.original_file(sema.db);
+
+    let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?;
+    if new_field_type.is_unknown() {
+        return None;
+    }
+    let new_field = make::record_field(
+        record_expr_field.field_name()?,
+        make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
+    );
+
+    let last_field = record_fields.fields().last()?;
+    let last_field_syntax = last_field.syntax();
+    let indent = IndentLevel::from_node(last_field_syntax);
+
+    let mut new_field = new_field.to_string();
+    if usage_file_id != def_file_id {
+        new_field = format!("pub(crate) {}", new_field);
+    }
+    new_field = format!("\n{}{}", indent, new_field);
+
+    let needs_comma = !last_field_syntax.to_string().ends_with(',');
+    if needs_comma {
+        new_field = format!(",{}", new_field);
+    }
+
+    let source_change = SourceFileEdit {
+        file_id: def_file_id,
+        edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
+    };
+    let fix = Fix::new("Create field", source_change.into());
+    return Some(fix);
+
+    fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
+        match field_def_list {
+            ast::FieldList::RecordFieldList(it) => Some(it),
+            ast::FieldList::TupleFieldList(_) => None,
+        }
     }
 }

From 37aa68f050fae0079db7b6ebd81bacea4441fb7e Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 15:08:55 +0300
Subject: [PATCH 62/76] Add rustdocs

---
 crates/ra_hir_expand/src/diagnostics.rs               | 1 +
 crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index e58defa681e..cc7dc3af22c 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -22,6 +22,7 @@ use crate::InFile;
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
+    /// A presentation source of the diagnostics, to use in highlighting and similar actions
     fn presentation(&self) -> InFile<SyntaxNodePtr>;
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
index 56d454ac61f..1955e152106 100644
--- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -1,3 +1,4 @@
+//! Provides a way to derive fixes based on the diagnostic data.
 use crate::Fix;
 use ast::{edit::IndentLevel, make};
 use hir::{
@@ -13,8 +14,9 @@ use ra_ide_db::{
 use ra_syntax::{algo, ast, AstNode, TextRange};
 use ra_text_edit::{TextEdit, TextEditBuilder};
 
-// TODO kb
+/// A trait to implement fot the Diagnostic that has a fix available.
 pub trait DiagnosticWithFix {
+    /// Provides a fix with the fix range, if applicable in the current semantics.
     fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)>;
 }
 

From 188ec3459e795732ad097758f7bf6b6b95bdbf5e Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 17:13:40 +0300
Subject: [PATCH 63/76] Simplify fix structure

---
 crates/ra_ide/src/diagnostics.rs              |  68 +++++-------
 .../src/diagnostics/diagnostics_with_fix.rs   | 103 +++++++++---------
 crates/ra_ide/src/lib.rs                      |  12 +-
 crates/rust-analyzer/src/handlers.rs          |   7 +-
 4 files changed, 93 insertions(+), 97 deletions(-)

diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 165ff5249c6..757b76fd40f 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -6,10 +6,7 @@
 
 use std::cell::RefCell;
 
-use hir::{
-    diagnostics::{Diagnostic as HirDiagnostics, DiagnosticSinkBuilder},
-    Semantics,
-};
+use hir::{diagnostics::DiagnosticSinkBuilder, Semantics};
 use itertools::Itertools;
 use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
@@ -73,7 +70,7 @@ pub(crate) fn diagnostics(
         .build(|d| {
             res.borrow_mut().push(Diagnostic {
                 message: d.message(),
-                range: sema.diagnostics_presentation_range(d).range,
+                range: sema.diagnostics_display_range(d).range,
                 severity: Severity::Error,
                 fix: None,
             })
@@ -86,12 +83,9 @@ pub(crate) fn diagnostics(
     res.into_inner()
 }
 
-fn diagnostic_with_fix<D: HirDiagnostics + DiagnosticWithFix>(
-    d: &D,
-    sema: &Semantics<RootDatabase>,
-) -> Diagnostic {
+fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
     Diagnostic {
-        range: sema.diagnostics_presentation_range(d).range,
+        range: sema.diagnostics_display_range(d).range,
         message: d.message(),
         severity: Severity::Error,
         fix: d.fix(&sema),
@@ -120,8 +114,9 @@ fn check_unnecessary_braces_in_use_statement(
             range: use_range,
             message: "Unnecessary braces in use statement".to_string(),
             severity: Severity::WeakWarning,
-            fix: Some((
-                Fix::new("Remove unnecessary braces", SourceFileEdit { file_id, edit }.into()),
+            fix: Some(Fix::new(
+                "Remove unnecessary braces",
+                SourceFileEdit { file_id, edit }.into(),
                 use_range,
             )),
         });
@@ -165,11 +160,9 @@ fn check_struct_shorthand_initialization(
                     range: field_range,
                     message: "Shorthand struct initialization".to_string(),
                     severity: Severity::WeakWarning,
-                    fix: Some((
-                        Fix::new(
-                            "Use struct shorthand initialization",
-                            SourceFileEdit { file_id, edit }.into(),
-                        ),
+                    fix: Some(Fix::new(
+                        "Use struct shorthand initialization",
+                        SourceFileEdit { file_id, edit }.into(),
                         field_range,
                     )),
                 });
@@ -197,7 +190,7 @@ mod tests {
 
         let (analysis, file_position) = analysis_and_position(ra_fixture_before);
         let diagnostic = analysis.diagnostics(file_position.file_id, true).unwrap().pop().unwrap();
-        let (mut fix, fix_range) = diagnostic.fix.unwrap();
+        let mut fix = diagnostic.fix.unwrap();
         let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
         let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
         let actual = {
@@ -208,9 +201,10 @@ mod tests {
 
         assert_eq_text!(&after, &actual);
         assert!(
-            fix_range.start() <= file_position.offset && fix_range.end() >= file_position.offset,
+            fix.fix_trigger_range.start() <= file_position.offset
+                && fix.fix_trigger_range.end() >= file_position.offset,
             "diagnostic fix range {:?} does not touch cursor position {:?}",
-            fix_range,
+            fix.fix_trigger_range,
             file_position.offset
         );
     }
@@ -222,7 +216,7 @@ mod tests {
         let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
         let current_file_id = file_pos.file_id;
         let diagnostic = analysis.diagnostics(current_file_id, true).unwrap().pop().unwrap();
-        let mut fix = diagnostic.fix.unwrap().0;
+        let mut fix = diagnostic.fix.unwrap();
         let edit = fix.source_change.source_file_edits.pop().unwrap();
         let changed_file_id = edit.file_id;
         let before = analysis.file_text(changed_file_id).unwrap();
@@ -513,24 +507,22 @@ fn test_fn() {
                         range: 0..8,
                         severity: Error,
                         fix: Some(
-                            (
-                                Fix {
-                                    label: "Create module",
-                                    source_change: SourceChange {
-                                        source_file_edits: [],
-                                        file_system_edits: [
-                                            CreateFile {
-                                                anchor: FileId(
-                                                    1,
-                                                ),
-                                                dst: "foo.rs",
-                                            },
-                                        ],
-                                        is_snippet: false,
-                                    },
+                            Fix {
+                                label: "Create module",
+                                source_change: SourceChange {
+                                    source_file_edits: [],
+                                    file_system_edits: [
+                                        CreateFile {
+                                            anchor: FileId(
+                                                1,
+                                            ),
+                                            dst: "foo.rs",
+                                        },
+                                    ],
+                                    is_snippet: false,
                                 },
-                                0..8,
-                            ),
+                                fix_trigger_range: 0..8,
+                            },
                         ),
                     },
                 ]
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
index 1955e152106..57b54a61edb 100644
--- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -1,9 +1,9 @@
-//! Provides a way to derive fixes based on the diagnostic data.
+//! Provides a way to attach fix actions to the
 use crate::Fix;
 use ast::{edit::IndentLevel, make};
 use hir::{
     db::AstDatabase,
-    diagnostics::{MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
+    diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule},
     HasSource, HirDisplay, Semantics, VariantDef,
 };
 use ra_db::FileId;
@@ -11,94 +11,90 @@ use ra_ide_db::{
     source_change::{FileSystemEdit, SourceFileEdit},
     RootDatabase,
 };
-use ra_syntax::{algo, ast, AstNode, TextRange};
+use ra_syntax::{algo, ast, AstNode};
 use ra_text_edit::{TextEdit, TextEditBuilder};
 
-/// A trait to implement fot the Diagnostic that has a fix available.
-pub trait DiagnosticWithFix {
-    /// Provides a fix with the fix range, if applicable in the current semantics.
-    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)>;
+/// A [Diagnostic] that potentially has a fix available.
+///
+/// [Diagnostic]: hir::diagnostics::Diagnostic
+pub trait DiagnosticWithFix: Diagnostic {
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix>;
 }
 
 impl DiagnosticWithFix for UnresolvedModule {
-    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
-        let fix = Fix::new(
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
+        let root = sema.db.parse_or_expand(self.file)?;
+        let unresolved_module = self.decl.to_node(&root);
+        Some(Fix::new(
             "Create module",
             FileSystemEdit::CreateFile {
                 anchor: self.file.original_file(sema.db),
                 dst: self.candidate.clone(),
             }
             .into(),
-        );
-
-        let root = sema.db.parse_or_expand(self.file)?;
-        let unresolved_module = self.decl.to_node(&root);
-        Some((fix, unresolved_module.syntax().text_range()))
+            unresolved_module.syntax().text_range(),
+        ))
     }
 }
 
 impl DiagnosticWithFix for NoSuchField {
-    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
         let root = sema.db.parse_or_expand(self.file)?;
-        let record_expr_field = self.field.to_node(&root);
-        let fix =
-            missing_struct_field_fix(&sema, self.file.original_file(sema.db), &record_expr_field)?;
-        Some((fix, record_expr_field.syntax().text_range()))
+        missing_record_expr_field_fix(
+            &sema,
+            self.file.original_file(sema.db),
+            &self.field.to_node(&root),
+        )
     }
 }
 
 impl DiagnosticWithFix for MissingFields {
-    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
         // Note that although we could add a diagnostics to
         // fill the missing tuple field, e.g :
         // `struct A(usize);`
         // `let a = A { 0: () }`
         // but it is uncommon usage and it should not be encouraged.
         if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
-            None
-        } else {
-            let root = sema.db.parse_or_expand(self.file)?;
-            let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?;
-            let mut new_field_list = old_field_list.clone();
-            for f in self.missed_fields.iter() {
-                let field = make::record_expr_field(
-                    make::name_ref(&f.to_string()),
-                    Some(make::expr_unit()),
-                );
-                new_field_list = new_field_list.append_field(&field);
-            }
-
-            let edit = {
-                let mut builder = TextEditBuilder::default();
-                algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
-                    .into_text_edit(&mut builder);
-                builder.finish()
-            };
-            Some((
-                Fix::new(
-                    "Fill struct fields",
-                    SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(),
-                ),
-                sema.original_range(&old_field_list.syntax()).range,
-                // old_field_list.syntax().text_range(),
-            ))
+            return None;
         }
+
+        let root = sema.db.parse_or_expand(self.file)?;
+        let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?;
+        let mut new_field_list = old_field_list.clone();
+        for f in self.missed_fields.iter() {
+            let field =
+                make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
+            new_field_list = new_field_list.append_field(&field);
+        }
+
+        let edit = {
+            let mut builder = TextEditBuilder::default();
+            algo::diff(&old_field_list.syntax(), &new_field_list.syntax())
+                .into_text_edit(&mut builder);
+            builder.finish()
+        };
+        Some(Fix::new(
+            "Fill struct fields",
+            SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(),
+            sema.original_range(&old_field_list.syntax()).range,
+        ))
     }
 }
 
 impl DiagnosticWithFix for MissingOkInTailExpr {
-    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<(Fix, TextRange)> {
+    fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
         let root = sema.db.parse_or_expand(self.file)?;
         let tail_expr = self.expr.to_node(&root);
         let tail_expr_range = tail_expr.syntax().text_range();
         let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax()));
         let source_change =
             SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
-        Some((Fix::new("Wrap with ok", source_change), tail_expr_range))
+        Some(Fix::new("Wrap with ok", source_change, tail_expr_range))
     }
 }
 
-fn missing_struct_field_fix(
+fn missing_record_expr_field_fix(
     sema: &Semantics<RootDatabase>,
     usage_file_id: FileId,
     record_expr_field: &ast::RecordExprField,
@@ -159,8 +155,11 @@ fn missing_struct_field_fix(
         file_id: def_file_id,
         edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
     };
-    let fix = Fix::new("Create field", source_change.into());
-    return Some(fix);
+    return Some(Fix::new(
+        "Create field",
+        source_change.into(),
+        record_expr_field.syntax().text_range(),
+    ));
 
     fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
         match field_def_list {
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 45a4b2421e7..89fcb6f178f 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -105,20 +105,26 @@ pub struct Diagnostic {
     pub message: String,
     pub range: TextRange,
     pub severity: Severity,
-    pub fix: Option<(Fix, TextRange)>,
+    pub fix: Option<Fix>,
 }
 
 #[derive(Debug)]
 pub struct Fix {
     pub label: String,
     pub source_change: SourceChange,
+    /// Allows to trigger the fix only when the caret is in the range given
+    pub fix_trigger_range: TextRange,
 }
 
 impl Fix {
-    pub fn new(label: impl Into<String>, source_change: SourceChange) -> Self {
+    pub fn new(
+        label: impl Into<String>,
+        source_change: SourceChange,
+        fix_trigger_range: TextRange,
+    ) -> Self {
         let label = label.into();
         assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.'));
-        Self { label, source_change }
+        Self { label, source_change, fix_trigger_range }
     }
 }
 
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 144c641b2a8..785dd2a2678 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -773,12 +773,11 @@ fn handle_fixes(
 
     let diagnostics = snap.analysis.diagnostics(file_id, snap.config.experimental_diagnostics)?;
 
-    let fixes_from_diagnostics = diagnostics
+    for fix in diagnostics
         .into_iter()
         .filter_map(|d| d.fix)
-        .filter(|(_fix, fix_range)| fix_range.intersect(range).is_some())
-        .map(|(fix, _range)| fix);
-    for fix in fixes_from_diagnostics {
+        .filter(|fix| fix.fix_trigger_range.intersect(range).is_some())
+    {
         let title = fix.label;
         let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
         let action = lsp_ext::CodeAction {

From db12ccee96bf37367b39ad99638d06da7123c088 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 17:15:11 +0300
Subject: [PATCH 64/76] Better naming and docs

---
 crates/ra_hir/src/semantics.rs                |  8 ++--
 crates/ra_hir_def/src/diagnostics.rs          |  2 +-
 crates/ra_hir_expand/src/diagnostics.rs       |  4 +-
 crates/ra_hir_ty/src/diagnostics.rs           | 39 ++++++++++---------
 crates/ra_ide/src/diagnostics.rs              |  2 +-
 .../src/diagnostics/diagnostics_with_fix.rs   |  3 +-
 6 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 2dfe69039fb..e3c417b41be 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -138,8 +138,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.original_range(node)
     }
 
-    pub fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        self.imp.diagnostics_presentation_range(diagnostics)
+    pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        self.imp.diagnostics_display_range(diagnostics)
     }
 
     pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
@@ -369,8 +369,8 @@ impl<'db> SemanticsImpl<'db> {
         original_range(self.db, node.as_ref())
     }
 
-    fn diagnostics_presentation_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
-        let src = diagnostics.presentation();
+    fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
+        let src = diagnostics.display_source();
         let root = self.db.parse_or_expand(src.file_id).unwrap();
         let node = src.value.to_node(&root);
         self.cache(root, src.file_id);
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 9435c72544b..71d177070dd 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -18,7 +18,7 @@ impl Diagnostic for UnresolvedModule {
     fn message(&self) -> String {
         "unresolved module".to_string()
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.decl.clone().into())
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index cc7dc3af22c..b138500e734 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -22,8 +22,8 @@ use crate::InFile;
 
 pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
     fn message(&self) -> String;
-    /// A presentation source of the diagnostics, to use in highlighting and similar actions
-    fn presentation(&self) -> InFile<SyntaxNodePtr>;
+    /// Used in highlighting and related purposes
+    fn display_source(&self) -> InFile<SyntaxNodePtr>;
     fn as_any(&self) -> &(dyn Any + Send + 'static);
     fn is_experimental(&self) -> bool {
         false
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 24435e8a71f..7ab7f79db66 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -37,7 +37,7 @@ impl Diagnostic for NoSuchField {
         "no such field".to_string()
     }
 
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.field.clone().into())
     }
 
@@ -63,7 +63,7 @@ impl Diagnostic for MissingFields {
         buf
     }
 
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile {
             file_id: self.file,
             value: self
@@ -95,13 +95,15 @@ impl Diagnostic for MissingPatFields {
         }
         buf
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
-        let value = self
-            .field_list_parent_path
-            .clone()
-            .map(SyntaxNodePtr::from)
-            .unwrap_or_else(|| self.field_list_parent.clone().into());
-        InFile { file_id: self.file, value }
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
+        InFile {
+            file_id: self.file,
+            value: self
+                .field_list_parent_path
+                .clone()
+                .map(SyntaxNodePtr::from)
+                .unwrap_or_else(|| self.field_list_parent.clone().into()),
+        }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
         self
@@ -119,7 +121,7 @@ impl Diagnostic for MissingMatchArms {
     fn message(&self) -> String {
         String::from("Missing match arm")
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.match_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -137,7 +139,7 @@ impl Diagnostic for MissingOkInTailExpr {
     fn message(&self) -> String {
         "wrap return expression in Ok".to_string()
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -155,7 +157,7 @@ impl Diagnostic for BreakOutsideOfLoop {
     fn message(&self) -> String {
         "break outside of loop".to_string()
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -173,7 +175,7 @@ impl Diagnostic for MissingUnsafe {
     fn message(&self) -> String {
         format!("This operation is unsafe and requires an unsafe function or block")
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -194,7 +196,7 @@ impl Diagnostic for MismatchedArgCount {
         let s = if self.expected == 1 { "" } else { "s" };
         format!("Expected {} argument{}, found {}", self.expected, s, self.found)
     }
-    fn presentation(&self) -> InFile<SyntaxNodePtr> {
+    fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile { file_id: self.file, value: self.call_expr.clone().into() }
     }
     fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -256,12 +258,11 @@ mod tests {
 
         let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
         db.diagnostics(|d| {
+            let src = d.display_source();
+            let root = db.parse_or_expand(src.file_id).unwrap();
             // FIXME: macros...
-            let diagnostics_presentation = d.presentation();
-            let root = db.parse_or_expand(diagnostics_presentation.file_id).unwrap();
-
-            let file_id = diagnostics_presentation.file_id.original_file(&db);
-            let range = diagnostics_presentation.value.to_node(&root).text_range();
+            let file_id = src.file_id.original_file(&db);
+            let range = src.value.to_node(&root).text_range();
             let message = d.message().to_owned();
             actual.entry(file_id).or_default().push((range, message));
         });
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 757b76fd40f..1046d7ab374 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -183,7 +183,7 @@ mod tests {
     /// Takes a multi-file input fixture with annotated cursor positions,
     /// and checks that:
     ///  * a diagnostic is produced
-    ///  * this diagnostic fix touches the input cursor position
+    ///  * this diagnostic fix trigger range touches the input cursor position
     ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
     fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
         let after = trim_indent(ra_fixture_after);
diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
index 57b54a61edb..f7c73773f3a 100644
--- a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
+++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs
@@ -1,4 +1,5 @@
-//! Provides a way to attach fix actions to the
+//! Provides a way to attach fixes to the diagnostics.
+//! The same module also has all curret custom fixes for the diagnostics implemented.
 use crate::Fix;
 use ast::{edit::IndentLevel, make};
 use hir::{

From 7543b06d301024d10b803f4c2bc269af9d481296 Mon Sep 17 00:00:00 2001
From: Kirill Bulatov <mail4score@gmail.com>
Date: Tue, 11 Aug 2020 22:33:17 +0300
Subject: [PATCH 65/76] Display snippet in the completion label

---
 crates/ra_ide/src/completion/complete_snippet.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index 28d8f787681..4368e4eec84 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -36,7 +36,7 @@ pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
     snippet(
         ctx,
         cap,
-        "Test module",
+        "tmod (Test module)",
         "\
 #[cfg(test)]
 mod tests {
@@ -54,7 +54,7 @@ mod tests {
     snippet(
         ctx,
         cap,
-        "Test function",
+        "tfn (Test function)",
         "\
 #[test]
 fn ${1:feature}() {
@@ -106,10 +106,10 @@ mod tests {
 }
 "#,
             expect![[r#"
-                sn Test function
-                sn Test module
                 sn macro_rules
                 sn pub(crate)
+                sn tfn (Test function)
+                sn tmod (Test module)
             "#]],
         )
     }

From 8aba6bfef511acb2c5dc968bd75ef291c2ad3425 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 10:14:08 +0200
Subject: [PATCH 66/76] Simplify

---
 crates/ra_parser/src/parser.rs | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs
index d797f2cc963..d2487acc3b9 100644
--- a/crates/ra_parser/src/parser.rs
+++ b/crates/ra_parser/src/parser.rs
@@ -269,8 +269,8 @@ impl Marker {
     pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
         self.bomb.defuse();
         let idx = self.pos as usize;
-        match p.events[idx] {
-            Event::Start { kind: ref mut slot, .. } => {
+        match &mut p.events[idx] {
+            Event::Start { kind: slot, .. } => {
                 *slot = kind;
             }
             _ => unreachable!(),
@@ -320,8 +320,8 @@ impl CompletedMarker {
     pub(crate) fn precede(self, p: &mut Parser) -> Marker {
         let new_pos = p.start();
         let idx = self.start_pos as usize;
-        match p.events[idx] {
-            Event::Start { ref mut forward_parent, .. } => {
+        match &mut p.events[idx] {
+            Event::Start { forward_parent, .. } => {
                 *forward_parent = Some(new_pos.pos - self.start_pos);
             }
             _ => unreachable!(),
@@ -333,12 +333,12 @@ impl CompletedMarker {
     pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker {
         let start_idx = self.start_pos as usize;
         let finish_idx = self.finish_pos as usize;
-        match p.events[start_idx] {
-            Event::Start { ref mut kind, forward_parent: None } => *kind = TOMBSTONE,
+        match &mut p.events[start_idx] {
+            Event::Start { kind, forward_parent: None } => *kind = TOMBSTONE,
             _ => unreachable!(),
         }
-        match p.events[finish_idx] {
-            ref mut slot @ Event::Finish => *slot = Event::tombstone(),
+        match &mut p.events[finish_idx] {
+            slot @ Event::Finish => *slot = Event::tombstone(),
             _ => unreachable!(),
         }
         Marker::new(self.start_pos)

From 96001921fc1a00fe4f13dbe140e2df01430d11ea Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 12:21:03 +0200
Subject: [PATCH 67/76] Minor

---
 crates/ra_hir/src/source_analyzer.rs | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index d0cb62ef018..d3d62debfdd 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -265,8 +265,7 @@ impl SourceAnalyzer {
         }
 
         // This must be a normal source file rather than macro file.
-        let hir_path =
-            crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
+        let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
 
         // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
         // trying to resolve foo::bar.
@@ -451,7 +450,7 @@ fn adjust(
 pub(crate) fn resolve_hir_path(
     db: &dyn HirDatabase,
     resolver: &Resolver,
-    path: &crate::Path,
+    path: &Path,
 ) -> Option<PathResolution> {
     let types =
         resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty {
@@ -512,7 +511,7 @@ pub(crate) fn resolve_hir_path(
 pub(crate) fn resolve_hir_path_qualifier(
     db: &dyn HirDatabase,
     resolver: &Resolver,
-    path: &crate::Path,
+    path: &Path,
 ) -> Option<PathResolution> {
     let items = resolver
         .resolve_module_path_in_items(db.upcast(), path.mod_path())

From 67b2b3d0ce59fe082b442de30627c3386048eaca Mon Sep 17 00:00:00 2001
From: Jonas Schievink <jonas.schievink@ferrous-systems.com>
Date: Wed, 12 Aug 2020 11:49:49 +0200
Subject: [PATCH 68/76] Fix build on musl and test it in CI

---
 .github/workflows/ci.yaml          | 18 +++++++++++++-----
 Cargo.lock                         |  4 ++--
 crates/ra_prof/src/memory_usage.rs |  2 +-
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 2acd440122b..f977c88bee7 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -88,11 +88,14 @@ jobs:
       if: matrix.os == 'windows-latest'
       run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe
 
-  # Weird target to catch non-portable code
-  rust-power:
-    name: Rust Power
+  # Weird targets to catch non-portable code
+  rust-cross:
+    name: Rust Cross
     runs-on: ubuntu-latest
 
+    env:
+      targets: "powerpc-unknown-linux-gnu x86_64-unknown-linux-musl"
+
     steps:
     - name: Checkout repository
       uses: actions/checkout@v2
@@ -103,7 +106,9 @@ jobs:
         toolchain: stable
         profile: minimal
         override: true
-        target: 'powerpc-unknown-linux-gnu'
+
+    - name: Install Rust targets
+      run: rustup target add ${{ env.targets }}
 
     - name: Cache cargo directories
       uses: actions/cache@v2
@@ -114,7 +119,10 @@ jobs:
         key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
 
     - name: Check
-      run: cargo check --target=powerpc-unknown-linux-gnu --all-targets
+      run: |
+        for target in ${{ env.targets }}; do
+          cargo check --target=$target --all-targets
+        done
 
   typescript:
     name: TypeScript
diff --git a/Cargo.lock b/Cargo.lock
index a094ec4f7ae..26588916254 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -852,9 +852,9 @@ dependencies = [
 
 [[package]]
 name = "perf-event-open-sys"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95db63e37862bc1b842135d2234ef9418f222cc660c6752f45e7cf9ddfb97f96"
+checksum = "83e7183862f36d10263d0a1ccaef50fef734ade948bf026afd1bd97355c78273"
 dependencies = [
  "libc",
 ]
diff --git a/crates/ra_prof/src/memory_usage.rs b/crates/ra_prof/src/memory_usage.rs
index c2ecbd33cf4..83390212ae0 100644
--- a/crates/ra_prof/src/memory_usage.rs
+++ b/crates/ra_prof/src/memory_usage.rs
@@ -24,7 +24,7 @@ impl std::ops::Sub for MemoryUsage {
 impl MemoryUsage {
     pub fn current() -> MemoryUsage {
         cfg_if! {
-            if #[cfg(target_os = "linux")] {
+            if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
                 // Note: This is incredibly slow.
                 let alloc = unsafe { libc::mallinfo() }.uordblks as isize;
                 MemoryUsage { allocated: Bytes(alloc) }

From 49af51129b943784a3e6dbe33b05b6f683965b28 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 12:45:38 +0200
Subject: [PATCH 69/76] Deny clippy

---
 crates/ra_parser/src/grammar/expressions.rs |  1 -
 xtask/tests/tidy.rs                         | 14 ++++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index e1c25a838fb..51eaf7af650 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -509,7 +509,6 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
 //     x.1i32;
 //     x.0x01;
 // }
-#[allow(clippy::if_same_then_else)]
 fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker {
     assert!(p.at(T![.]));
     let m = lhs.precede(p);
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index d65a2acbca1..ca23010213a 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -44,11 +44,25 @@ fn rust_files_are_tidy() {
         let text = fs2::read_to_string(&path).unwrap();
         check_todo(&path, &text);
         check_trailing_ws(&path, &text);
+        deny_clippy(&path, &text);
         tidy_docs.visit(&path, &text);
     }
     tidy_docs.finish();
 }
 
+fn deny_clippy(path: &PathBuf, text: &String) {
+    if text.contains("[\u{61}llow(clippy") {
+        panic!(
+            "\n\nallowing lints is forbidden: {}.
+rust-analyzer intentionally doesn't check clippy on CI.
+You can allow lint globally via `xtask clippy`.
+
+",
+            path.display()
+        )
+    }
+}
+
 #[test]
 fn check_licenses() {
     let expected = "

From c81e7a3a5936688cbe94c32f4e872ff36100def2 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 13:03:43 +0200
Subject: [PATCH 70/76] Minor

---
 xtask/tests/tidy.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index ca23010213a..4ff72865e85 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -56,6 +56,7 @@ fn deny_clippy(path: &PathBuf, text: &String) {
             "\n\nallowing lints is forbidden: {}.
 rust-analyzer intentionally doesn't check clippy on CI.
 You can allow lint globally via `xtask clippy`.
+See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion.
 
 ",
             path.display()

From fcd4b0176f1544b389c9b028c547a1dfc92f9a56 Mon Sep 17 00:00:00 2001
From: Igor Aleksanov <popzxc@yandex.ru>
Date: Wed, 12 Aug 2020 14:08:55 +0300
Subject: [PATCH 71/76] Revert style preference-related fixes

---
 crates/flycheck/src/lib.rs                    |  1 -
 crates/ra_arena/src/map.rs                    | 10 +++++-----
 .../ra_parser/src/grammar/expressions/atom.rs |  8 +++++---
 crates/vfs/src/file_set.rs                    |  3 ---
 xtask/src/codegen/gen_syntax.rs               | 20 ++++++++++---------
 5 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index ec769459c15..7c38f5ef9d5 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -106,7 +106,6 @@ struct FlycheckActor {
     cargo_handle: Option<CargoHandle>,
 }
 
-#[allow(clippy::large_enum_variant)]
 enum Event {
     Restart(Restart),
     CheckEvent(Option<cargo_metadata::Message>),
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index c1b58712c16..0f33907c0ae 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -13,18 +13,18 @@ pub struct ArenaMap<ID, V> {
 
 impl<T, V> ArenaMap<Idx<T>, V> {
     pub fn insert(&mut self, id: Idx<T>, t: V) {
-        let idx = Self::into_idx(id);
+        let idx = Self::to_idx(id);
 
         self.v.resize_with((idx + 1).max(self.v.len()), || None);
         self.v[idx] = Some(t);
     }
 
     pub fn get(&self, id: Idx<T>) -> Option<&V> {
-        self.v.get(Self::into_idx(id)).and_then(|it| it.as_ref())
+        self.v.get(Self::to_idx(id)).and_then(|it| it.as_ref())
     }
 
     pub fn get_mut(&mut self, id: Idx<T>) -> Option<&mut V> {
-        self.v.get_mut(Self::into_idx(id)).and_then(|it| it.as_mut())
+        self.v.get_mut(Self::to_idx(id)).and_then(|it| it.as_mut())
     }
 
     pub fn values(&self) -> impl Iterator<Item = &V> {
@@ -39,7 +39,7 @@ impl<T, V> ArenaMap<Idx<T>, V> {
         self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
     }
 
-    fn into_idx(id: Idx<T>) -> usize {
+    fn to_idx(id: Idx<T>) -> usize {
         u32::from(id.into_raw()) as usize
     }
 
@@ -51,7 +51,7 @@ impl<T, V> ArenaMap<Idx<T>, V> {
 impl<T, V> std::ops::Index<Idx<V>> for ArenaMap<Idx<V>, T> {
     type Output = T;
     fn index(&self, id: Idx<V>) -> &T {
-        self.v[Self::into_idx(id)].as_ref().unwrap()
+        self.v[Self::to_idx(id)].as_ref().unwrap()
     }
 }
 
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index ca6569c9f25..0b01d3bc646 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -243,10 +243,12 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker {
         // test lambda_ret_block
         // fn main() { || -> i32 { 92 }(); }
         block_expr(p);
-    } else if p.at_ts(EXPR_FIRST) {
-        expr(p);
     } else {
-        p.error("expected expression");
+        if p.at_ts(EXPR_FIRST) {
+            expr(p);
+        } else {
+            p.error("expected expression");
+        }
     }
     m.complete(p, CLOSURE_EXPR)
 }
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 9f11268eeed..e9196fcd2f9 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -19,9 +19,6 @@ impl FileSet {
     pub fn len(&self) -> usize {
         self.files.len()
     }
-    pub fn is_empty(&self) -> bool {
-        self.len() == 0
-    }
     pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
         let mut base = self.paths[&anchor].clone();
         base.pop();
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs
index af9d63b06e7..cafad8070d2 100644
--- a/xtask/src/codegen/gen_syntax.rs
+++ b/xtask/src/codegen/gen_syntax.rs
@@ -91,16 +91,18 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> Result<String> {
                             support::children(&self.syntax)
                         }
                     }
-                } else if let Some(token_kind) = field.token_kind() {
-                    quote! {
-                        pub fn #method_name(&self) -> Option<#ty> {
-                            support::token(&self.syntax, #token_kind)
-                        }
-                    }
                 } else {
-                    quote! {
-                        pub fn #method_name(&self) -> Option<#ty> {
-                            support::child(&self.syntax)
+                    if let Some(token_kind) = field.token_kind() {
+                        quote! {
+                            pub fn #method_name(&self) -> Option<#ty> {
+                                support::token(&self.syntax, #token_kind)
+                            }
+                        }
+                    } else {
+                        quote! {
+                            pub fn #method_name(&self) -> Option<#ty> {
+                                support::child(&self.syntax)
+                            }
                         }
                     }
                 }

From c9a42c7c46bd4f8828082c7d1cbab7384a2bca93 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 13:56:58 +0200
Subject: [PATCH 72/76] Fix docs

---
 crates/ra_assists/src/handlers/apply_demorgan.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index de701f8b831..3ac4aed7d29 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -4,7 +4,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
 
 // Assist: apply_demorgan
 //
-// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
+// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
 // This transforms expressions of the form `!l || !r` into `!(l && r)`.
 // This also works with `&&`. This assist can only be applied with the cursor
 // on either `||` or `&&`, with both operands being a negation of some kind.

From f73a6419d43b21d07b7ee5d3804bdd586ee8036f Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 14:26:36 +0200
Subject: [PATCH 73/76] Allow default everywhere

closes #5681
---
 crates/ra_parser/src/grammar.rs               |   2 +-
 crates/ra_parser/src/grammar/expressions.rs   |   2 +-
 crates/ra_parser/src/grammar/items.rs         |  50 ++--
 crates/ra_parser/src/grammar/items/traits.rs  |   4 +-
 .../parser/err/0043_default_const.rast        |  40 ----
 .../parser/err/0043_default_const.rs          |   3 -
 .../inline/err/0014_default_fn_type.rast      |  58 -----
 .../parser/inline/err/0014_default_fn_type.rs |   4 -
 .../inline/ok/0132_default_fn_type.rast       |  55 -----
 .../parser/inline/ok/0132_default_fn_type.rs  |   4 -
 .../inline/ok/0163_default_unsafe_fn.rast     |  40 ----
 .../inline/ok/0163_default_unsafe_impl.rast   |  18 --
 .../inline/ok/0163_default_unsafe_impl.rs     |   1 -
 .../inline/ok/0163_default_unsafe_item.rast   |  44 ++++
 ...safe_fn.rs => 0163_default_unsafe_item.rs} |   2 +-
 .../parser/inline/ok/0164_default_item.rast   |  24 ++
 .../parser/inline/ok/0164_default_item.rs     |   1 +
 .../parser/ok/0066_default_const.rast         |  44 ----
 .../test_data/parser/ok/0066_default_const.rs |   3 -
 .../parser/ok/0066_default_modifier.rast      | 218 ++++++++++++++++++
 .../parser/ok/0066_default_modifier.rs        |  16 ++
 21 files changed, 326 insertions(+), 307 deletions(-)
 delete mode 100644 crates/ra_syntax/test_data/parser/err/0043_default_const.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/err/0043_default_const.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast
 rename crates/ra_syntax/test_data/parser/inline/ok/{0163_default_unsafe_fn.rs => 0163_default_unsafe_item.rs} (50%)
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
 create mode 100644 crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast
 create mode 100644 crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs

diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index c2e1d701e22..88468bc9716 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -110,7 +110,7 @@ pub(crate) mod fragments {
     }
 
     pub(crate) fn item(p: &mut Parser) {
-        items::item_or_macro(p, true, items::ItemFlavor::Mod)
+        items::item_or_macro(p, true)
     }
 
     pub(crate) fn macro_items(p: &mut Parser) {
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 51eaf7af650..3291e3f1469 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -73,7 +73,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
 
     // test block_items
     // fn a() { fn b() {} }
-    let m = match items::maybe_item(p, m, items::ItemFlavor::Mod) {
+    let m = match items::maybe_item(p, m) {
         Ok(()) => return,
         Err(m) => m,
     };
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index cca524ceaa0..9b76234345d 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -22,24 +22,19 @@ use super::*;
 pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
     attributes::inner_attributes(p);
     while !(stop_on_r_curly && p.at(T!['}']) || p.at(EOF)) {
-        item_or_macro(p, stop_on_r_curly, ItemFlavor::Mod)
+        item_or_macro(p, stop_on_r_curly)
     }
 }
 
-pub(super) enum ItemFlavor {
-    Mod,
-    Trait,
-}
-
 pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![
     FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW,
     CRATE_KW, USE_KW, MACRO_KW
 ];
 
-pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) {
+pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) {
     let m = p.start();
     attributes::outer_attributes(p);
-    let m = match maybe_item(p, m, flavor) {
+    let m = match maybe_item(p, m) {
         Ok(()) => {
             if p.at(T![;]) {
                 p.err_and_bump(
@@ -76,7 +71,7 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemF
     }
 }
 
-pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Result<(), Marker> {
+pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
     // test_err pub_expr
     // fn foo() { pub 92; }
     let has_visibility = opt_visibility(p);
@@ -114,38 +109,29 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
         has_mods = true;
     }
 
-    if p.at(IDENT)
-        && p.at_contextual_kw("default")
-        && (match p.nth(1) {
-            T![impl] => true,
+    // test default_item
+    // default impl T for Foo {}
+    if p.at(IDENT) && p.at_contextual_kw("default") {
+        match p.nth(1) {
+            T![fn] | T![type] | T![const] | T![impl] => {
+                p.bump_remap(T![default]);
+                has_mods = true;
+            }
             T![unsafe] => {
-                // test default_unsafe_impl
-                // default unsafe impl Foo {}
-
-                // test default_unsafe_fn
-                // impl T for Foo {
+                // test default_unsafe_item
+                // default unsafe impl T for Foo {
                 //     default unsafe fn foo() {}
                 // }
-                if p.nth(2) == T![impl] || p.nth(2) == T![fn] {
+                if matches!(p.nth(2), T![impl] | T![fn]) {
                     p.bump_remap(T![default]);
                     p.bump(T![unsafe]);
                     has_mods = true;
                 }
-                false
             }
-            T![fn] | T![type] | T![const] => {
-                if let ItemFlavor::Mod = flavor {
-                    true
-                } else {
-                    false
-                }
-            }
-            _ => false,
-        })
-    {
-        p.bump_remap(T![default]);
-        has_mods = true;
+            _ => (),
+        }
     }
+
     if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] {
         p.bump_remap(T![existential]);
         has_mods = true;
diff --git a/crates/ra_parser/src/grammar/items/traits.rs b/crates/ra_parser/src/grammar/items/traits.rs
index ef9c8ff5b03..751ce65f2dc 100644
--- a/crates/ra_parser/src/grammar/items/traits.rs
+++ b/crates/ra_parser/src/grammar/items/traits.rs
@@ -47,7 +47,7 @@ pub(crate) fn trait_item_list(p: &mut Parser) {
             error_block(p, "expected an item");
             continue;
         }
-        item_or_macro(p, true, ItemFlavor::Trait);
+        item_or_macro(p, true);
     }
     p.expect(T!['}']);
     m.complete(p, ASSOC_ITEM_LIST);
@@ -104,7 +104,7 @@ pub(crate) fn impl_item_list(p: &mut Parser) {
             error_block(p, "expected an item");
             continue;
         }
-        item_or_macro(p, true, ItemFlavor::Mod);
+        item_or_macro(p, true);
     }
     p.expect(T!['}']);
     m.complete(p, ASSOC_ITEM_LIST);
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
deleted file mode 100644
index 51ad2a846fa..00000000000
--- a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
+++ /dev/null
@@ -1,40 +0,0 @@
-SOURCE_FILE@0..39
-  TRAIT@0..38
-    TRAIT_KW@0..5 "trait"
-    WHITESPACE@5..6 " "
-    NAME@6..7
-      IDENT@6..7 "T"
-    WHITESPACE@7..8 " "
-    ASSOC_ITEM_LIST@8..38
-      L_CURLY@8..9 "{"
-      WHITESPACE@9..12 "\n  "
-      MACRO_CALL@12..19
-        PATH@12..19
-          PATH_SEGMENT@12..19
-            NAME_REF@12..19
-              IDENT@12..19 "default"
-      WHITESPACE@19..20 " "
-      CONST@20..36
-        CONST_KW@20..25 "const"
-        WHITESPACE@25..26 " "
-        NAME@26..27
-          IDENT@26..27 "f"
-        COLON@27..28 ":"
-        WHITESPACE@28..29 " "
-        PATH_TYPE@29..31
-          PATH@29..31
-            PATH_SEGMENT@29..31
-              NAME_REF@29..31
-                IDENT@29..31 "u8"
-        WHITESPACE@31..32 " "
-        EQ@32..33 "="
-        WHITESPACE@33..34 " "
-        LITERAL@34..35
-          INT_NUMBER@34..35 "0"
-        SEMICOLON@35..36 ";"
-      WHITESPACE@36..37 "\n"
-      R_CURLY@37..38 "}"
-  WHITESPACE@38..39 "\n"
-error 19..19: expected BANG
-error 19..19: expected `{`, `[`, `(`
-error 19..19: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
deleted file mode 100644
index 80f15474a5d..00000000000
--- a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-trait T {
-  default const f: u8 = 0;
-}
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast
deleted file mode 100644
index acd72094b95..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast
+++ /dev/null
@@ -1,58 +0,0 @@
-SOURCE_FILE@0..62
-  TRAIT@0..61
-    TRAIT_KW@0..5 "trait"
-    WHITESPACE@5..6 " "
-    NAME@6..7
-      IDENT@6..7 "T"
-    WHITESPACE@7..8 " "
-    ASSOC_ITEM_LIST@8..61
-      L_CURLY@8..9 "{"
-      WHITESPACE@9..14 "\n    "
-      MACRO_CALL@14..21
-        PATH@14..21
-          PATH_SEGMENT@14..21
-            NAME_REF@14..21
-              IDENT@14..21 "default"
-      WHITESPACE@21..22 " "
-      TYPE_ALIAS@22..35
-        TYPE_KW@22..26 "type"
-        WHITESPACE@26..27 " "
-        NAME@27..28
-          IDENT@27..28 "T"
-        WHITESPACE@28..29 " "
-        EQ@29..30 "="
-        WHITESPACE@30..31 " "
-        PATH_TYPE@31..34
-          PATH@31..34
-            PATH_SEGMENT@31..34
-              NAME_REF@31..34
-                IDENT@31..34 "Bar"
-        SEMICOLON@34..35 ";"
-      WHITESPACE@35..40 "\n    "
-      MACRO_CALL@40..47
-        PATH@40..47
-          PATH_SEGMENT@40..47
-            NAME_REF@40..47
-              IDENT@40..47 "default"
-      WHITESPACE@47..48 " "
-      FN@48..59
-        FN_KW@48..50 "fn"
-        WHITESPACE@50..51 " "
-        NAME@51..54
-          IDENT@51..54 "foo"
-        PARAM_LIST@54..56
-          L_PAREN@54..55 "("
-          R_PAREN@55..56 ")"
-        WHITESPACE@56..57 " "
-        BLOCK_EXPR@57..59
-          L_CURLY@57..58 "{"
-          R_CURLY@58..59 "}"
-      WHITESPACE@59..60 "\n"
-      R_CURLY@60..61 "}"
-  WHITESPACE@61..62 "\n"
-error 21..21: expected BANG
-error 21..21: expected `{`, `[`, `(`
-error 21..21: expected SEMICOLON
-error 47..47: expected BANG
-error 47..47: expected `{`, `[`, `(`
-error 47..47: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs
deleted file mode 100644
index 15ba8f4a85e..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-trait T {
-    default type T = Bar;
-    default fn foo() {}
-}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast
deleted file mode 100644
index b8d26a53a5d..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast
+++ /dev/null
@@ -1,55 +0,0 @@
-SOURCE_FILE@0..69
-  IMPL@0..68
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..6
-      PATH@5..6
-        PATH_SEGMENT@5..6
-          NAME_REF@5..6
-            IDENT@5..6 "T"
-    WHITESPACE@6..7 " "
-    FOR_KW@7..10 "for"
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..14
-      PATH@11..14
-        PATH_SEGMENT@11..14
-          NAME_REF@11..14
-            IDENT@11..14 "Foo"
-    WHITESPACE@14..15 " "
-    ASSOC_ITEM_LIST@15..68
-      L_CURLY@15..16 "{"
-      WHITESPACE@16..21 "\n    "
-      TYPE_ALIAS@21..42
-        DEFAULT_KW@21..28 "default"
-        WHITESPACE@28..29 " "
-        TYPE_KW@29..33 "type"
-        WHITESPACE@33..34 " "
-        NAME@34..35
-          IDENT@34..35 "T"
-        WHITESPACE@35..36 " "
-        EQ@36..37 "="
-        WHITESPACE@37..38 " "
-        PATH_TYPE@38..41
-          PATH@38..41
-            PATH_SEGMENT@38..41
-              NAME_REF@38..41
-                IDENT@38..41 "Bar"
-        SEMICOLON@41..42 ";"
-      WHITESPACE@42..47 "\n    "
-      FN@47..66
-        DEFAULT_KW@47..54 "default"
-        WHITESPACE@54..55 " "
-        FN_KW@55..57 "fn"
-        WHITESPACE@57..58 " "
-        NAME@58..61
-          IDENT@58..61 "foo"
-        PARAM_LIST@61..63
-          L_PAREN@61..62 "("
-          R_PAREN@62..63 ")"
-        WHITESPACE@63..64 " "
-        BLOCK_EXPR@64..66
-          L_CURLY@64..65 "{"
-          R_CURLY@65..66 "}"
-      WHITESPACE@66..67 "\n"
-      R_CURLY@67..68 "}"
-  WHITESPACE@68..69 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs
deleted file mode 100644
index 8f5d6111399..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-impl T for Foo {
-    default type T = Bar;
-    default fn foo() {}
-}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
deleted file mode 100644
index 1269621dc2d..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
+++ /dev/null
@@ -1,40 +0,0 @@
-SOURCE_FILE@0..50
-  IMPL@0..49
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..6
-      PATH@5..6
-        PATH_SEGMENT@5..6
-          NAME_REF@5..6
-            IDENT@5..6 "T"
-    WHITESPACE@6..7 " "
-    FOR_KW@7..10 "for"
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..14
-      PATH@11..14
-        PATH_SEGMENT@11..14
-          NAME_REF@11..14
-            IDENT@11..14 "Foo"
-    WHITESPACE@14..15 " "
-    ASSOC_ITEM_LIST@15..49
-      L_CURLY@15..16 "{"
-      WHITESPACE@16..21 "\n    "
-      FN@21..47
-        DEFAULT_KW@21..28 "default"
-        WHITESPACE@28..29 " "
-        UNSAFE_KW@29..35 "unsafe"
-        WHITESPACE@35..36 " "
-        FN_KW@36..38 "fn"
-        WHITESPACE@38..39 " "
-        NAME@39..42
-          IDENT@39..42 "foo"
-        PARAM_LIST@42..44
-          L_PAREN@42..43 "("
-          R_PAREN@43..44 ")"
-        WHITESPACE@44..45 " "
-        BLOCK_EXPR@45..47
-          L_CURLY@45..46 "{"
-          R_CURLY@46..47 "}"
-      WHITESPACE@47..48 "\n"
-      R_CURLY@48..49 "}"
-  WHITESPACE@49..50 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
deleted file mode 100644
index 6bfe925af25..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
+++ /dev/null
@@ -1,18 +0,0 @@
-SOURCE_FILE@0..27
-  IMPL@0..26
-    DEFAULT_KW@0..7 "default"
-    WHITESPACE@7..8 " "
-    UNSAFE_KW@8..14 "unsafe"
-    WHITESPACE@14..15 " "
-    IMPL_KW@15..19 "impl"
-    WHITESPACE@19..20 " "
-    PATH_TYPE@20..23
-      PATH@20..23
-        PATH_SEGMENT@20..23
-          NAME_REF@20..23
-            IDENT@20..23 "Foo"
-    WHITESPACE@23..24 " "
-    ASSOC_ITEM_LIST@24..26
-      L_CURLY@24..25 "{"
-      R_CURLY@25..26 "}"
-  WHITESPACE@26..27 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
deleted file mode 100644
index ba0998ff4d9..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
+++ /dev/null
@@ -1 +0,0 @@
-default unsafe impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast
new file mode 100644
index 00000000000..f2e2014605a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast
@@ -0,0 +1,44 @@
+SOURCE_FILE@0..65
+  IMPL@0..64
+    DEFAULT_KW@0..7 "default"
+    WHITESPACE@7..8 " "
+    UNSAFE_KW@8..14 "unsafe"
+    WHITESPACE@14..15 " "
+    IMPL_KW@15..19 "impl"
+    WHITESPACE@19..20 " "
+    PATH_TYPE@20..21
+      PATH@20..21
+        PATH_SEGMENT@20..21
+          NAME_REF@20..21
+            IDENT@20..21 "T"
+    WHITESPACE@21..22 " "
+    FOR_KW@22..25 "for"
+    WHITESPACE@25..26 " "
+    PATH_TYPE@26..29
+      PATH@26..29
+        PATH_SEGMENT@26..29
+          NAME_REF@26..29
+            IDENT@26..29 "Foo"
+    WHITESPACE@29..30 " "
+    ASSOC_ITEM_LIST@30..64
+      L_CURLY@30..31 "{"
+      WHITESPACE@31..36 "\n    "
+      FN@36..62
+        DEFAULT_KW@36..43 "default"
+        WHITESPACE@43..44 " "
+        UNSAFE_KW@44..50 "unsafe"
+        WHITESPACE@50..51 " "
+        FN_KW@51..53 "fn"
+        WHITESPACE@53..54 " "
+        NAME@54..57
+          IDENT@54..57 "foo"
+        PARAM_LIST@57..59
+          L_PAREN@57..58 "("
+          R_PAREN@58..59 ")"
+        WHITESPACE@59..60 " "
+        BLOCK_EXPR@60..62
+          L_CURLY@60..61 "{"
+          R_CURLY@61..62 "}"
+      WHITESPACE@62..63 "\n"
+      R_CURLY@63..64 "}"
+  WHITESPACE@64..65 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs
similarity index 50%
rename from crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
rename to crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs
index 12926cd8a1b..96340f84ab3 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs
@@ -1,3 +1,3 @@
-impl T for Foo {
+default unsafe impl T for Foo {
     default unsafe fn foo() {}
 }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast
new file mode 100644
index 00000000000..9282772f34d
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast
@@ -0,0 +1,24 @@
+SOURCE_FILE@0..26
+  IMPL@0..25
+    DEFAULT_KW@0..7 "default"
+    WHITESPACE@7..8 " "
+    IMPL_KW@8..12 "impl"
+    WHITESPACE@12..13 " "
+    PATH_TYPE@13..14
+      PATH@13..14
+        PATH_SEGMENT@13..14
+          NAME_REF@13..14
+            IDENT@13..14 "T"
+    WHITESPACE@14..15 " "
+    FOR_KW@15..18 "for"
+    WHITESPACE@18..19 " "
+    PATH_TYPE@19..22
+      PATH@19..22
+        PATH_SEGMENT@19..22
+          NAME_REF@19..22
+            IDENT@19..22 "Foo"
+    WHITESPACE@22..23 " "
+    ASSOC_ITEM_LIST@23..25
+      L_CURLY@23..24 "{"
+      R_CURLY@24..25 "}"
+  WHITESPACE@25..26 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs
new file mode 100644
index 00000000000..a6836cbd577
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs
@@ -0,0 +1 @@
+default impl T for Foo {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
deleted file mode 100644
index 6246a31a664..00000000000
--- a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
+++ /dev/null
@@ -1,44 +0,0 @@
-SOURCE_FILE@0..46
-  IMPL@0..45
-    IMPL_KW@0..4 "impl"
-    WHITESPACE@4..5 " "
-    PATH_TYPE@5..6
-      PATH@5..6
-        PATH_SEGMENT@5..6
-          NAME_REF@5..6
-            IDENT@5..6 "T"
-    WHITESPACE@6..7 " "
-    FOR_KW@7..10 "for"
-    WHITESPACE@10..11 " "
-    PATH_TYPE@11..14
-      PATH@11..14
-        PATH_SEGMENT@11..14
-          NAME_REF@11..14
-            IDENT@11..14 "Foo"
-    WHITESPACE@14..15 " "
-    ASSOC_ITEM_LIST@15..45
-      L_CURLY@15..16 "{"
-      WHITESPACE@16..19 "\n  "
-      CONST@19..43
-        DEFAULT_KW@19..26 "default"
-        WHITESPACE@26..27 " "
-        CONST_KW@27..32 "const"
-        WHITESPACE@32..33 " "
-        NAME@33..34
-          IDENT@33..34 "f"
-        COLON@34..35 ":"
-        WHITESPACE@35..36 " "
-        PATH_TYPE@36..38
-          PATH@36..38
-            PATH_SEGMENT@36..38
-              NAME_REF@36..38
-                IDENT@36..38 "u8"
-        WHITESPACE@38..39 " "
-        EQ@39..40 "="
-        WHITESPACE@40..41 " "
-        LITERAL@41..42
-          INT_NUMBER@41..42 "0"
-        SEMICOLON@42..43 ";"
-      WHITESPACE@43..44 "\n"
-      R_CURLY@44..45 "}"
-  WHITESPACE@45..46 "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
deleted file mode 100644
index dfb3b92dca7..00000000000
--- a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-impl T for Foo {
-  default const f: u8 = 0;
-}
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast
new file mode 100644
index 00000000000..e9b57ec3b3f
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast
@@ -0,0 +1,218 @@
+SOURCE_FILE@0..294
+  TRAIT@0..113
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "T"
+    WHITESPACE@7..8 " "
+    ASSOC_ITEM_LIST@8..113
+      L_CURLY@8..9 "{"
+      WHITESPACE@9..12 "\n  "
+      TYPE_ALIAS@12..33
+        DEFAULT_KW@12..19 "default"
+        WHITESPACE@19..20 " "
+        TYPE_KW@20..24 "type"
+        WHITESPACE@24..25 " "
+        NAME@25..26
+          IDENT@25..26 "T"
+        WHITESPACE@26..27 " "
+        EQ@27..28 "="
+        WHITESPACE@28..29 " "
+        PATH_TYPE@29..32
+          PATH@29..32
+            PATH_SEGMENT@29..32
+              NAME_REF@29..32
+                IDENT@29..32 "Bar"
+        SEMICOLON@32..33 ";"
+      WHITESPACE@33..36 "\n  "
+      CONST@36..60
+        DEFAULT_KW@36..43 "default"
+        WHITESPACE@43..44 " "
+        CONST_KW@44..49 "const"
+        WHITESPACE@49..50 " "
+        NAME@50..51
+          IDENT@50..51 "f"
+        COLON@51..52 ":"
+        WHITESPACE@52..53 " "
+        PATH_TYPE@53..55
+          PATH@53..55
+            PATH_SEGMENT@53..55
+              NAME_REF@53..55
+                IDENT@53..55 "u8"
+        WHITESPACE@55..56 " "
+        EQ@56..57 "="
+        WHITESPACE@57..58 " "
+        LITERAL@58..59
+          INT_NUMBER@58..59 "0"
+        SEMICOLON@59..60 ";"
+      WHITESPACE@60..63 "\n  "
+      FN@63..82
+        DEFAULT_KW@63..70 "default"
+        WHITESPACE@70..71 " "
+        FN_KW@71..73 "fn"
+        WHITESPACE@73..74 " "
+        NAME@74..77
+          IDENT@74..77 "foo"
+        PARAM_LIST@77..79
+          L_PAREN@77..78 "("
+          R_PAREN@78..79 ")"
+        WHITESPACE@79..80 " "
+        BLOCK_EXPR@80..82
+          L_CURLY@80..81 "{"
+          R_CURLY@81..82 "}"
+      WHITESPACE@82..85 "\n  "
+      FN@85..111
+        DEFAULT_KW@85..92 "default"
+        WHITESPACE@92..93 " "
+        UNSAFE_KW@93..99 "unsafe"
+        WHITESPACE@99..100 " "
+        FN_KW@100..102 "fn"
+        WHITESPACE@102..103 " "
+        NAME@103..106
+          IDENT@103..106 "bar"
+        PARAM_LIST@106..108
+          L_PAREN@106..107 "("
+          R_PAREN@107..108 ")"
+        WHITESPACE@108..109 " "
+        BLOCK_EXPR@109..111
+          L_CURLY@109..110 "{"
+          R_CURLY@110..111 "}"
+      WHITESPACE@111..112 "\n"
+      R_CURLY@112..113 "}"
+  WHITESPACE@113..115 "\n\n"
+  IMPL@115..235
+    IMPL_KW@115..119 "impl"
+    WHITESPACE@119..120 " "
+    PATH_TYPE@120..121
+      PATH@120..121
+        PATH_SEGMENT@120..121
+          NAME_REF@120..121
+            IDENT@120..121 "T"
+    WHITESPACE@121..122 " "
+    FOR_KW@122..125 "for"
+    WHITESPACE@125..126 " "
+    PATH_TYPE@126..129
+      PATH@126..129
+        PATH_SEGMENT@126..129
+          NAME_REF@126..129
+            IDENT@126..129 "Foo"
+    WHITESPACE@129..130 " "
+    ASSOC_ITEM_LIST@130..235
+      L_CURLY@130..131 "{"
+      WHITESPACE@131..134 "\n  "
+      TYPE_ALIAS@134..155
+        DEFAULT_KW@134..141 "default"
+        WHITESPACE@141..142 " "
+        TYPE_KW@142..146 "type"
+        WHITESPACE@146..147 " "
+        NAME@147..148
+          IDENT@147..148 "T"
+        WHITESPACE@148..149 " "
+        EQ@149..150 "="
+        WHITESPACE@150..151 " "
+        PATH_TYPE@151..154
+          PATH@151..154
+            PATH_SEGMENT@151..154
+              NAME_REF@151..154
+                IDENT@151..154 "Bar"
+        SEMICOLON@154..155 ";"
+      WHITESPACE@155..158 "\n  "
+      CONST@158..182
+        DEFAULT_KW@158..165 "default"
+        WHITESPACE@165..166 " "
+        CONST_KW@166..171 "const"
+        WHITESPACE@171..172 " "
+        NAME@172..173
+          IDENT@172..173 "f"
+        COLON@173..174 ":"
+        WHITESPACE@174..175 " "
+        PATH_TYPE@175..177
+          PATH@175..177
+            PATH_SEGMENT@175..177
+              NAME_REF@175..177
+                IDENT@175..177 "u8"
+        WHITESPACE@177..178 " "
+        EQ@178..179 "="
+        WHITESPACE@179..180 " "
+        LITERAL@180..181
+          INT_NUMBER@180..181 "0"
+        SEMICOLON@181..182 ";"
+      WHITESPACE@182..185 "\n  "
+      FN@185..204
+        DEFAULT_KW@185..192 "default"
+        WHITESPACE@192..193 " "
+        FN_KW@193..195 "fn"
+        WHITESPACE@195..196 " "
+        NAME@196..199
+          IDENT@196..199 "foo"
+        PARAM_LIST@199..201
+          L_PAREN@199..200 "("
+          R_PAREN@200..201 ")"
+        WHITESPACE@201..202 " "
+        BLOCK_EXPR@202..204
+          L_CURLY@202..203 "{"
+          R_CURLY@203..204 "}"
+      WHITESPACE@204..207 "\n  "
+      FN@207..233
+        DEFAULT_KW@207..214 "default"
+        WHITESPACE@214..215 " "
+        UNSAFE_KW@215..221 "unsafe"
+        WHITESPACE@221..222 " "
+        FN_KW@222..224 "fn"
+        WHITESPACE@224..225 " "
+        NAME@225..228
+          IDENT@225..228 "bar"
+        PARAM_LIST@228..230
+          L_PAREN@228..229 "("
+          R_PAREN@229..230 ")"
+        WHITESPACE@230..231 " "
+        BLOCK_EXPR@231..233
+          L_CURLY@231..232 "{"
+          R_CURLY@232..233 "}"
+      WHITESPACE@233..234 "\n"
+      R_CURLY@234..235 "}"
+  WHITESPACE@235..237 "\n\n"
+  IMPL@237..261
+    DEFAULT_KW@237..244 "default"
+    WHITESPACE@244..245 " "
+    IMPL_KW@245..249 "impl"
+    WHITESPACE@249..250 " "
+    PATH_TYPE@250..251
+      PATH@250..251
+        PATH_SEGMENT@250..251
+          NAME_REF@250..251
+            IDENT@250..251 "T"
+    WHITESPACE@251..252 " "
+    FOR_KW@252..255 "for"
+    WHITESPACE@255..256 " "
+    TUPLE_TYPE@256..258
+      L_PAREN@256..257 "("
+      R_PAREN@257..258 ")"
+    WHITESPACE@258..259 " "
+    ASSOC_ITEM_LIST@259..261
+      L_CURLY@259..260 "{"
+      R_CURLY@260..261 "}"
+  WHITESPACE@261..262 "\n"
+  IMPL@262..293
+    DEFAULT_KW@262..269 "default"
+    WHITESPACE@269..270 " "
+    UNSAFE_KW@270..276 "unsafe"
+    WHITESPACE@276..277 " "
+    IMPL_KW@277..281 "impl"
+    WHITESPACE@281..282 " "
+    PATH_TYPE@282..283
+      PATH@282..283
+        PATH_SEGMENT@282..283
+          NAME_REF@282..283
+            IDENT@282..283 "T"
+    WHITESPACE@283..284 " "
+    FOR_KW@284..287 "for"
+    WHITESPACE@287..288 " "
+    TUPLE_TYPE@288..290
+      L_PAREN@288..289 "("
+      R_PAREN@289..290 ")"
+    WHITESPACE@290..291 " "
+    ASSOC_ITEM_LIST@291..293
+      L_CURLY@291..292 "{"
+      R_CURLY@292..293 "}"
+  WHITESPACE@293..294 "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs
new file mode 100644
index 00000000000..e443e3495e3
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs
@@ -0,0 +1,16 @@
+trait T {
+  default type T = Bar;
+  default const f: u8 = 0;
+  default fn foo() {}
+  default unsafe fn bar() {}
+}
+
+impl T for Foo {
+  default type T = Bar;
+  default const f: u8 = 0;
+  default fn foo() {}
+  default unsafe fn bar() {}
+}
+
+default impl T for () {}
+default unsafe impl T for () {}

From f8bfd77e84e5b51dc28ff219e99fdfd6fd9f92c2 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 14:52:37 +0200
Subject: [PATCH 74/76] Cleanup parser modifiers tests

---
 crates/ra_parser/src/grammar/items.rs         |  62 +----
 ...ird_blocks.rast => 0043_weird_blocks.rast} |   0
 ...3_weird_blocks.rs => 0043_weird_blocks.rs} |   0
 .../0045_item_modifiers.rast}                 |   0
 .../0045_item_modifiers.rs}                   |   0
 .../parser/inline/ok/0016_unsafe_trait.rast   |  13 --
 .../parser/inline/ok/0016_unsafe_trait.rs     |   1 -
 .../inline/ok/0036_unsafe_extern_fn.rast      |  21 --
 .../parser/inline/ok/0036_unsafe_extern_fn.rs |   1 -
 .../inline/ok/0047_unsafe_default_impl.rast   |  18 --
 .../inline/ok/0047_unsafe_default_impl.rs     |   1 -
 .../parser/inline/ok/0057_const_fn.rast       |  16 --
 .../parser/inline/ok/0057_const_fn.rs         |   1 -
 .../parser/inline/ok/0087_unsafe_impl.rast    |  16 --
 .../parser/inline/ok/0087_unsafe_impl.rs      |   1 -
 .../parser/inline/ok/0089_extern_fn.rast      |  17 --
 .../parser/inline/ok/0089_extern_fn.rs        |   1 -
 .../parser/inline/ok/0091_auto_trait.rast     |  13 --
 .../parser/inline/ok/0091_auto_trait.rs       |   1 -
 .../inline/ok/0094_unsafe_auto_trait.rast     |  15 --
 .../inline/ok/0094_unsafe_auto_trait.rs       |   1 -
 .../parser/inline/ok/0097_default_impl.rast   |  16 --
 .../parser/inline/ok/0097_default_impl.rs     |   1 -
 .../inline/ok/0098_const_unsafe_fn.rast       |  18 --
 .../parser/inline/ok/0098_const_unsafe_fn.rs  |   1 -
 .../parser/inline/ok/0101_unsafe_fn.rast      |  16 --
 .../parser/inline/ok/0101_unsafe_fn.rs        |   1 -
 .../parser/inline/ok/0124_async_fn.rast       |  16 --
 .../parser/inline/ok/0124_async_fn.rs         |   1 -
 .../parser/inline/ok/0128_combined_fns.rast   |  35 ---
 .../parser/inline/ok/0128_combined_fns.rs     |   2 -
 .../test_data/parser/inline/ok/0151_fn.rast   |  14 ++
 .../test_data/parser/inline/ok/0151_fn.rs     |   1 +
 .../test_data/parser/inline/ok/0152_impl.rast |  22 ++
 .../test_data/parser/inline/ok/0152_impl.rs   |   1 +
 .../parser/inline/ok/0153_trait.rast          |  11 +
 .../test_data/parser/inline/ok/0153_trait.rs  |   1 +
 .../test_data/parser/ok/0021_extern_fn.rast   |  56 -----
 .../test_data/parser/ok/0021_extern_fn.rs     |   8 -
 .../parser/ok/0068_item_modifiers.rast        | 218 ++++++++++++++++++
 .../parser/ok/0068_item_modifiers.rs          |  16 ++
 41 files changed, 292 insertions(+), 362 deletions(-)
 rename crates/ra_syntax/test_data/parser/err/{0163_weird_blocks.rast => 0043_weird_blocks.rast} (100%)
 rename crates/ra_syntax/test_data/parser/err/{0163_weird_blocks.rs => 0043_weird_blocks.rs} (100%)
 rename crates/ra_syntax/test_data/parser/{inline/err/0010_wrong_order_fns.rast => err/0045_item_modifiers.rast} (100%)
 rename crates/ra_syntax/test_data/parser/{inline/err/0010_wrong_order_fns.rs => err/0045_item_modifiers.rs} (100%)
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast
 create mode 100644 crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs
 delete mode 100644 crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast
 delete mode 100644 crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs
 create mode 100644 crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast
 create mode 100644 crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs

diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 9b76234345d..d091b0fbb29 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -132,6 +132,8 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
         }
     }
 
+    // test existential_type
+    // existential type Foo: Fn() -> usize;
     if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] {
         p.bump_remap(T![existential]);
         has_mods = true;
@@ -139,79 +141,31 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> {
 
     // items
     match p.current() {
-        // test async_fn
-        // async fn foo() {}
-
-        // test extern_fn
-        // extern fn foo() {}
-
-        // test const_fn
-        // const fn foo() {}
-
-        // test const_unsafe_fn
-        // const unsafe fn foo() {}
-
-        // test unsafe_extern_fn
-        // unsafe extern "C" fn foo() {}
-
-        // test unsafe_fn
-        // unsafe fn foo() {}
-
-        // test combined_fns
-        // async unsafe fn foo() {}
-        // const unsafe fn bar() {}
-
-        // test_err wrong_order_fns
-        // unsafe async fn foo() {}
-        // unsafe const fn bar() {}
+        // test fn
+        // fn foo() {}
         T![fn] => {
             fn_def(p);
             m.complete(p, FN);
         }
 
-        // test unsafe_trait
-        // unsafe trait T {}
-
-        // test auto_trait
-        // auto trait T {}
-
-        // test unsafe_auto_trait
-        // unsafe auto trait T {}
+        // test trait
+        // trait T {}
         T![trait] => {
             traits::trait_def(p);
             m.complete(p, TRAIT);
         }
 
-        // test unsafe_impl
-        // unsafe impl Foo {}
-
-        // test default_impl
-        // default impl Foo {}
-
-        // test_err default_fn_type
-        // trait T {
-        //     default type T = Bar;
-        //     default fn foo() {}
-        // }
-
-        // test default_fn_type
-        // impl T for Foo {
-        //     default type T = Bar;
-        //     default fn foo() {}
-        // }
         T![const] => {
             consts::const_def(p, m);
         }
 
-        // test unsafe_default_impl
-        // unsafe default impl Foo {}
+        // test impl
+        // impl T for S {}
         T![impl] => {
             traits::impl_def(p);
             m.complete(p, IMPL);
         }
 
-        // test existential_type
-        // existential type Foo: Fn() -> usize;
         T![type] => {
             type_def(p, m);
         }
diff --git a/crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rast b/crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rast
similarity index 100%
rename from crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rast
rename to crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rast
diff --git a/crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rs b/crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rs
similarity index 100%
rename from crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rs
rename to crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rs
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rast
similarity index 100%
rename from crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
rename to crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rast
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rs b/crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rs
similarity index 100%
rename from crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rs
rename to crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rs
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast
deleted file mode 100644
index 625ab4c2d9f..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast
+++ /dev/null
@@ -1,13 +0,0 @@
-SOURCE_FILE@0..18
-  TRAIT@0..17
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    TRAIT_KW@7..12 "trait"
-    WHITESPACE@12..13 " "
-    NAME@13..14
-      IDENT@13..14 "T"
-    WHITESPACE@14..15 " "
-    ASSOC_ITEM_LIST@15..17
-      L_CURLY@15..16 "{"
-      R_CURLY@16..17 "}"
-  WHITESPACE@17..18 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs
deleted file mode 100644
index 04e021550d8..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe trait T {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast
deleted file mode 100644
index 293b1d64c7e..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast
+++ /dev/null
@@ -1,21 +0,0 @@
-SOURCE_FILE@0..30
-  FN@0..29
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    ABI@7..17
-      EXTERN_KW@7..13 "extern"
-      WHITESPACE@13..14 " "
-      STRING@14..17 "\"C\""
-    WHITESPACE@17..18 " "
-    FN_KW@18..20 "fn"
-    WHITESPACE@20..21 " "
-    NAME@21..24
-      IDENT@21..24 "foo"
-    PARAM_LIST@24..26
-      L_PAREN@24..25 "("
-      R_PAREN@25..26 ")"
-    WHITESPACE@26..27 " "
-    BLOCK_EXPR@27..29
-      L_CURLY@27..28 "{"
-      R_CURLY@28..29 "}"
-  WHITESPACE@29..30 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs
deleted file mode 100644
index 1295c2cd22b..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe extern "C" fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast
deleted file mode 100644
index d6dfa83b709..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast
+++ /dev/null
@@ -1,18 +0,0 @@
-SOURCE_FILE@0..27
-  IMPL@0..26
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    DEFAULT_KW@7..14 "default"
-    WHITESPACE@14..15 " "
-    IMPL_KW@15..19 "impl"
-    WHITESPACE@19..20 " "
-    PATH_TYPE@20..23
-      PATH@20..23
-        PATH_SEGMENT@20..23
-          NAME_REF@20..23
-            IDENT@20..23 "Foo"
-    WHITESPACE@23..24 " "
-    ASSOC_ITEM_LIST@24..26
-      L_CURLY@24..25 "{"
-      R_CURLY@25..26 "}"
-  WHITESPACE@26..27 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs
deleted file mode 100644
index 9cd6c57bd89..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe default impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast
deleted file mode 100644
index 97548a5eebc..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..18
-  FN@0..17
-    CONST_KW@0..5 "const"
-    WHITESPACE@5..6 " "
-    FN_KW@6..8 "fn"
-    WHITESPACE@8..9 " "
-    NAME@9..12
-      IDENT@9..12 "foo"
-    PARAM_LIST@12..14
-      L_PAREN@12..13 "("
-      R_PAREN@13..14 ")"
-    WHITESPACE@14..15 " "
-    BLOCK_EXPR@15..17
-      L_CURLY@15..16 "{"
-      R_CURLY@16..17 "}"
-  WHITESPACE@17..18 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs
deleted file mode 100644
index 8c84d9cd7c4..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-const fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast
deleted file mode 100644
index 43c09affedf..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..19
-  IMPL@0..18
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    IMPL_KW@7..11 "impl"
-    WHITESPACE@11..12 " "
-    PATH_TYPE@12..15
-      PATH@12..15
-        PATH_SEGMENT@12..15
-          NAME_REF@12..15
-            IDENT@12..15 "Foo"
-    WHITESPACE@15..16 " "
-    ASSOC_ITEM_LIST@16..18
-      L_CURLY@16..17 "{"
-      R_CURLY@17..18 "}"
-  WHITESPACE@18..19 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs
deleted file mode 100644
index 41055f41d96..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast
deleted file mode 100644
index 405b6a259f9..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast
+++ /dev/null
@@ -1,17 +0,0 @@
-SOURCE_FILE@0..19
-  FN@0..18
-    ABI@0..6
-      EXTERN_KW@0..6 "extern"
-    WHITESPACE@6..7 " "
-    FN_KW@7..9 "fn"
-    WHITESPACE@9..10 " "
-    NAME@10..13
-      IDENT@10..13 "foo"
-    PARAM_LIST@13..15
-      L_PAREN@13..14 "("
-      R_PAREN@14..15 ")"
-    WHITESPACE@15..16 " "
-    BLOCK_EXPR@16..18
-      L_CURLY@16..17 "{"
-      R_CURLY@17..18 "}"
-  WHITESPACE@18..19 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs
deleted file mode 100644
index 394a049f0f9..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-extern fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast
deleted file mode 100644
index 0cac9ac431f..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast
+++ /dev/null
@@ -1,13 +0,0 @@
-SOURCE_FILE@0..16
-  TRAIT@0..15
-    AUTO_KW@0..4 "auto"
-    WHITESPACE@4..5 " "
-    TRAIT_KW@5..10 "trait"
-    WHITESPACE@10..11 " "
-    NAME@11..12
-      IDENT@11..12 "T"
-    WHITESPACE@12..13 " "
-    ASSOC_ITEM_LIST@13..15
-      L_CURLY@13..14 "{"
-      R_CURLY@14..15 "}"
-  WHITESPACE@15..16 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs
deleted file mode 100644
index 72adf603519..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs
+++ /dev/null
@@ -1 +0,0 @@
-auto trait T {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast
deleted file mode 100644
index 0ef11c6825e..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast
+++ /dev/null
@@ -1,15 +0,0 @@
-SOURCE_FILE@0..23
-  TRAIT@0..22
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    AUTO_KW@7..11 "auto"
-    WHITESPACE@11..12 " "
-    TRAIT_KW@12..17 "trait"
-    WHITESPACE@17..18 " "
-    NAME@18..19
-      IDENT@18..19 "T"
-    WHITESPACE@19..20 " "
-    ASSOC_ITEM_LIST@20..22
-      L_CURLY@20..21 "{"
-      R_CURLY@21..22 "}"
-  WHITESPACE@22..23 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs
deleted file mode 100644
index 03d29f3241d..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe auto trait T {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast
deleted file mode 100644
index 0a1b21d6e68..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..20
-  IMPL@0..19
-    DEFAULT_KW@0..7 "default"
-    WHITESPACE@7..8 " "
-    IMPL_KW@8..12 "impl"
-    WHITESPACE@12..13 " "
-    PATH_TYPE@13..16
-      PATH@13..16
-        PATH_SEGMENT@13..16
-          NAME_REF@13..16
-            IDENT@13..16 "Foo"
-    WHITESPACE@16..17 " "
-    ASSOC_ITEM_LIST@17..19
-      L_CURLY@17..18 "{"
-      R_CURLY@18..19 "}"
-  WHITESPACE@19..20 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs
deleted file mode 100644
index ef6aa84a295..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs
+++ /dev/null
@@ -1 +0,0 @@
-default impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast
deleted file mode 100644
index 32a77ba490e..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast
+++ /dev/null
@@ -1,18 +0,0 @@
-SOURCE_FILE@0..25
-  FN@0..24
-    CONST_KW@0..5 "const"
-    WHITESPACE@5..6 " "
-    UNSAFE_KW@6..12 "unsafe"
-    WHITESPACE@12..13 " "
-    FN_KW@13..15 "fn"
-    WHITESPACE@15..16 " "
-    NAME@16..19
-      IDENT@16..19 "foo"
-    PARAM_LIST@19..21
-      L_PAREN@19..20 "("
-      R_PAREN@20..21 ")"
-    WHITESPACE@21..22 " "
-    BLOCK_EXPR@22..24
-      L_CURLY@22..23 "{"
-      R_CURLY@23..24 "}"
-  WHITESPACE@24..25 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs
deleted file mode 100644
index 31a1e435f55..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-const unsafe fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast
deleted file mode 100644
index 73c94e5d43a..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..19
-  FN@0..18
-    UNSAFE_KW@0..6 "unsafe"
-    WHITESPACE@6..7 " "
-    FN_KW@7..9 "fn"
-    WHITESPACE@9..10 " "
-    NAME@10..13
-      IDENT@10..13 "foo"
-    PARAM_LIST@13..15
-      L_PAREN@13..14 "("
-      R_PAREN@14..15 ")"
-    WHITESPACE@15..16 " "
-    BLOCK_EXPR@16..18
-      L_CURLY@16..17 "{"
-      R_CURLY@17..18 "}"
-  WHITESPACE@18..19 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs
deleted file mode 100644
index 33cfc4cd7a6..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-unsafe fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast
deleted file mode 100644
index a7df188bd63..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCE_FILE@0..18
-  FN@0..17
-    ASYNC_KW@0..5 "async"
-    WHITESPACE@5..6 " "
-    FN_KW@6..8 "fn"
-    WHITESPACE@8..9 " "
-    NAME@9..12
-      IDENT@9..12 "foo"
-    PARAM_LIST@12..14
-      L_PAREN@12..13 "("
-      R_PAREN@13..14 ")"
-    WHITESPACE@14..15 " "
-    BLOCK_EXPR@15..17
-      L_CURLY@15..16 "{"
-      R_CURLY@16..17 "}"
-  WHITESPACE@17..18 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs
deleted file mode 100644
index f4adcb62b3b..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs
+++ /dev/null
@@ -1 +0,0 @@
-async fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast b/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast
deleted file mode 100644
index 98a20f36d62..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast
+++ /dev/null
@@ -1,35 +0,0 @@
-SOURCE_FILE@0..50
-  FN@0..24
-    ASYNC_KW@0..5 "async"
-    WHITESPACE@5..6 " "
-    UNSAFE_KW@6..12 "unsafe"
-    WHITESPACE@12..13 " "
-    FN_KW@13..15 "fn"
-    WHITESPACE@15..16 " "
-    NAME@16..19
-      IDENT@16..19 "foo"
-    PARAM_LIST@19..21
-      L_PAREN@19..20 "("
-      R_PAREN@20..21 ")"
-    WHITESPACE@21..22 " "
-    BLOCK_EXPR@22..24
-      L_CURLY@22..23 "{"
-      R_CURLY@23..24 "}"
-  WHITESPACE@24..25 "\n"
-  FN@25..49
-    CONST_KW@25..30 "const"
-    WHITESPACE@30..31 " "
-    UNSAFE_KW@31..37 "unsafe"
-    WHITESPACE@37..38 " "
-    FN_KW@38..40 "fn"
-    WHITESPACE@40..41 " "
-    NAME@41..44
-      IDENT@41..44 "bar"
-    PARAM_LIST@44..46
-      L_PAREN@44..45 "("
-      R_PAREN@45..46 ")"
-    WHITESPACE@46..47 " "
-    BLOCK_EXPR@47..49
-      L_CURLY@47..48 "{"
-      R_CURLY@48..49 "}"
-  WHITESPACE@49..50 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs b/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs
deleted file mode 100644
index 12628714532..00000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-async unsafe fn foo() {}
-const unsafe fn bar() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast
new file mode 100644
index 00000000000..23c4269b306
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast
@@ -0,0 +1,14 @@
+SOURCE_FILE@0..12
+  FN@0..11
+    FN_KW@0..2 "fn"
+    WHITESPACE@2..3 " "
+    NAME@3..6
+      IDENT@3..6 "foo"
+    PARAM_LIST@6..8
+      L_PAREN@6..7 "("
+      R_PAREN@7..8 ")"
+    WHITESPACE@8..9 " "
+    BLOCK_EXPR@9..11
+      L_CURLY@9..10 "{"
+      R_CURLY@10..11 "}"
+  WHITESPACE@11..12 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs
new file mode 100644
index 00000000000..8f3b7ef112a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs
@@ -0,0 +1 @@
+fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast
new file mode 100644
index 00000000000..7968cf9ffa0
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast
@@ -0,0 +1,22 @@
+SOURCE_FILE@0..16
+  IMPL@0..15
+    IMPL_KW@0..4 "impl"
+    WHITESPACE@4..5 " "
+    PATH_TYPE@5..6
+      PATH@5..6
+        PATH_SEGMENT@5..6
+          NAME_REF@5..6
+            IDENT@5..6 "T"
+    WHITESPACE@6..7 " "
+    FOR_KW@7..10 "for"
+    WHITESPACE@10..11 " "
+    PATH_TYPE@11..12
+      PATH@11..12
+        PATH_SEGMENT@11..12
+          NAME_REF@11..12
+            IDENT@11..12 "S"
+    WHITESPACE@12..13 " "
+    ASSOC_ITEM_LIST@13..15
+      L_CURLY@13..14 "{"
+      R_CURLY@14..15 "}"
+  WHITESPACE@15..16 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs
new file mode 100644
index 00000000000..a1a550d8a60
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs
@@ -0,0 +1 @@
+impl T for S {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast
new file mode 100644
index 00000000000..9881e5048c8
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast
@@ -0,0 +1,11 @@
+SOURCE_FILE@0..11
+  TRAIT@0..10
+    TRAIT_KW@0..5 "trait"
+    WHITESPACE@5..6 " "
+    NAME@6..7
+      IDENT@6..7 "T"
+    WHITESPACE@7..8 " "
+    ASSOC_ITEM_LIST@8..10
+      L_CURLY@8..9 "{"
+      R_CURLY@9..10 "}"
+  WHITESPACE@10..11 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs
new file mode 100644
index 00000000000..8d183dbb5d3
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs
@@ -0,0 +1 @@
+trait T {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast b/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast
deleted file mode 100644
index 5524efaafed..00000000000
--- a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast
+++ /dev/null
@@ -1,56 +0,0 @@
-SOURCE_FILE@0..71
-  FN@0..19
-    ABI@0..6
-      EXTERN_KW@0..6 "extern"
-    WHITESPACE@6..7 " "
-    FN_KW@7..9 "fn"
-    WHITESPACE@9..10 " "
-    NAME@10..13
-      IDENT@10..13 "foo"
-    PARAM_LIST@13..15
-      L_PAREN@13..14 "("
-      R_PAREN@14..15 ")"
-    WHITESPACE@15..16 " "
-    BLOCK_EXPR@16..19
-      L_CURLY@16..17 "{"
-      WHITESPACE@17..18 "\n"
-      R_CURLY@18..19 "}"
-  WHITESPACE@19..21 "\n\n"
-  FN@21..44
-    ABI@21..31
-      EXTERN_KW@21..27 "extern"
-      WHITESPACE@27..28 " "
-      STRING@28..31 "\"C\""
-    WHITESPACE@31..32 " "
-    FN_KW@32..34 "fn"
-    WHITESPACE@34..35 " "
-    NAME@35..38
-      IDENT@35..38 "bar"
-    PARAM_LIST@38..40
-      L_PAREN@38..39 "("
-      R_PAREN@39..40 ")"
-    WHITESPACE@40..41 " "
-    BLOCK_EXPR@41..44
-      L_CURLY@41..42 "{"
-      WHITESPACE@42..43 "\n"
-      R_CURLY@43..44 "}"
-  WHITESPACE@44..46 "\n\n"
-  FN@46..70
-    ABI@46..57
-      EXTERN_KW@46..52 "extern"
-      WHITESPACE@52..53 " "
-      RAW_STRING@53..57 "r\"D\""
-    WHITESPACE@57..58 " "
-    FN_KW@58..60 "fn"
-    WHITESPACE@60..61 " "
-    NAME@61..64
-      IDENT@61..64 "baz"
-    PARAM_LIST@64..66
-      L_PAREN@64..65 "("
-      R_PAREN@65..66 ")"
-    WHITESPACE@66..67 " "
-    BLOCK_EXPR@67..70
-      L_CURLY@67..68 "{"
-      WHITESPACE@68..69 "\n"
-      R_CURLY@69..70 "}"
-  WHITESPACE@70..71 "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs b/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs
deleted file mode 100644
index e929eef7414..00000000000
--- a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-extern fn foo() {
-}
-
-extern "C" fn bar() {
-}
-
-extern r"D" fn baz() {
-}
diff --git a/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast
new file mode 100644
index 00000000000..50a6d8ee9a6
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast
@@ -0,0 +1,218 @@
+SOURCE_FILE@0..304
+  FN@0..17
+    ASYNC_KW@0..5 "async"
+    WHITESPACE@5..6 " "
+    FN_KW@6..8 "fn"
+    WHITESPACE@8..9 " "
+    NAME@9..12
+      IDENT@9..12 "foo"
+    PARAM_LIST@12..14
+      L_PAREN@12..13 "("
+      R_PAREN@13..14 ")"
+    WHITESPACE@14..15 " "
+    BLOCK_EXPR@15..17
+      L_CURLY@15..16 "{"
+      R_CURLY@16..17 "}"
+  WHITESPACE@17..18 "\n"
+  FN@18..36
+    ABI@18..24
+      EXTERN_KW@18..24 "extern"
+    WHITESPACE@24..25 " "
+    FN_KW@25..27 "fn"
+    WHITESPACE@27..28 " "
+    NAME@28..31
+      IDENT@28..31 "foo"
+    PARAM_LIST@31..33
+      L_PAREN@31..32 "("
+      R_PAREN@32..33 ")"
+    WHITESPACE@33..34 " "
+    BLOCK_EXPR@34..36
+      L_CURLY@34..35 "{"
+      R_CURLY@35..36 "}"
+  WHITESPACE@36..37 "\n"
+  FN@37..54
+    CONST_KW@37..42 "const"
+    WHITESPACE@42..43 " "
+    FN_KW@43..45 "fn"
+    WHITESPACE@45..46 " "
+    NAME@46..49
+      IDENT@46..49 "foo"
+    PARAM_LIST@49..51
+      L_PAREN@49..50 "("
+      R_PAREN@50..51 ")"
+    WHITESPACE@51..52 " "
+    BLOCK_EXPR@52..54
+      L_CURLY@52..53 "{"
+      R_CURLY@53..54 "}"
+  WHITESPACE@54..55 "\n"
+  FN@55..79
+    CONST_KW@55..60 "const"
+    WHITESPACE@60..61 " "
+    UNSAFE_KW@61..67 "unsafe"
+    WHITESPACE@67..68 " "
+    FN_KW@68..70 "fn"
+    WHITESPACE@70..71 " "
+    NAME@71..74
+      IDENT@71..74 "foo"
+    PARAM_LIST@74..76
+      L_PAREN@74..75 "("
+      R_PAREN@75..76 ")"
+    WHITESPACE@76..77 " "
+    BLOCK_EXPR@77..79
+      L_CURLY@77..78 "{"
+      R_CURLY@78..79 "}"
+  WHITESPACE@79..80 "\n"
+  FN@80..109
+    UNSAFE_KW@80..86 "unsafe"
+    WHITESPACE@86..87 " "
+    ABI@87..97
+      EXTERN_KW@87..93 "extern"
+      WHITESPACE@93..94 " "
+      STRING@94..97 "\"C\""
+    WHITESPACE@97..98 " "
+    FN_KW@98..100 "fn"
+    WHITESPACE@100..101 " "
+    NAME@101..104
+      IDENT@101..104 "foo"
+    PARAM_LIST@104..106
+      L_PAREN@104..105 "("
+      R_PAREN@105..106 ")"
+    WHITESPACE@106..107 " "
+    BLOCK_EXPR@107..109
+      L_CURLY@107..108 "{"
+      R_CURLY@108..109 "}"
+  WHITESPACE@109..110 "\n"
+  FN@110..128
+    UNSAFE_KW@110..116 "unsafe"
+    WHITESPACE@116..117 " "
+    FN_KW@117..119 "fn"
+    WHITESPACE@119..120 " "
+    NAME@120..123
+      IDENT@120..123 "foo"
+    PARAM_LIST@123..125
+      L_PAREN@123..124 "("
+      R_PAREN@124..125 ")"
+    WHITESPACE@125..126 " "
+    BLOCK_EXPR@126..128
+      L_CURLY@126..127 "{"
+      R_CURLY@127..128 "}"
+  WHITESPACE@128..129 "\n"
+  FN@129..153
+    ASYNC_KW@129..134 "async"
+    WHITESPACE@134..135 " "
+    UNSAFE_KW@135..141 "unsafe"
+    WHITESPACE@141..142 " "
+    FN_KW@142..144 "fn"
+    WHITESPACE@144..145 " "
+    NAME@145..148
+      IDENT@145..148 "foo"
+    PARAM_LIST@148..150
+      L_PAREN@148..149 "("
+      R_PAREN@149..150 ")"
+    WHITESPACE@150..151 " "
+    BLOCK_EXPR@151..153
+      L_CURLY@151..152 "{"
+      R_CURLY@152..153 "}"
+  WHITESPACE@153..154 "\n"
+  FN@154..178
+    CONST_KW@154..159 "const"
+    WHITESPACE@159..160 " "
+    UNSAFE_KW@160..166 "unsafe"
+    WHITESPACE@166..167 " "
+    FN_KW@167..169 "fn"
+    WHITESPACE@169..170 " "
+    NAME@170..173
+      IDENT@170..173 "bar"
+    PARAM_LIST@173..175
+      L_PAREN@173..174 "("
+      R_PAREN@174..175 ")"
+    WHITESPACE@175..176 " "
+    BLOCK_EXPR@176..178
+      L_CURLY@176..177 "{"
+      R_CURLY@177..178 "}"
+  WHITESPACE@178..180 "\n\n"
+  TRAIT@180..197
+    UNSAFE_KW@180..186 "unsafe"
+    WHITESPACE@186..187 " "
+    TRAIT_KW@187..192 "trait"
+    WHITESPACE@192..193 " "
+    NAME@193..194
+      IDENT@193..194 "T"
+    WHITESPACE@194..195 " "
+    ASSOC_ITEM_LIST@195..197
+      L_CURLY@195..196 "{"
+      R_CURLY@196..197 "}"
+  WHITESPACE@197..198 "\n"
+  TRAIT@198..213
+    AUTO_KW@198..202 "auto"
+    WHITESPACE@202..203 " "
+    TRAIT_KW@203..208 "trait"
+    WHITESPACE@208..209 " "
+    NAME@209..210
+      IDENT@209..210 "T"
+    WHITESPACE@210..211 " "
+    ASSOC_ITEM_LIST@211..213
+      L_CURLY@211..212 "{"
+      R_CURLY@212..213 "}"
+  WHITESPACE@213..214 "\n"
+  TRAIT@214..236
+    UNSAFE_KW@214..220 "unsafe"
+    WHITESPACE@220..221 " "
+    AUTO_KW@221..225 "auto"
+    WHITESPACE@225..226 " "
+    TRAIT_KW@226..231 "trait"
+    WHITESPACE@231..232 " "
+    NAME@232..233
+      IDENT@232..233 "T"
+    WHITESPACE@233..234 " "
+    ASSOC_ITEM_LIST@234..236
+      L_CURLY@234..235 "{"
+      R_CURLY@235..236 "}"
+  WHITESPACE@236..238 "\n\n"
+  IMPL@238..256
+    UNSAFE_KW@238..244 "unsafe"
+    WHITESPACE@244..245 " "
+    IMPL_KW@245..249 "impl"
+    WHITESPACE@249..250 " "
+    PATH_TYPE@250..253
+      PATH@250..253
+        PATH_SEGMENT@250..253
+          NAME_REF@250..253
+            IDENT@250..253 "Foo"
+    WHITESPACE@253..254 " "
+    ASSOC_ITEM_LIST@254..256
+      L_CURLY@254..255 "{"
+      R_CURLY@255..256 "}"
+  WHITESPACE@256..257 "\n"
+  IMPL@257..276
+    DEFAULT_KW@257..264 "default"
+    WHITESPACE@264..265 " "
+    IMPL_KW@265..269 "impl"
+    WHITESPACE@269..270 " "
+    PATH_TYPE@270..273
+      PATH@270..273
+        PATH_SEGMENT@270..273
+          NAME_REF@270..273
+            IDENT@270..273 "Foo"
+    WHITESPACE@273..274 " "
+    ASSOC_ITEM_LIST@274..276
+      L_CURLY@274..275 "{"
+      R_CURLY@275..276 "}"
+  WHITESPACE@276..277 "\n"
+  IMPL@277..303
+    UNSAFE_KW@277..283 "unsafe"
+    WHITESPACE@283..284 " "
+    DEFAULT_KW@284..291 "default"
+    WHITESPACE@291..292 " "
+    IMPL_KW@292..296 "impl"
+    WHITESPACE@296..297 " "
+    PATH_TYPE@297..300
+      PATH@297..300
+        PATH_SEGMENT@297..300
+          NAME_REF@297..300
+            IDENT@297..300 "Foo"
+    WHITESPACE@300..301 " "
+    ASSOC_ITEM_LIST@301..303
+      L_CURLY@301..302 "{"
+      R_CURLY@302..303 "}"
+  WHITESPACE@303..304 "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs
new file mode 100644
index 00000000000..8d697c04b95
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs
@@ -0,0 +1,16 @@
+async fn foo() {}
+extern fn foo() {}
+const fn foo() {}
+const unsafe fn foo() {}
+unsafe extern "C" fn foo() {}
+unsafe fn foo() {}
+async unsafe fn foo() {}
+const unsafe fn bar() {}
+
+unsafe trait T {}
+auto trait T {}
+unsafe auto trait T {}
+
+unsafe impl Foo {}
+default impl Foo {}
+unsafe default impl Foo {}

From 1c359ab634edb81b51e3c7eadfb83d46c926e890 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 15:04:06 +0200
Subject: [PATCH 75/76] Replace SepBy with Itertools

---
 .../src/handlers/add_custom_impl.rs           |  4 +-
 .../ra_assists/src/handlers/generate_impl.rs  |  5 +-
 .../ra_assists/src/handlers/generate_new.rs   |  9 +--
 crates/ra_ide/src/completion/presentation.rs  | 31 +++++----
 .../src/syntax_highlighting/injection.rs      |  5 +-
 crates/ra_syntax/src/ast/traits.rs            |  5 +-
 crates/stdx/src/lib.rs                        | 65 +------------------
 7 files changed, 30 insertions(+), 94 deletions(-)

diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index b67438b6ba2..ebdf00e676c 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -1,10 +1,10 @@
+use itertools::Itertools;
 use ra_syntax::{
     ast::{self, AstNode},
     Direction, SmolStr,
     SyntaxKind::{IDENT, WHITESPACE},
     TextRange, TextSize,
 };
-use stdx::SepBy;
 
 use crate::{
     assist_context::{AssistContext, Assists},
@@ -61,9 +61,9 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
             .filter(|t| t != trait_token.text())
             .collect::<Vec<SmolStr>>();
         let has_more_derives = !new_attr_input.is_empty();
-        let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
 
         if has_more_derives {
+            let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
             builder.replace(input.syntax().text_range(), new_attr_input);
         } else {
             let attr_range = attr.syntax().text_range();
diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs
index d9b87c9c0dd..7162dc18486 100644
--- a/crates/ra_assists/src/handlers/generate_impl.rs
+++ b/crates/ra_assists/src/handlers/generate_impl.rs
@@ -1,5 +1,6 @@
+use itertools::Itertools;
 use ra_syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner};
-use stdx::{format_to, SepBy};
+use stdx::format_to;
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -50,7 +51,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
                     .filter_map(|it| it.name())
                     .map(|it| it.text().clone());
 
-                let generic_params = lifetime_params.chain(type_params).sep_by(", ");
+                let generic_params = lifetime_params.chain(type_params).format(", ");
                 format_to!(buf, "<{}>", generic_params)
             }
             match ctx.config.snippet_cap {
diff --git a/crates/ra_assists/src/handlers/generate_new.rs b/crates/ra_assists/src/handlers/generate_new.rs
index b84aa24b6c7..32dfed274ad 100644
--- a/crates/ra_assists/src/handlers/generate_new.rs
+++ b/crates/ra_assists/src/handlers/generate_new.rs
@@ -1,9 +1,10 @@
 use hir::Adt;
+use itertools::Itertools;
 use ra_syntax::{
     ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
     T,
 };
-use stdx::{format_to, SepBy};
+use stdx::format_to;
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -52,8 +53,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
         let params = field_list
             .fields()
             .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
-            .sep_by(", ");
-        let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
+            .format(", ");
+        let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
 
         format_to!(buf, "    {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
 
@@ -102,7 +103,7 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
             .map(|it| it.text().clone());
         let type_params =
             type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
-        format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", "))
+        format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
     }
 
     format_to!(buf, " {{\n{}\n}}\n", code);
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 9a94ff47671..59f1b14246f 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -2,8 +2,8 @@
 //! It also handles scoring (sorting) completions.
 
 use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
+use itertools::Itertools;
 use ra_syntax::ast::NameOwner;
-use stdx::SepBy;
 use test_utils::mark;
 
 use crate::{
@@ -289,16 +289,16 @@ impl Completions {
             .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
         let variant_kind = variant.kind(ctx.db);
         let detail = match variant_kind {
-            StructKind::Tuple | StructKind::Unit => detail_types
-                .map(|(_, t)| t.display(ctx.db).to_string())
-                .sep_by(", ")
-                .surround_with("(", ")")
-                .to_string(),
-            StructKind::Record => detail_types
-                .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
-                .sep_by(", ")
-                .surround_with("{ ", " }")
-                .to_string(),
+            StructKind::Tuple | StructKind::Unit => format!(
+                "({})",
+                detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ")
+            ),
+            StructKind::Record => format!(
+                "{{ {} }}",
+                detail_types
+                    .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
+                    .format(", ")
+            ),
         };
         let mut res = CompletionItem::new(
             CompletionKind::Reference,
@@ -412,11 +412,10 @@ impl Builder {
             self = self.trigger_call_info();
             let snippet = match (ctx.config.add_call_argument_snippets, params) {
                 (true, Params::Named(params)) => {
-                    let function_params_snippet = params
-                        .iter()
-                        .enumerate()
-                        .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
-                        .sep_by(", ");
+                    let function_params_snippet =
+                        params.iter().enumerate().format_with(", ", |(index, param_name), f| {
+                            f(&format_args!("${{{}:{}}}", index + 1, param_name))
+                        });
                     format!("{}({})$0", name, function_params_snippet)
                 }
                 _ => {
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index 8665b480fdb..6046643ef1e 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -4,8 +4,8 @@ use std::{collections::BTreeMap, convert::TryFrom};
 
 use ast::{HasQuotes, HasStringValue};
 use hir::Semantics;
+use itertools::Itertools;
 use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
-use stdx::SepBy;
 
 use crate::{
     call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag,
@@ -129,8 +129,7 @@ pub(super) fn extract_doc_comments(
 
             line[pos..].to_owned()
         })
-        .sep_by("\n")
-        .to_string();
+        .join("\n");
 
     if doctest.is_empty() {
         return None;
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index 3a56b1674c8..0bdc22d953f 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -1,7 +1,7 @@
 //! Various traits that are implemented by ast nodes.
 //!
 //! The implementations are usually trivial, and live in generated.rs
-use stdx::SepBy;
+use itertools::Itertools;
 
 use crate::{
     ast::{self, support, AstChildren, AstNode, AstToken},
@@ -119,8 +119,7 @@ impl CommentIter {
                 // of a line in markdown.
                 line[pos..end].to_owned()
             })
-            .sep_by("\n")
-            .to_string();
+            .join("\n");
 
         if has_comments {
             Some(docs)
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 00bfcd29ed0..3c5027fe570 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,5 @@
 //! Missing batteries for standard libraries.
-use std::{cell::Cell, fmt, time::Instant};
+use std::time::Instant;
 
 mod macros;
 
@@ -8,69 +8,6 @@ pub fn is_ci() -> bool {
     option_env!("CI").is_some()
 }
 
-pub trait SepBy: Sized {
-    /// Returns an `impl fmt::Display`, which joins elements via a separator.
-    fn sep_by(self, sep: &str) -> SepByBuilder<'_, Self>;
-}
-
-impl<I> SepBy for I
-where
-    I: Iterator,
-    I::Item: fmt::Display,
-{
-    fn sep_by(self, sep: &str) -> SepByBuilder<'_, Self> {
-        SepByBuilder::new(sep, self)
-    }
-}
-
-pub struct SepByBuilder<'a, I> {
-    sep: &'a str,
-    prefix: &'a str,
-    suffix: &'a str,
-    iter: Cell<Option<I>>,
-}
-
-impl<'a, I> SepByBuilder<'a, I> {
-    fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> {
-        SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) }
-    }
-
-    pub fn prefix(mut self, prefix: &'a str) -> Self {
-        self.prefix = prefix;
-        self
-    }
-
-    pub fn suffix(mut self, suffix: &'a str) -> Self {
-        self.suffix = suffix;
-        self
-    }
-
-    /// Set both suffix and prefix.
-    pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self {
-        self.prefix(prefix).suffix(suffix)
-    }
-}
-
-impl<I> fmt::Display for SepByBuilder<'_, I>
-where
-    I: Iterator,
-    I::Item: fmt::Display,
-{
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(self.prefix)?;
-        let mut first = true;
-        for item in self.iter.take().unwrap() {
-            if !first {
-                f.write_str(self.sep)?;
-            }
-            first = false;
-            fmt::Display::fmt(&item, f)?;
-        }
-        f.write_str(self.suffix)?;
-        Ok(())
-    }
-}
-
 #[must_use]
 pub fn timeit(label: &'static str) -> impl Drop {
     struct Guard {

From 5534bc0321f3a1174882b3fbbf2a08eb19a9868d Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Wed, 12 Aug 2020 15:54:39 +0200
Subject: [PATCH 76/76] Completely remove cargo audit

My current feeling is that the build maintenance friction it creates
is not proportional to the benefits it provides.

We are pretty frugal with the set of Rust dependencies, and our
security model is "we run build.rs and proc macros", so it doesn't
seem like cargo audit could help us much.
---
 .github/workflows/ci.yaml | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index f977c88bee7..f46fb8fecc2 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -16,20 +16,6 @@ env:
   RUSTUP_MAX_RETRIES: 10
 
 jobs:
-  # rust-audit:
-  #   name: Audit Rust vulnerabilities
-  #   runs-on: ubuntu-latest
-  #   steps:
-  #   - name: Checkout repository
-  #     uses: actions/checkout@v2
-
-  #   - uses: actions-rs/install@v0.1
-  #     with:
-  #       crate: cargo-audit
-  #       use-tool-cache: true
-
-  #   - run: cargo audit
-
   rust:
     name: Rust
     runs-on: ${{ matrix.os }}