From 21d2cebcf1a417bce72da98aa638a20235c050db Mon Sep 17 00:00:00 2001 From: David Lattimore 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 4348b43beb..8be862fd6e 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 6f62000f4a..d5b65eaacf 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 2ae03c64c4..0a49a46e38 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 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 e73b3a2119..6994e611b2 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 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 451928c27b..dc49fc4bdc 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 623ce261a0..83397d5793 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 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 2deee404cc..b9b3580223 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 b81b6f1c3b..7c519ef5e5 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 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 { @@ -50,5 +56,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd HasUnsafeFn.unsafe_method(); let y = *(x); let z = -x; + let a = global_mut.a; } } \ No newline at end of file From 8e657f663d519771ac8ffcd4b52ded8cb381b918 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria 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 a32ae0165e..89efe71dab 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 7c519ef5e5..87d8c15568 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 a: u8 } -static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; +static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; fn main() { let x = &5 as *const usize; @@ -56,6 +56,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd HasUnsafeFn.unsafe_method(); let y = *(x); let z = -x; - let a = global_mut.a; + let a = global_mut.a; } } \ 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 23c25ad8ce..8e0160eee5 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 } } -static mut STATIC_MUT: i32 = 0; +static mut STATIC_MUT: i32 = 0; fn foo<'a, T>() -> T { foo::<'a, i32>() @@ -97,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } unsafe { vec.set_len(0); - STATIC_MUT = 1; + STATIC_MUT = 1; } for e in vec { From f089690a21357ca6d396bce98bf598f0954199b5 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria 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 5cc76bdce4..61ffbf5d15 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 { 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, 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 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 2deee404cc..a09422da35 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 b81b6f1c3b..39582b5bb4 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
unsafe fn unsafe_fn() {}
 
+union Union {
+    a: u32,
+    b: f32,
+}
+
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
@@ -45,8 +50,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 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;

From c04b2e39dacfea5c6ba158d842fb3b7f3e0db12b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=BCdiger=20Herrmann?= 
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 1adf055d0c..ee5f96bf32 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 
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 ceddb2b056..438e965e08 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 
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 3fd7d5cd4f..a2db6944df 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 
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 28f32a0a4d..3d12039a6d 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 1eacc6f95e..7638f167b5 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 a2db6944df..5a7cf9455b 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 39582b5bb4..de70363da2 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
 
 fn main() {
     let x = &5 as *const usize;
-    let u = Union { b: 0 };
+    let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
-        let b = u.b;
+        let b = u.b;
         match u {
-            Union { b: 0 } => (),
-            Union { a } => (),
+            Union { b: 0 } => (),
+            Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
         let y = *(x);

From be935b2b56dcbda5a5918d8c600552b0adbb3a96 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
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 a32ae0165e..bfe6143ca5 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 {
     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,
     bindings_shadow_count: &mut FxHashMap,
@@ -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) -> 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 de70363da2..cfc872832a 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
     let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
-        let b = u.b;
+        let b = u.b;
         match u {
-            Union { b: 0 } => (),
-            Union { a } => (),
+            Union { b: 0 } => (),
+            Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
         let y = *(x);

From 6cde0b1aa0f6b8623c6b81b2396f4a0345891233 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
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 0c565e89af..537322a72c 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 307b336f20..e392130ab6 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 {
+        self.imp.resolve_extern_crate(extern_crate)
+    }
+
     pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option {
         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 {
+        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 {
         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 {
+        Some(Crate { id: self.resolver.krate()? })
+    }
+
     /// Note: `FxHashSet` 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 {
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index 0fdf8e9a58..b5ff9fa2de 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 {
+        None
+    }
+}
+
 impl ShortLabel for ast::TypeAlias {
     fn short_label(&self) -> Option {
         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 4e3f428fae..b44b6fe22f 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 aa48cb412f..a632ea6a20 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 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 {
                     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 cf456630a5..9dd228b9c3 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,
 ) -> Option> {
     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::(&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 89efe71dab..ec442bcd83 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 b9b3580223..b8d60bdc63 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 0000000000..800d894c76
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_extern_crate.html
@@ -0,0 +1,40 @@
+
+
+
extern crate std;
+extern crate alloc as abc;
+
\ 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 b51000b03f..e2a4f2983c 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 { - 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 { + 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 { + 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, 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, 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 { + 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 for Definition { diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index 1fba71ff85..9e040973b3 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 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 ceddb2b056..d69f7941d8 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 (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::(|s, ()| Ok(s.fetch_workspaces()))? .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 970fedb378..49d2d1c6fb 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -64,7 +64,8 @@ export async function sendRequestWithRetry( param: TParam, token?: vscode.CancellationToken, ): Promise { - 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( 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 Date: Sun, 9 Aug 2020 21:33:14 +0800 Subject: [PATCH 15/76] format in to_proto::markup_content Signed-off-by: JmPotato --- 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 aa48cb412f..385e3e64e3 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 5eba1f1555..27460db78c 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 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 d69f7941d8..eb7c96933c 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 (from() guarantees lossless conversion) From 7252babc136b351d5011a78878607c7aaeea5ed8 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb 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 18948cb3c4..bd7a150f0a 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 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 bd7a150f0a..f5db55b8cc 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 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 54d5678d14..0de6fdf3fb 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 27cdabea03..44456e49e2 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 { + 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 b44b6fe22f..45389fd23f 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 a632ea6a20..f6e493817b 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 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 9dd228b9c3..453985de3e 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, ) -> Option> { 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::(&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 e2a4f2983c..9bb95277d0 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 { 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 { - 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, 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 { - 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 721d41a58f..0d386841e5 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 4ac8c8772e..f17fc5dfe9 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 Date: Mon, 10 Aug 2020 14:02:40 +0800 Subject: [PATCH 20/76] Show const body in short_label Signed-off-by: JmPotato --- 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 b5ff9fa2de..d8acb3be76 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 { - 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 9c7348898a..f66f62bfb5 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 Date: Mon, 10 Aug 2020 17:51:45 +0800 Subject: [PATCH 21/76] Better codes Signed-off-by: JmPotato --- 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 d8acb3be76..010c34705c 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 { - 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 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 21a458d477..bd83895f78 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 7c38f5ef9d..36e0e085ac 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), + CheckEvent(Option>), } 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) { @@ -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 0f33907c0a..c1b58712c1 100644 --- a/crates/ra_arena/src/map.rs +++ b/crates/ra_arena/src/map.rs @@ -13,18 +13,18 @@ pub struct ArenaMap { impl ArenaMap, V> { pub fn insert(&mut self, id: Idx, 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) -> 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) -> 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 { @@ -39,7 +39,7 @@ impl ArenaMap, V> { self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?))) } - fn to_idx(id: Idx) -> usize { + fn into_idx(id: Idx) -> usize { u32::from(id.into_raw()) as usize } @@ -51,7 +51,7 @@ impl ArenaMap, V> { impl std::ops::Index> for ArenaMap, T> { type Output = T; fn index(&self, id: Idx) -> &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 f9e515b811..933a3a3b5e 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 0b01d3bc64..ca6569c9f2 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 5bcdacb487..37dd3f4965 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 25554f583e..d68791cf1f 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 { - 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; + + 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 8faf1cc679..20c3f5eabf 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 b65875c96e..00bfcd29ed 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 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 e9196fcd2f..9f11268eee 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 { 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 cafad8070d..af9d63b06e 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 { 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 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 44456e49e2..6f9c56d294 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 { 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 6cb56a1cd0..6d59c86428 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, + pub repr: Option, } #[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 { + 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 { + 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 { 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 { 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 6b7874460a..0cab684eb4 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 09062c38e7..f2c078d347 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 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 6f9c56d294..0007d7fa88 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 { + db.struct_data(self.id).repr.clone() } fn variant_data(self, db: &dyn HirDatabase) -> Arc { @@ -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 0cab684eb4..b82b51efda 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 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 b82b51efda..c5098189b3 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 f2c078d347..c408058501 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 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 c5098189b3..9e8419c5f8 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 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 6d59c86428..4ba6944805 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 { 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 9e8419c5f8..a4a7aa2280 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 c408058501..a7f5ad8622 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 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 0007d7fa88..a880fa6713 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 { + db.function_data(self.id).self_param } pub fn params(self, db: &dyn HirDatabase) -> Vec { diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 88a8ef9bff..2a26b0183e 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, 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 a67e75dac0..1eaea66e4a 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, pub is_unsafe: bool, pub params: Box<[TypeRef]>, pub is_varargs: bool, @@ -508,6 +508,12 @@ pub struct Function { pub ast_id: FileAstId, } +#[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 450ef87981..89ad91d376 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 fb4b30a131..79c5adf0f1 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 5326652852..5488db43f7 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 d9a0ef167d..e3ba7ebc47 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 9a94ff4767..fc3d1a4bda 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, ) { - 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 a4a7aa2280..454fef39c6 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 = (|| { + 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, + db: &RootDatabase, + def: Definition, + name_ref: Option, + 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 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 = (|| { + 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 { 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 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 a7f5ad8622..a8087635a8 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 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 454fef39c6..02b16b13c0 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, + 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, 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 { let mut h = Highlight::new(HighlightTag::Function); - let modifier: Option = (|| { - 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 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 a880fa6713..0007d7fa88 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 { - 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 { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 31f3241c9e..34b02c5365 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 2a26b0183e..88a8ef9bff 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, + 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 1eaea66e4a..a67e75dac0 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, + 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, } -#[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 89ad91d376..450ef87981 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 79c5adf0f1..fb4b30a131 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 5488db43f7..5326652852 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 e3ba7ebc47..d9a0ef167d 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 fc3d1a4bda..9a94ff4767 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, ) { - 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 02b16b13c0..d5a5f69cca 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 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 e392130ab6..1072b39718 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 4ba6944805..35c3a91402 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 { - 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 { @@ -93,11 +77,8 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } 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 0000000000..0e04c0b607 --- /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, + pub signature: String, + pub active_parameter: Option, + parameters: Vec, +} + +impl CallInfo { + pub fn parameter_labels(&self) -> impl Iterator + '_ { + 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 { + 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, + token: SyntaxToken, +) -> Option<(hir::Callable, Option)> { + // 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(¯o_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 { + 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, token: SyntaxToken) -> Option { + 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 { + 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 { + 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 { + 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 { + 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::>() + .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 + (, 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 + (, 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, ) + "#]], + ); + 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, ) + "#]], + ); + } + + #[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 + (, y: u32) + "#]], + ); + } + + #[test] + fn test_fn_signature_two_args_first_generics() { + check( + r#" +fn foo(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 + (, y: {unknown}) + "#]], + ); + } + + #[test] + fn test_fn_signature_no_params() { + check( + r#" +fn foo() -> 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) + () + "#]], + ); + } + + #[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) + (, 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 + () + "#]], + ); + } + + #[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 + () + "##]], + ); + } + + #[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 + () + "##]], + ); + } + + #[test] + fn test_fn_signature_with_docs_from_actix() { + check( + r#" +struct WriteHandler; + +impl WriteHandler { + /// 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}) + () + "#]], + ); + } + + #[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, ) + "#]], + ); + } + + #[test] + fn generic_struct() { + check( + r#" +struct S(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) + () + "#]], + ); + } + + #[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 + () + "#]], + ) + } + + #[test] + fn call_info_for_fn_ptr() { + check( + r#" +fn main(f: fn(i32, f64) -> char) { + f(0, <|>) +} + "#, + expect![[r#" + (i32, f64) -> char + (i32, ) + "#]], + ) + } +} diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index d5a5f69cca..cf93205b6e 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 = (|| { 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 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 6322d404fb..46c1e0a11f 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 /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html index 18addd00d2..60c3943994 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; } -
fn fixture(ra_fixture: &str) {}
+
fn fixture(ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 79409fe816..454ff6d5f8 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
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
-    unsafe fn unsafe_method(&self) {}
+    unsafe fn unsafe_method(&self) {}
 }
 
 struct TypeForStaticMut {
@@ -55,9 +55,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
 fn main() {
-    let x = &5 as *const usize;
+    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 {
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 8e0160eee5..678cf9bd33 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
 }
 
 trait Bar {
-    fn bar(&self) -> i32;
+    fn bar(&self) -> i32;
 }
 
 impl Bar for Foo {
-    fn bar(&self) -> i32 {
+    fn bar(&self) -> i32 {
         self.x
     }
 }
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         self.x
     }
 
-    fn qux(&mut self) {
+    fn qux(&mut self) {
         self.x = 0;
     }
 }
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     noop!(noop!(1));
 
     let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let y = &mut x;
+    let z = &y;
 
     let Foo { x: z, y } = Foo { x: z, y };
 

From 87cb09365cf841b559e76951eedb826f2d4d3dfd Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
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 0e04c0b607..0000000000
--- 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,
-    pub signature: String,
-    pub active_parameter: Option,
-    parameters: Vec,
-}
-
-impl CallInfo {
-    pub fn parameter_labels(&self) -> impl Iterator + '_ {
-        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 {
-    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,
-    token: SyntaxToken,
-) -> Option<(hir::Callable, Option)> {
-    // 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(¯o_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 {
-        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, token: SyntaxToken) -> Option {
-        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 {
-        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 {
-        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 {
-        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 {
-        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::>()
-                    .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
-                (, 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
-                (, 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, )
-            "#]],
-        );
-        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, )
-            "#]],
-        );
-    }
-
-    #[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
-                (, y: u32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_first_generics() {
-        check(
-            r#"
-fn foo(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
-                (, y: {unknown})
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_no_params() {
-        check(
-            r#"
-fn foo() -> 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)
-                ()
-            "#]],
-        );
-    }
-
-    #[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)
-                (, 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
-                ()
-            "#]],
-        );
-    }
-
-    #[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
-                ()
-            "##]],
-        );
-    }
-
-    #[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
-                ()
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_from_actix() {
-        check(
-            r#"
-struct WriteHandler;
-
-impl WriteHandler {
-    /// 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})
-                ()
-            "#]],
-        );
-    }
-
-    #[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, )
-            "#]],
-        );
-    }
-
-    #[test]
-    fn generic_struct() {
-        check(
-            r#"
-struct S(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)
-                ()
-            "#]],
-        );
-    }
-
-    #[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
-                ()
-            "#]],
-        )
-    }
-
-    #[test]
-    fn call_info_for_fn_ptr() {
-        check(
-            r#"
-fn main(f: fn(i32, f64) -> char) {
-    f(0, <|>)
-}
-        "#,
-            expect![[r#"
-                (i32, f64) -> char
-                (i32, )
-            "#]],
-        )
-    }
-}

From a6af0272f7bf129a3063cdd7096f685fc58438e6 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
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 1072b39718..f706a186e7 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 cf93205b6e..e29f65a784 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 = (|| {
-                        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,
-    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,
     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 {
             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 {
             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 46c1e0a11f..6322d404fb 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
     /// ```sh
     /// echo 1
     /// ```
-    pub fn foo(&self) -> bool {
+    pub fn foo(&self) -> bool {
         true
     }
 }
diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
index 60c3943994..18addd00d2 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; }
 
-
fn fixture(ra_fixture: &str) {}
+
fn fixture(ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 454ff6d5f8..a2df2c27e6 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
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
-    unsafe fn unsafe_method(&self) {}
+    unsafe fn unsafe_method(&self) {}
 }
 
 struct TypeForStaticMut {
@@ -55,7 +55,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
 fn main() {
-    let x = &5 as *const _ as *const usize;
+    let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
     unsafe {
         // unsafe fn and method calls
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 678cf9bd33..8e0160eee5 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
 }
 
 trait Bar {
-    fn bar(&self) -> i32;
+    fn bar(&self) -> i32;
 }
 
 impl Bar for Foo {
-    fn bar(&self) -> i32 {
+    fn bar(&self) -> i32 {
         self.x
     }
 }
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         self.x
     }
 
-    fn qux(&mut self) {
+    fn qux(&mut self) {
         self.x = 0;
     }
 }
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     noop!(noop!(1));
 
     let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let y = &mut x;
+    let z = &y;
 
     let Foo { x: z, y } = Foo { x: z, y };
 

From 39fdd41df4052cef5da4876067ae28615012476b Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
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 f706a186e7..9697c7082b 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 e29f65a784..4527885e93 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 {
             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 
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 9697c7082b..758d004098 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 
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 758d004098..872f5fa4c7 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 4527885e93..c62bb3f1ab 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 a2df2c27e6..552fea6689 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
 
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
+#[repr(packed)]
+struct Packed {
+    a: u16,
+}
+
+trait DoTheAutoref {
+    fn calls_autoref(&self);
+}
+
+impl DoTheAutoref for u16 {
+    fn calls_autoref(&self) {}
+}
+
 fn main() {
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
@@ -66,8 +79,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             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 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();
     }
 }
\ No newline at end of file From 72baf1acdd544c645fd69c16967b91be9e75371b Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria 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 c62bb3f1ab..c10e15db8b 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 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 36e0e085ac..ec769459c1 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -106,9 +106,10 @@ struct FlycheckActor { cargo_handle: Option, } +#[allow(clippy::large_enum_variant)] enum Event { Restart(Restart), - CheckEvent(Option>), + CheckEvent(Option), } 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) { @@ -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 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 dc49fc4bdc..a094ec4f7a 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 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 0e592ac1be..658a50d150 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, pub(crate) semantic_tokens_cache: Arc>>, pub(crate) vfs: Arc)>>, + 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 0ac6434dd7..e6cf46df23 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::(|s, ()| Ok(s.fetch_workspaces()))? .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? - .on_sync::(|_, ()| Ok(()))? + .on_sync::(|s, ()| { + s.shutdown_requested = true; + Ok(()) + })? .on_sync::(|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?= 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 d4bc4b07c4..f1bcdc4aff 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 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 67813a9c07..51cf716b3d 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 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 7c38f5ef9d..31e14246de 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 Date: Tue, 11 Aug 2020 10:55:26 +0800 Subject: [PATCH 45/76] Address some FIXMEs Signed-off-by: JmPotato --- 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 a094ec4f7a..936e863f52 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 bd2905f080..a436e861d7 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 15ec75c956..02c4a4baeb 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 507646cc80..890996a68d 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, + label: String, + group: Option, /// 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, 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 { + 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 895af1dd78..c2afcf192d 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(¶ms.id, ':').unwrap(); let index = index.parse::().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 27460db78c..62fda8a1f2 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 { 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 Date: Tue, 11 Aug 2020 12:09:11 +0800 Subject: [PATCH 46/76] Typo fix Signed-off-by: JmPotato --- 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 18fcb90498..e738364220 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 ` ` 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 Date: Tue, 11 Aug 2020 14:35:15 +0800 Subject: [PATCH 47/76] Remove redundant dependencies Signed-off-by: JmPotato --- 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 936e863f52..a094ec4f7a 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 a436e861d7..bd2905f080 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 02c4a4baeb..6c92124eda 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?= 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 6b03b30bb5..4aa761148d 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 Date: Tue, 11 Aug 2020 16:50:45 +0800 Subject: [PATCH 49/76] Typo fix Signed-off-by: JmPotato --- 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 6b482a60c5..aefe47bd32 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, - // 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 2e8d636917..abae498d82 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 Date: Tue, 11 Aug 2020 17:19:02 +0800 Subject: [PATCH 50/76] Revert some FIXMEs Signed-off-by: JmPotato --- 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 6c92124eda..07c978378a 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 abae498d82..8bb735fc62 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 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 73c0b82754..5c8ea46abb 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::(|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::(|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 0fede0d879..45a4b2421e 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, + pub fix: Option<(Fix, TextRange)>, } #[derive(Debug)] diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index c2afcf192d..144c641b2a 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 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 84ba97b14a..e889f070fa 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(&self) -> Option<&D> { diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 977c0525b5..a5b00ed485 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, pub missed_fields: Vec, + pub list_parent_path: Option>, +} + +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 { + 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 95bbf2d955..3c37fc58e9 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 5c8ea46abb..7ae4bda0b8 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 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 1aefa79cc3..a19dbf33f6 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 e889f070fa..ffeca5e827 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; + fn highlighting_source(&self) -> InFile { + 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(&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 a5b00ed485..73d2414343 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>, } -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 { - 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 { 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 { + 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 7ae4bda0b8..e847df6ea9 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -69,7 +69,6 @@ pub(crate) fn diagnostics( }) }) .on::(|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 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 e392130ab6..1c5dc3d510 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 30db48f868..e532695895 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 { + fn fix_source(&self) -> InFile { 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 ffeca5e827..074a8c45e8 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; - fn highlighting_source(&self) -> InFile { - self.source() + fn fix_source(&self) -> InFile; + fn source(&self) -> InFile { + 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 73d2414343..a4cede81dc 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } + fn source(&self) -> InFile { + 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 { - 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { 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 { + fn fix_source(&self) -> InFile { 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> = 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 e847df6ea9..0d2ff17e1f 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::(|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::(|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::(|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 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 a4cede81dc..48b578fb0e 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, + pub field_list_parent_path: Option>, pub missed_fields: Vec, - pub list_parent_path: Option>, } impl Diagnostic for MissingFields { @@ -76,7 +76,7 @@ impl Diagnostic for MissingFields { } fn source(&self) -> InFile { - 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, + pub field_list_parent_path: Option>, pub missed_fields: Vec, } @@ -114,6 +115,12 @@ impl Diagnostic for MissingPatFields { fn fix_source(&self) -> InFile { InFile { file_id: self.file, value: self.field_list.clone().into() } } + fn source(&self) -> InFile { + 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 3c37fc58e9..98959ab684 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 507edcb7de..deca244dbb 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 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 074a8c45e8..23f28a7f71 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(&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 48b578fb0e..9d29f30716 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> = 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 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 e532695895..30db48f868 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 { + fn source(&self) -> InFile { 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 23f28a7f71..90a3b87f96 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; - fn source(&self) -> InFile { - self.fix_source() + /// A source to be used in highlighting and other visual representations + fn source(&self) -> InFile; + /// A source to be used during the fix application + fn fix_source(&self) -> InFile { + 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(&self) -> Option<&D> { diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 9d29f30716..efca096199 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 { + fn source(&self) -> InFile { 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 { + fn source(&self) -> InFile { 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 { + fn source(&self) -> InFile { 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 { + fn source(&self) -> InFile { 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 { + fn source(&self) -> InFile { 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 { + fn source(&self) -> InFile { 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 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 1c5dc3d510..b4420d3785 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(&self, d: &T) -> ::AST { - let file_id = d.source().file_id; + pub fn diagnostic_fix_source( + &self, + d: &T, + ) -> ::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 { @@ -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 + '_ { @@ -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 30db48f868..be96128465 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 { + fn presentation(&self) -> InFile { 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 90a3b87f96..2b74473cec 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; - /// A source to be used during the fix application - fn fix_source(&self) -> InFile { - self.source() - } + fn presentation(&self) -> InFile; 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(&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 efca096199..1e3a446375 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 { + fn presentation(&self) -> InFile { 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, + pub field_list_parent: AstPtr, pub field_list_parent_path: Option>, pub missed_fields: Vec, } @@ -71,15 +71,16 @@ impl Diagnostic for MissingFields { } buf } - fn fix_source(&self) -> InFile { - InFile { file_id: self.file, value: self.field_list.clone().into() } - } - fn source(&self) -> InFile { - 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 { + 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, + pub field_list_parent: AstPtr, pub field_list_parent_path: Option>, pub missed_fields: Vec, } @@ -112,14 +113,13 @@ impl Diagnostic for MissingPatFields { } buf } - fn fix_source(&self) -> InFile { - InFile { file_id: self.file, value: self.field_list.clone().into() } - } - fn source(&self) -> InFile { - self.field_list_parent_path + fn presentation(&self) -> InFile { + 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 { + fn presentation(&self) -> InFile { 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 { + fn presentation(&self) -> InFile { 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 { + fn presentation(&self) -> InFile { 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 { + fn presentation(&self) -> InFile { 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 { + fn presentation(&self) -> InFile { 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> = 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 98959ab684..51adcecafa 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 0d2ff17e1f..55593a8cb8 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::(|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::(|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::(|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 { - 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 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 266b513dcf..564f6a5db2 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 b4420d3785..c5bc2baffe 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( + pub fn diagnostic_fix_source( &self, d: &T, - ) -> ::AST { + ) -> Option<::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 be96128465..033be683c4 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 { + 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 2b74473cec..62a09a73ae 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(&self) -> Option<&D> { - self.as_any().downcast_ref() - } + fn fix_source(&self, db: &dyn AstDatabase) -> Option; } pub struct DiagnosticSink<'a> { @@ -83,12 +77,9 @@ impl<'a> DiagnosticSinkBuilder<'a> { self } - pub fn on(mut self, mut cb: F) -> Self { - let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::() { - Some(d) => { - cb(d); - Ok(()) - } + pub fn on Option<()> + 'a>(mut self, mut cb: F) -> Self { + let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::() { + 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 1e3a446375..b34ba5bfc8 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 { + 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 { + 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 { + 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 55593a8cb8..043ce357b9 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::(|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::(|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::(|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 { - 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 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 564f6a5db2..363164b9b4 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 c5bc2baffe..e9f7a033c5 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( - &self, - d: &T, - ) -> Option<::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 { diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 033be683c4..9435c72544 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 { - 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 62a09a73ae..8358c488b8 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; -} - pub struct DiagnosticSink<'a> { callbacks: Vec Result<(), ()> + 'a>>, filters: Vec bool + 'a>>, diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index b34ba5bfc8..24435e8a71 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 { - 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 { - 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 { - 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 043ce357b9..ca1a7c1aae 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::(|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( + sema: &Semantics, + d: &T, +) -> Option<::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, usage_file_id: FileId, d: &hir::diagnostics::NoSuchField, ) -> Option { - 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 0000000000..8578a90ec0 --- /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; +} + +impl DiagnosticWithFix for UnresolvedModule { + type AST = ast::Module; + fn fix_source(&self, db: &dyn AstDatabase) -> Option { + 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 { + 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 { + 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 { + 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 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 e9f7a033c5..2dfe69039f 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 { 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 8358c488b8..e58defa681 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 Option<()> + 'a>(mut self, mut cb: F) -> Self { + pub fn on(mut self, mut cb: F) -> Self { let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::() { - 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 ca1a7c1aae..165ff5249c 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::(|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::(|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::(|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::(|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( +fn diagnostic_with_fix( + d: &D, sema: &Semantics, - d: &T, -) -> Option<::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, - usage_file_id: FileId, - d: &hir::diagnostics::NoSuchField, -) -> Option { - 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 { - 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 8578a90ec0..56d454ac61 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; + fn fix(&self, sema: &Semantics) -> Option<(Fix, TextRange)>; } impl DiagnosticWithFix for UnresolvedModule { - type AST = ast::Module; - fn fix_source(&self, db: &dyn AstDatabase) -> Option { - let root = db.parse_or_expand(self.file)?; - Some(self.decl.to_node(&root)) + fn fix(&self, sema: &Semantics) -> 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 { - let root = db.parse_or_expand(self.file)?; - Some(self.field.to_node(&root)) + fn fix(&self, sema: &Semantics) -> 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) -> 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 { - 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 { - let root = db.parse_or_expand(self.file)?; - Some(self.expr.to_node(&root)) + fn fix(&self, sema: &Semantics) -> 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, + usage_file_id: FileId, + record_expr_field: &ast::RecordExprField, +) -> Option { + 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 { + 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 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 e58defa681..cc7dc3af22 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; 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 56d454ac61..1955e15210 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) -> Option<(Fix, TextRange)>; } From 188ec3459e795732ad097758f7bf6b6b95bdbf5e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov 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 165ff5249c..757b76fd40 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: &D, - sema: &Semantics, -) -> Diagnostic { +fn diagnostic_with_fix(d: &D, sema: &Semantics) -> 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 1955e15210..57b54a61ed 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) -> Option<(Fix, TextRange)>; +/// A [Diagnostic] that potentially has a fix available. +/// +/// [Diagnostic]: hir::diagnostics::Diagnostic +pub trait DiagnosticWithFix: Diagnostic { + fn fix(&self, sema: &Semantics) -> Option; } impl DiagnosticWithFix for UnresolvedModule { - fn fix(&self, sema: &Semantics) -> Option<(Fix, TextRange)> { - let fix = Fix::new( + fn fix(&self, sema: &Semantics) -> Option { + 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) -> Option<(Fix, TextRange)> { + fn fix(&self, sema: &Semantics) -> Option { 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) -> Option<(Fix, TextRange)> { + fn fix(&self, sema: &Semantics) -> Option { // 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) -> Option<(Fix, TextRange)> { + fn fix(&self, sema: &Semantics) -> Option { 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, 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 { match field_def_list { diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 45a4b2421e..89fcb6f178 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, } #[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, source_change: SourceChange) -> Self { + pub fn new( + label: impl Into, + 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 144c641b2a..785dd2a267 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 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 2dfe69039f..e3c417b41b 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 + '_ { @@ -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 9435c72544..71d177070d 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 { + fn display_source(&self) -> InFile { 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 cc7dc3af22..b138500e73 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; + /// Used in highlighting and related purposes + fn display_source(&self) -> InFile; 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 24435e8a71..7ab7f79db6 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 { + fn display_source(&self) -> InFile { InFile::new(self.file, self.field.clone().into()) } @@ -63,7 +63,7 @@ impl Diagnostic for MissingFields { buf } - fn presentation(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self @@ -95,13 +95,15 @@ impl Diagnostic for MissingPatFields { } buf } - fn presentation(&self) -> InFile { - 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 { + 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 { + fn display_source(&self) -> InFile { 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 { + fn display_source(&self) -> InFile { 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 { + fn display_source(&self) -> InFile { 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 { + fn display_source(&self) -> InFile { 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 { + fn display_source(&self) -> InFile { 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> = 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 757b76fd40..1046d7ab37 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 57b54a61ed..f7c73773f3 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 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 28d8f78768..4368e4eec8 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 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 d797f2cc96..d2487acc3b 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 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 d0cb62ef01..d3d62debfd 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 { 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 { 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 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 2acd440122..f977c88bee 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 a094ec4f7a..2658891625 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 c2ecbd33cf..83390212ae 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 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 e1c25a838f..51eaf7af65 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 d65a2acbca..ca23010213 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 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 ca23010213..4ff72865e8 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 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 ec769459c1..7c38f5ef9d 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -106,7 +106,6 @@ struct FlycheckActor { cargo_handle: Option, } -#[allow(clippy::large_enum_variant)] enum Event { Restart(Restart), CheckEvent(Option), diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs index c1b58712c1..0f33907c0a 100644 --- a/crates/ra_arena/src/map.rs +++ b/crates/ra_arena/src/map.rs @@ -13,18 +13,18 @@ pub struct ArenaMap { impl ArenaMap, V> { pub fn insert(&mut self, id: Idx, 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) -> 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) -> 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 { @@ -39,7 +39,7 @@ impl ArenaMap, V> { self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?))) } - fn into_idx(id: Idx) -> usize { + fn to_idx(id: Idx) -> usize { u32::from(id.into_raw()) as usize } @@ -51,7 +51,7 @@ impl ArenaMap, V> { impl std::ops::Index> for ArenaMap, T> { type Output = T; fn index(&self, id: Idx) -> &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 ca6569c9f2..0b01d3bc64 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 9f11268eee..e9196fcd2f 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 { 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 af9d63b06e..cafad8070d 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 { 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 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 de701f8b83..3ac4aed7d2 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 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 c2e1d701e2..88468bc971 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 51eaf7af65..3291e3f146 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 cca524ceaa..9b76234345 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 ef9c8ff5b0..751ce65f2d 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 51ad2a846f..0000000000 --- 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 80f15474a5..0000000000 --- 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 acd72094b9..0000000000 --- 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 15ba8f4a85..0000000000 --- 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 b8d26a53a5..0000000000 --- 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 8f5d611139..0000000000 --- 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 1269621dc2..0000000000 --- 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 6bfe925af2..0000000000 --- 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 ba0998ff4d..0000000000 --- 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 0000000000..f2e2014605 --- /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 12926cd8a1..96340f84ab 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 0000000000..9282772f34 --- /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 0000000000..a6836cbd57 --- /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 6246a31a66..0000000000 --- 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 dfb3b92dca..0000000000 --- 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 0000000000..e9b57ec3b3 --- /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 0000000000..e443e3495e --- /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 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 9b76234345..d091b0fbb2 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 625ab4c2d9..0000000000 --- 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 04e021550d..0000000000 --- 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 293b1d64c7..0000000000 --- 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 1295c2cd22..0000000000 --- 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 d6dfa83b70..0000000000 --- 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 9cd6c57bd8..0000000000 --- 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 97548a5eeb..0000000000 --- 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 8c84d9cd7c..0000000000 --- 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 43c09affed..0000000000 --- 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 41055f41d9..0000000000 --- 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 405b6a259f..0000000000 --- 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 394a049f0f..0000000000 --- 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 0cac9ac431..0000000000 --- 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 72adf60351..0000000000 --- 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 0ef11c6825..0000000000 --- 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 03d29f3241..0000000000 --- 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 0a1b21d6e6..0000000000 --- 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 ef6aa84a29..0000000000 --- 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 32a77ba490..0000000000 --- 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 31a1e435f5..0000000000 --- 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 73c94e5d43..0000000000 --- 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 33cfc4cd7a..0000000000 --- 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 a7df188bd6..0000000000 --- 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 f4adcb62b3..0000000000 --- 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 98a20f36d6..0000000000 --- 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 1262871453..0000000000 --- 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 0000000000..23c4269b30 --- /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 0000000000..8f3b7ef112 --- /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 0000000000..7968cf9ffa --- /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 0000000000..a1a550d8a6 --- /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 0000000000..9881e5048c --- /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 0000000000..8d183dbb5d --- /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 5524efaafe..0000000000 --- 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 e929eef741..0000000000 --- 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 0000000000..50a6d8ee9a --- /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 0000000000..8d697c04b9 --- /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 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 b67438b6ba..ebdf00e676 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::>(); 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 d9b87c9c0d..7162dc1848 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 b84aa24b6c..32dfed274a 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 9a94ff4767..59f1b14246 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 8665b480fd..6046643ef1 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 3a56b1674c..0bdc22d953 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 00bfcd29ed..3c5027fe57 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 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>, -} - -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 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 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 f977c88bee..f46fb8fecc 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 }}