aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock37
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs61
-rw-r--r--crates/assists/src/handlers/generate_getter.rs8
-rw-r--r--crates/assists/src/handlers/generate_getter_mut.rs8
-rw-r--r--crates/assists/src/handlers/generate_impl.rs70
-rw-r--r--crates/assists/src/handlers/generate_setter.rs8
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs5
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs26
-rw-r--r--crates/assists/src/utils.rs52
-rw-r--r--crates/ide/src/annotations.rs40
-rw-r--r--crates/ide/src/references.rs305
-rw-r--r--crates/ide/src/references/rename.rs179
-rw-r--r--crates/ide_db/src/defs.rs14
-rw-r--r--crates/project_model/src/cargo_workspace.rs11
-rw-r--r--crates/project_model/src/lib.rs4
-rw-r--r--crates/project_model/src/sysroot.rs40
-rw-r--r--crates/project_model/src/workspace.rs13
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs1
-rw-r--r--crates/rust-analyzer/src/caps.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs15
-rw-r--r--crates/rust-analyzer/src/handlers.rs58
-rw-r--r--crates/rust-analyzer/src/line_endings.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs119
-rw-r--r--crates/syntax/src/ast/node_ext.rs5
-rw-r--r--docs/user/generated_config.adoc2
-rw-r--r--editors/code/package-lock.json36
-rw-r--r--editors/code/package.json8
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--editors/code/src/main.ts6
30 files changed, 692 insertions, 448 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 99b7ccf69..f393cbfd7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -99,12 +99,6 @@ dependencies = [
99] 99]
100 100
101[[package]] 101[[package]]
102name = "base64"
103version = "0.12.3"
104source = "registry+https://github.com/rust-lang/crates.io-index"
105checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
106
107[[package]]
108name = "base_db" 102name = "base_db"
109version = "0.0.0" 103version = "0.0.0"
110dependencies = [ 104dependencies = [
@@ -381,9 +375,9 @@ dependencies = [
381 375
382[[package]] 376[[package]]
383name = "env_logger" 377name = "env_logger"
384version = "0.8.2" 378version = "0.8.3"
385source = "registry+https://github.com/rust-lang/crates.io-index" 379source = "registry+https://github.com/rust-lang/crates.io-index"
386checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" 380checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
387dependencies = [ 381dependencies = [
388 "log", 382 "log",
389] 383]
@@ -406,7 +400,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
406dependencies = [ 400dependencies = [
407 "cfg-if", 401 "cfg-if",
408 "libc", 402 "libc",
409 "redox_syscall 0.2.4", 403 "redox_syscall",
410 "winapi", 404 "winapi",
411] 405]
412 406
@@ -840,11 +834,10 @@ dependencies = [
840 834
841[[package]] 835[[package]]
842name = "lsp-types" 836name = "lsp-types"
843version = "0.86.0" 837version = "0.87.0"
844source = "registry+https://github.com/rust-lang/crates.io-index" 838source = "registry+https://github.com/rust-lang/crates.io-index"
845checksum = "f2a5c40d566f2704dac30859bca152217583fc94fd5b178d8baba915e1abd382" 839checksum = "05ee10a6da8f7ca2eb2881258d23ef3576da85cbd19b314da8e9028eb68eedf0"
846dependencies = [ 840dependencies = [
847 "base64",
848 "bitflags", 841 "bitflags",
849 "serde", 842 "serde",
850 "serde_json", 843 "serde_json",
@@ -1034,14 +1027,14 @@ dependencies = [
1034 1027
1035[[package]] 1028[[package]]
1036name = "parking_lot_core" 1029name = "parking_lot_core"
1037version = "0.8.2" 1030version = "0.8.3"
1038source = "registry+https://github.com/rust-lang/crates.io-index" 1031source = "registry+https://github.com/rust-lang/crates.io-index"
1039checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" 1032checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
1040dependencies = [ 1033dependencies = [
1041 "cfg-if", 1034 "cfg-if",
1042 "instant", 1035 "instant",
1043 "libc", 1036 "libc",
1044 "redox_syscall 0.1.57", 1037 "redox_syscall",
1045 "smallvec", 1038 "smallvec",
1046 "winapi", 1039 "winapi",
1047] 1040]
@@ -1238,9 +1231,9 @@ dependencies = [
1238 1231
1239[[package]] 1232[[package]]
1240name = "quote" 1233name = "quote"
1241version = "1.0.8" 1234version = "1.0.9"
1242source = "registry+https://github.com/rust-lang/crates.io-index" 1235source = "registry+https://github.com/rust-lang/crates.io-index"
1243checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 1236checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
1244dependencies = [ 1237dependencies = [
1245 "proc-macro2", 1238 "proc-macro2",
1246] 1239]
@@ -1272,15 +1265,9 @@ dependencies = [
1272 1265
1273[[package]] 1266[[package]]
1274name = "redox_syscall" 1267name = "redox_syscall"
1275version = "0.1.57" 1268version = "0.2.5"
1276source = "registry+https://github.com/rust-lang/crates.io-index"
1277checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
1278
1279[[package]]
1280name = "redox_syscall"
1281version = "0.2.4"
1282source = "registry+https://github.com/rust-lang/crates.io-index" 1269source = "registry+https://github.com/rust-lang/crates.io-index"
1283checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" 1270checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
1284dependencies = [ 1271dependencies = [
1285 "bitflags", 1272 "bitflags",
1286] 1273]
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
index f6febd3aa..d9388a737 100644
--- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,15 +1,9 @@
1use ast::GenericParamsOwner;
2use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
3use ide_db::RootDatabase; 2use ide_db::RootDatabase;
4use itertools::Itertools; 3use syntax::ast::{self, AstNode, NameOwner};
5use stdx::format_to;
6use syntax::{
7 ast::{self, AstNode, NameOwner},
8 SmolStr,
9};
10use test_utils::mark; 4use test_utils::mark;
11 5
12use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
13 7
14// Assist: generate_from_impl_for_enum 8// Assist: generate_from_impl_for_enum
15// 9//
@@ -31,8 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
31pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 25pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
32 let variant = ctx.find_node_at_offset::<ast::Variant>()?; 26 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
33 let variant_name = variant.name()?; 27 let variant_name = variant.name()?;
34 let enum_name = variant.parent_enum().name()?; 28 let enum_ = ast::Adt::Enum(variant.parent_enum());
35 let enum_type_params = variant.parent_enum().generic_param_list();
36 let (field_name, field_type) = match variant.kind() { 29 let (field_name, field_type) = match variant.kind() {
37 ast::StructKind::Tuple(field_list) => { 30 ast::StructKind::Tuple(field_list) => {
38 if field_list.fields().count() != 1 { 31 if field_list.fields().count() != 1 {
@@ -62,49 +55,27 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
62 target, 55 target,
63 |edit| { 56 |edit| {
64 let start_offset = variant.parent_enum().syntax().text_range().end(); 57 let start_offset = variant.parent_enum().syntax().text_range().end();
65 let mut buf = String::from("\n\nimpl"); 58 let from_trait = format!("From<{}>", field_type.syntax());
66 if let Some(type_params) = &enum_type_params { 59 let impl_code = if let Some(name) = field_name {
67 format_to!(buf, "{}", type_params.syntax()); 60 format!(
68 } 61 r#" fn from({0}: {1}) -> Self {{
69 format_to!(buf, " From<{}> for {}", field_type.syntax(), enum_name);
70 if let Some(type_params) = enum_type_params {
71 let lifetime_params = type_params
72 .lifetime_params()
73 .filter_map(|it| it.lifetime())
74 .map(|it| SmolStr::from(it.text()));
75 let type_params = type_params
76 .type_params()
77 .filter_map(|it| it.name())
78 .map(|it| SmolStr::from(it.text()));
79
80 let generic_params = lifetime_params.chain(type_params).format(", ");
81 format_to!(buf, "<{}>", generic_params)
82 }
83 if let Some(name) = field_name {
84 format_to!(
85 buf,
86 r#" {{
87 fn from({0}: {1}) -> Self {{
88 Self::{2} {{ {0} }} 62 Self::{2} {{ {0} }}
89 }} 63 }}"#,
90}}"#,
91 name.text(), 64 name.text(),
92 field_type.syntax(), 65 field_type.syntax(),
93 variant_name, 66 variant_name,
94 ); 67 )
95 } else { 68 } else {
96 format_to!( 69 format!(
97 buf, 70 r#" fn from(v: {}) -> Self {{
98 r#" {{
99 fn from(v: {}) -> Self {{
100 Self::{}(v) 71 Self::{}(v)
101 }} 72 }}"#,
102}}"#,
103 field_type.syntax(), 73 field_type.syntax(),
104 variant_name, 74 variant_name,
105 ); 75 )
106 } 76 };
107 edit.insert(start_offset, buf); 77 let from_impl = generate_trait_impl_text(&enum_, &from_trait, &impl_code);
78 edit.insert(start_offset, from_impl);
108 }, 79 },
109 ) 80 )
110} 81}
diff --git a/crates/assists/src/handlers/generate_getter.rs b/crates/assists/src/handlers/generate_getter.rs
index fbcf8b069..df7d1bb95 100644
--- a/crates/assists/src/handlers/generate_getter.rs
+++ b/crates/assists/src/handlers/generate_getter.rs
@@ -1,10 +1,9 @@
1use stdx::{format_to, to_lower_snake_case}; 1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::VisibilityOwner; 2use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3use syntax::ast::{self, AstNode, NameOwner};
4 3
5use crate::{ 4use crate::{
6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, 5 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 6 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
8}; 7};
9 8
10// Assist: generate_getter 9// Assist: generate_getter
@@ -42,7 +41,8 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
42 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 41 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
43 42
44 let target = field.syntax().text_range(); 43 let target = field.syntax().text_range();
45 acc.add( 44 acc.add_group(
45 &GroupLabel("Generate getter/setter".to_owned()),
46 AssistId("generate_getter", AssistKind::Generate), 46 AssistId("generate_getter", AssistKind::Generate),
47 "Generate a getter method", 47 "Generate a getter method",
48 target, 48 target,
diff --git a/crates/assists/src/handlers/generate_getter_mut.rs b/crates/assists/src/handlers/generate_getter_mut.rs
index bf0d99881..821c2eed5 100644
--- a/crates/assists/src/handlers/generate_getter_mut.rs
+++ b/crates/assists/src/handlers/generate_getter_mut.rs
@@ -1,10 +1,9 @@
1use stdx::{format_to, to_lower_snake_case}; 1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::VisibilityOwner; 2use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3use syntax::ast::{self, AstNode, NameOwner};
4 3
5use crate::{ 4use crate::{
6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, 5 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 6 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
8}; 7};
9 8
10// Assist: generate_getter_mut 9// Assist: generate_getter_mut
@@ -46,7 +45,8 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Opt
46 )?; 45 )?;
47 46
48 let target = field.syntax().text_range(); 47 let target = field.syntax().text_range();
49 acc.add( 48 acc.add_group(
49 &GroupLabel("Generate getter/setter".to_owned()),
50 AssistId("generate_getter_mut", AssistKind::Generate), 50 AssistId("generate_getter_mut", AssistKind::Generate),
51 "Generate a mut getter method", 51 "Generate a mut getter method",
52 target, 52 target,
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs
index 61d1bd25c..16a600e6f 100644
--- a/crates/assists/src/handlers/generate_impl.rs
+++ b/crates/assists/src/handlers/generate_impl.rs
@@ -1,11 +1,6 @@
1use itertools::Itertools; 1use syntax::ast::{self, AstNode, NameOwner};
2use stdx::format_to;
3use syntax::{
4 ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
5 SmolStr,
6};
7 2
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 3use crate::{utils::generate_impl_text, AssistContext, AssistId, AssistKind, Assists};
9 4
10// Assist: generate_impl 5// Assist: generate_impl
11// 6//
@@ -36,44 +31,15 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
36 format!("Generate impl for `{}`", name), 31 format!("Generate impl for `{}`", name),
37 target, 32 target,
38 |edit| { 33 |edit| {
39 let type_params = nominal.generic_param_list();
40 let start_offset = nominal.syntax().text_range().end(); 34 let start_offset = nominal.syntax().text_range().end();
41 let mut buf = String::new();
42 buf.push_str("\n\n");
43 nominal
44 .attrs()
45 .filter(|attr| {
46 attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)
47 })
48 .for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str()));
49
50 buf.push_str("impl");
51 if let Some(type_params) = &type_params {
52 format_to!(buf, "{}", type_params.syntax());
53 }
54 buf.push_str(" ");
55 buf.push_str(name.text());
56 if let Some(type_params) = type_params {
57 let lifetime_params = type_params
58 .lifetime_params()
59 .filter_map(|it| it.lifetime())
60 .map(|it| SmolStr::from(it.text()));
61 let type_params = type_params
62 .type_params()
63 .filter_map(|it| it.name())
64 .map(|it| SmolStr::from(it.text()));
65
66 let generic_params = lifetime_params.chain(type_params).format(", ");
67 format_to!(buf, "<{}>", generic_params)
68 }
69 match ctx.config.snippet_cap { 35 match ctx.config.snippet_cap {
70 Some(cap) => { 36 Some(cap) => {
71 buf.push_str(" {\n $0\n}"); 37 let snippet = generate_impl_text(&nominal, " $0");
72 edit.insert_snippet(cap, start_offset, buf); 38 edit.insert_snippet(cap, start_offset, snippet);
73 } 39 }
74 None => { 40 None => {
75 buf.push_str(" {\n}"); 41 let snippet = generate_impl_text(&nominal, "");
76 edit.insert(start_offset, buf); 42 edit.insert(start_offset, snippet);
77 } 43 }
78 } 44 }
79 }, 45 },
@@ -132,6 +98,30 @@ mod tests {
132 $0 98 $0
133 }"#, 99 }"#,
134 ); 100 );
101
102 check_assist(
103 generate_impl,
104 r#"
105 struct Defaulted<T = i32> {}$0"#,
106 r#"
107 struct Defaulted<T = i32> {}
108
109 impl<T> Defaulted<T> {
110 $0
111 }"#,
112 );
113
114 check_assist(
115 generate_impl,
116 r#"
117 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {}$0"#,
118 r#"
119 struct Defaulted<'a, 'b: 'a, T: Debug + Clone + 'a + 'b = String> {}
120
121 impl<'a, 'b: 'a, T: Debug + Clone + 'a + 'b> Defaulted<'a, 'b, T> {
122 $0
123 }"#,
124 );
135 } 125 }
136 126
137 #[test] 127 #[test]
diff --git a/crates/assists/src/handlers/generate_setter.rs b/crates/assists/src/handlers/generate_setter.rs
index b655f9b9c..288cf745d 100644
--- a/crates/assists/src/handlers/generate_setter.rs
+++ b/crates/assists/src/handlers/generate_setter.rs
@@ -1,10 +1,9 @@
1use stdx::{format_to, to_lower_snake_case}; 1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::VisibilityOwner; 2use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3use syntax::ast::{self, AstNode, NameOwner};
4 3
5use crate::{ 4use crate::{
6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, 5 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 6 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
8}; 7};
9 8
10// Assist: generate_setter 9// Assist: generate_setter
@@ -46,7 +45,8 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
46 )?; 45 )?;
47 46
48 let target = field.syntax().text_range(); 47 let target = field.syntax().text_range();
49 acc.add( 48 acc.add_group(
49 &GroupLabel("Generate getter/setter".to_owned()),
50 AssistId("generate_setter", AssistKind::Generate), 50 AssistId("generate_setter", AssistKind::Generate),
51 "Generate a setter method", 51 "Generate a setter method",
52 target, 52 target,
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index 8d28431cf..9b228443f 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,6 +1,5 @@
1use std::collections::HashMap;
2
3use ide_db::{defs::Definition, search::FileReference}; 1use ide_db::{defs::Definition, search::FileReference};
2use rustc_hash::FxHashMap;
4use syntax::{ 3use syntax::{
5 ast::{self, AstNode, AstToken}, 4 ast::{self, AstNode, AstToken},
6 TextRange, 5 TextRange,
@@ -111,7 +110,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
111 .collect::<Result<_, _>>() 110 .collect::<Result<_, _>>()
112 .map(|b| (file_id, b)) 111 .map(|b| (file_id, b))
113 }) 112 })
114 .collect::<Result<HashMap<_, Vec<_>>, _>>()?; 113 .collect::<Result<FxHashMap<_, Vec<_>>, _>>()?;
115 114
116 let init_str = initializer_expr.syntax().text().to_string(); 115 let init_str = initializer_expr.syntax().text().to_string();
117 let init_in_paren = format!("({})", &init_str); 116 let init_in_paren = format!("({})", &init_str);
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 6aa9d2f2c..c69bc5cac 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -2,8 +2,7 @@ use ide_db::helpers::mod_path_to_ast;
2use ide_db::imports_locator; 2use ide_db::imports_locator;
3use itertools::Itertools; 3use itertools::Itertools;
4use syntax::{ 4use syntax::{
5 ast::{self, make, AstNode}, 5 ast::{self, make, AstNode, NameOwner},
6 Direction,
7 SyntaxKind::{IDENT, WHITESPACE}, 6 SyntaxKind::{IDENT, WHITESPACE},
8 TextSize, 7 TextSize,
9}; 8};
@@ -11,7 +10,8 @@ use syntax::{
11use crate::{ 10use crate::{
12 assist_context::{AssistBuilder, AssistContext, Assists}, 11 assist_context::{AssistBuilder, AssistContext, Assists},
13 utils::{ 12 utils::{
14 add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods, 13 add_trait_assoc_items_to_impl, filter_assoc_items, generate_trait_impl_text,
14 render_snippet, Cursor, DefaultMethods,
15 }, 15 },
16 AssistId, AssistKind, 16 AssistId, AssistKind,
17}; 17};
@@ -57,8 +57,9 @@ pub(crate) fn replace_derive_with_manual_impl(
57 let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?; 57 let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?;
58 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); 58 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
59 59
60 let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; 60 let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
61 let insert_pos = annotated_name.syntax().parent()?.text_range().end(); 61 let annotated_name = adt.name()?;
62 let insert_pos = adt.syntax().text_range().end();
62 63
63 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 64 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
64 let current_crate = current_module.krate(); 65 let current_crate = current_module.krate();
@@ -82,10 +83,10 @@ pub(crate) fn replace_derive_with_manual_impl(
82 83
83 let mut no_traits_found = true; 84 let mut no_traits_found = true;
84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 85 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
85 add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?; 86 add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &adt, &annotated_name, insert_pos)?;
86 } 87 }
87 if no_traits_found { 88 if no_traits_found {
88 add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?; 89 add_assist(acc, ctx, &attr, &trait_path, None, &adt, &annotated_name, insert_pos)?;
89 } 90 }
90 Some(()) 91 Some(())
91} 92}
@@ -96,6 +97,7 @@ fn add_assist(
96 attr: &ast::Attr, 97 attr: &ast::Attr,
97 trait_path: &ast::Path, 98 trait_path: &ast::Path,
98 trait_: Option<hir::Trait>, 99 trait_: Option<hir::Trait>,
100 adt: &ast::Adt,
99 annotated_name: &ast::Name, 101 annotated_name: &ast::Name,
100 insert_pos: TextSize, 102 insert_pos: TextSize,
101) -> Option<()> { 103) -> Option<()> {
@@ -112,15 +114,15 @@ fn add_assist(
112 let impl_def_with_items = 114 let impl_def_with_items =
113 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); 115 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
114 update_attribute(builder, &input, &trait_name, &attr); 116 update_attribute(builder, &input, &trait_name, &attr);
117 let trait_path = format!("{}", trait_path);
115 match (ctx.config.snippet_cap, impl_def_with_items) { 118 match (ctx.config.snippet_cap, impl_def_with_items) {
116 (None, _) => builder.insert( 119 (None, _) => {
117 insert_pos, 120 builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
118 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), 121 }
119 ),
120 (Some(cap), None) => builder.insert_snippet( 122 (Some(cap), None) => builder.insert_snippet(
121 cap, 123 cap,
122 insert_pos, 124 insert_pos,
123 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), 125 generate_trait_impl_text(adt, &trait_path, " $0"),
124 ), 126 ),
125 (Some(cap), Some((impl_def, first_assoc_item))) => { 127 (Some(cap), Some((impl_def, first_assoc_item))) => {
126 let mut cursor = Cursor::Before(first_assoc_item.syntax()); 128 let mut cursor = Cursor::Before(first_assoc_item.syntax());
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 5dd32aef1..8418e6e12 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -2,6 +2,7 @@
2 2
3use std::ops; 3use std::ops;
4 4
5use ast::TypeBoundsOwner;
5use hir::{Adt, HasSource}; 6use hir::{Adt, HasSource};
6use ide_db::{helpers::SnippetCap, RootDatabase}; 7use ide_db::{helpers::SnippetCap, RootDatabase};
7use itertools::Itertools; 8use itertools::Itertools;
@@ -367,21 +368,56 @@ pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Opti
367// Generates the surrounding `impl Type { <code> }` including type and lifetime 368// Generates the surrounding `impl Type { <code> }` including type and lifetime
368// parameters 369// parameters
369pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String { 370pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
370 let type_params = adt.generic_param_list(); 371 generate_impl_text_inner(adt, None, code)
372}
373
374// Generates the surrounding `impl <trait> for Type { <code> }` including type
375// and lifetime parameters
376pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
377 generate_impl_text_inner(adt, Some(trait_text), code)
378}
379
380fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
381 let generic_params = adt.generic_param_list();
371 let mut buf = String::with_capacity(code.len()); 382 let mut buf = String::with_capacity(code.len());
372 buf.push_str("\n\nimpl"); 383 buf.push_str("\n\n");
373 if let Some(type_params) = &type_params { 384 adt.attrs()
374 format_to!(buf, "{}", type_params.syntax()); 385 .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
386 .for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str()));
387 buf.push_str("impl");
388 if let Some(generic_params) = &generic_params {
389 let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
390 let type_params = generic_params.type_params().map(|type_param| {
391 let mut buf = String::new();
392 if let Some(it) = type_param.name() {
393 format_to!(buf, "{}", it.syntax());
394 }
395 if let Some(it) = type_param.colon_token() {
396 format_to!(buf, "{} ", it);
397 }
398 if let Some(it) = type_param.type_bound_list() {
399 format_to!(buf, "{}", it.syntax());
400 }
401 buf
402 });
403 let generics = lifetimes.chain(type_params).format(", ");
404 format_to!(buf, "<{}>", generics);
375 } 405 }
376 buf.push(' '); 406 buf.push(' ');
407 if let Some(trait_text) = trait_text {
408 buf.push_str(trait_text);
409 buf.push_str(" for ");
410 }
377 buf.push_str(adt.name().unwrap().text()); 411 buf.push_str(adt.name().unwrap().text());
378 if let Some(type_params) = type_params { 412 if let Some(generic_params) = generic_params {
379 let lifetime_params = type_params 413 let lifetime_params = generic_params
380 .lifetime_params() 414 .lifetime_params()
381 .filter_map(|it| it.lifetime()) 415 .filter_map(|it| it.lifetime())
382 .map(|it| SmolStr::from(it.text())); 416 .map(|it| SmolStr::from(it.text()));
383 let type_params = 417 let type_params = generic_params
384 type_params.type_params().filter_map(|it| it.name()).map(|it| SmolStr::from(it.text())); 418 .type_params()
419 .filter_map(|it| it.name())
420 .map(|it| SmolStr::from(it.text()));
385 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) 421 format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", "))
386 } 422 }
387 423
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 414a60bed..2e8e82b70 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -57,19 +57,19 @@ pub(crate) fn annotations(
57 let action = runnable.action(); 57 let action = runnable.action();
58 let range = runnable.nav.full_range; 58 let range = runnable.nav.full_range;
59 59
60 if action.debugee && config.debug { 60 if config.run {
61 annotations.push(Annotation { 61 annotations.push(Annotation {
62 range, 62 range,
63 63
64 // FIXME: This one allocates without reason if run is enabled, but debug is disabled 64 // FIXME: This one allocates without reason if run is enabled, but debug is disabled
65 kind: AnnotationKind::Runnable { debug: true, runnable: runnable.clone() }, 65 kind: AnnotationKind::Runnable { debug: false, runnable: runnable.clone() },
66 }); 66 });
67 } 67 }
68 68
69 if config.run { 69 if action.debugee && config.debug {
70 annotations.push(Annotation { 70 annotations.push(Annotation {
71 range, 71 range,
72 kind: AnnotationKind::Runnable { debug: false, runnable }, 72 kind: AnnotationKind::Runnable { debug: true, runnable },
73 }); 73 });
74 } 74 }
75 } 75 }
@@ -199,7 +199,7 @@ fn main() {
199 Annotation { 199 Annotation {
200 range: 50..85, 200 range: 50..85,
201 kind: Runnable { 201 kind: Runnable {
202 debug: true, 202 debug: false,
203 runnable: Runnable { 203 runnable: Runnable {
204 nav: NavigationTarget { 204 nav: NavigationTarget {
205 file_id: FileId( 205 file_id: FileId(
@@ -218,7 +218,7 @@ fn main() {
218 Annotation { 218 Annotation {
219 range: 50..85, 219 range: 50..85,
220 kind: Runnable { 220 kind: Runnable {
221 debug: false, 221 debug: true,
222 runnable: Runnable { 222 runnable: Runnable {
223 nav: NavigationTarget { 223 nav: NavigationTarget {
224 file_id: FileId( 224 file_id: FileId(
@@ -303,7 +303,7 @@ fn main() {
303 Annotation { 303 Annotation {
304 range: 14..48, 304 range: 14..48,
305 kind: Runnable { 305 kind: Runnable {
306 debug: true, 306 debug: false,
307 runnable: Runnable { 307 runnable: Runnable {
308 nav: NavigationTarget { 308 nav: NavigationTarget {
309 file_id: FileId( 309 file_id: FileId(
@@ -322,7 +322,7 @@ fn main() {
322 Annotation { 322 Annotation {
323 range: 14..48, 323 range: 14..48,
324 kind: Runnable { 324 kind: Runnable {
325 debug: false, 325 debug: true,
326 runnable: Runnable { 326 runnable: Runnable {
327 nav: NavigationTarget { 327 nav: NavigationTarget {
328 file_id: FileId( 328 file_id: FileId(
@@ -411,7 +411,7 @@ fn main() {
411 Annotation { 411 Annotation {
412 range: 66..100, 412 range: 66..100,
413 kind: Runnable { 413 kind: Runnable {
414 debug: true, 414 debug: false,
415 runnable: Runnable { 415 runnable: Runnable {
416 nav: NavigationTarget { 416 nav: NavigationTarget {
417 file_id: FileId( 417 file_id: FileId(
@@ -430,7 +430,7 @@ fn main() {
430 Annotation { 430 Annotation {
431 range: 66..100, 431 range: 66..100,
432 kind: Runnable { 432 kind: Runnable {
433 debug: false, 433 debug: true,
434 runnable: Runnable { 434 runnable: Runnable {
435 nav: NavigationTarget { 435 nav: NavigationTarget {
436 file_id: FileId( 436 file_id: FileId(
@@ -572,7 +572,7 @@ fn main() {}
572 Annotation { 572 Annotation {
573 range: 0..12, 573 range: 0..12,
574 kind: Runnable { 574 kind: Runnable {
575 debug: true, 575 debug: false,
576 runnable: Runnable { 576 runnable: Runnable {
577 nav: NavigationTarget { 577 nav: NavigationTarget {
578 file_id: FileId( 578 file_id: FileId(
@@ -591,7 +591,7 @@ fn main() {}
591 Annotation { 591 Annotation {
592 range: 0..12, 592 range: 0..12,
593 kind: Runnable { 593 kind: Runnable {
594 debug: false, 594 debug: true,
595 runnable: Runnable { 595 runnable: Runnable {
596 nav: NavigationTarget { 596 nav: NavigationTarget {
597 file_id: FileId( 597 file_id: FileId(
@@ -645,7 +645,7 @@ fn main() {
645 Annotation { 645 Annotation {
646 range: 58..95, 646 range: 58..95,
647 kind: Runnable { 647 kind: Runnable {
648 debug: true, 648 debug: false,
649 runnable: Runnable { 649 runnable: Runnable {
650 nav: NavigationTarget { 650 nav: NavigationTarget {
651 file_id: FileId( 651 file_id: FileId(
@@ -664,7 +664,7 @@ fn main() {
664 Annotation { 664 Annotation {
665 range: 58..95, 665 range: 58..95,
666 kind: Runnable { 666 kind: Runnable {
667 debug: false, 667 debug: true,
668 runnable: Runnable { 668 runnable: Runnable {
669 nav: NavigationTarget { 669 nav: NavigationTarget {
670 file_id: FileId( 670 file_id: FileId(
@@ -787,7 +787,7 @@ mod tests {
787 Annotation { 787 Annotation {
788 range: 0..12, 788 range: 0..12,
789 kind: Runnable { 789 kind: Runnable {
790 debug: true, 790 debug: false,
791 runnable: Runnable { 791 runnable: Runnable {
792 nav: NavigationTarget { 792 nav: NavigationTarget {
793 file_id: FileId( 793 file_id: FileId(
@@ -806,7 +806,7 @@ mod tests {
806 Annotation { 806 Annotation {
807 range: 0..12, 807 range: 0..12,
808 kind: Runnable { 808 kind: Runnable {
809 debug: false, 809 debug: true,
810 runnable: Runnable { 810 runnable: Runnable {
811 nav: NavigationTarget { 811 nav: NavigationTarget {
812 file_id: FileId( 812 file_id: FileId(
@@ -825,7 +825,7 @@ mod tests {
825 Annotation { 825 Annotation {
826 range: 14..64, 826 range: 14..64,
827 kind: Runnable { 827 kind: Runnable {
828 debug: true, 828 debug: false,
829 runnable: Runnable { 829 runnable: Runnable {
830 nav: NavigationTarget { 830 nav: NavigationTarget {
831 file_id: FileId( 831 file_id: FileId(
@@ -846,7 +846,7 @@ mod tests {
846 Annotation { 846 Annotation {
847 range: 14..64, 847 range: 14..64,
848 kind: Runnable { 848 kind: Runnable {
849 debug: false, 849 debug: true,
850 runnable: Runnable { 850 runnable: Runnable {
851 nav: NavigationTarget { 851 nav: NavigationTarget {
852 file_id: FileId( 852 file_id: FileId(
@@ -867,7 +867,7 @@ mod tests {
867 Annotation { 867 Annotation {
868 range: 30..62, 868 range: 30..62,
869 kind: Runnable { 869 kind: Runnable {
870 debug: true, 870 debug: false,
871 runnable: Runnable { 871 runnable: Runnable {
872 nav: NavigationTarget { 872 nav: NavigationTarget {
873 file_id: FileId( 873 file_id: FileId(
@@ -893,7 +893,7 @@ mod tests {
893 Annotation { 893 Annotation {
894 range: 30..62, 894 range: 30..62,
895 kind: Runnable { 895 kind: Runnable {
896 debug: false, 896 debug: true,
897 runnable: Runnable { 897 runnable: Runnable {
898 nav: NavigationTarget { 898 nav: NavigationTarget {
899 file_id: FileId( 899 file_id: FileId(
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index c7cefb3b6..a83b82f1b 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -11,7 +11,7 @@
11 11
12pub(crate) mod rename; 12pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::{PathResolution, Semantics};
15use ide_db::{ 15use ide_db::{
16 base_db::FileId, 16 base_db::FileId,
17 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
@@ -22,7 +22,7 @@ use rustc_hash::FxHashMap;
22use syntax::{ 22use syntax::{
23 algo::find_node_at_offset, 23 algo::find_node_at_offset,
24 ast::{self, NameOwner}, 24 ast::{self, NameOwner},
25 AstNode, SyntaxNode, TextRange, TokenAtOffset, T, 25 match_ast, AstNode, SyntaxNode, TextRange, T,
26}; 26};
27 27
28use crate::{display::TryToNav, FilePosition, NavigationTarget}; 28use crate::{display::TryToNav, FilePosition, NavigationTarget};
@@ -47,29 +47,40 @@ pub(crate) fn find_all_refs(
47 let _p = profile::span("find_all_refs"); 47 let _p = profile::span("find_all_refs");
48 let syntax = sema.parse(position.file_id).syntax().clone(); 48 let syntax = sema.parse(position.file_id).syntax().clone();
49 49
50 let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) = 50 let (def, is_literal_search) =
51 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) 51 if let Some(name) = get_name_of_item_declaration(&syntax, position) {
52 { 52 (NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), true)
53 ( 53 } else {
54 Some(name), 54 (find_def(&sema, &syntax, position)?, false)
55 Some(|name_ref| is_record_lit_name_ref(name_ref) || is_call_expr_name_ref(name_ref)), 55 };
56 )
57 } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position)
58 {
59 (Some(name), Some(is_enum_lit_name_ref))
60 } else {
61 (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None)
62 };
63
64 let def = find_def(&sema, &syntax, position, opt_name)?;
65 56
66 let mut usages = def.usages(sema).set_scope(search_scope).all(); 57 let mut usages = def.usages(sema).set_scope(search_scope).all();
67 if let Some(ctor_filter) = ctor_filter { 58 if is_literal_search {
68 // filter for constructor-literals 59 // filter for constructor-literals
69 usages.references.values_mut().for_each(|it| { 60 let refs = usages.references.values_mut();
70 it.retain(|reference| reference.name.as_name_ref().map_or(false, ctor_filter)); 61 match def {
71 }); 62 Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(enum_))) => {
72 usages.references.retain(|_, it| !it.is_empty()); 63 refs.for_each(|it| {
64 it.retain(|reference| {
65 reference
66 .name
67 .as_name_ref()
68 .map_or(false, |name_ref| is_enum_lit_name_ref(sema, enum_, name_ref))
69 })
70 });
71 usages.references.retain(|_, it| !it.is_empty());
72 }
73 Definition::ModuleDef(hir::ModuleDef::Adt(_))
74 | Definition::ModuleDef(hir::ModuleDef::Variant(_)) => {
75 refs.for_each(|it| {
76 it.retain(|reference| {
77 reference.name.as_name_ref().map_or(false, is_lit_name_ref)
78 })
79 });
80 usages.references.retain(|_, it| !it.is_empty());
81 }
82 _ => {}
83 }
73 } 84 }
74 let nav = def.try_to_nav(sema.db)?; 85 let nav = def.try_to_nav(sema.db)?;
75 let decl_range = nav.focus_or_full_range(); 86 let decl_range = nav.focus_or_full_range();
@@ -89,9 +100,9 @@ fn find_def(
89 sema: &Semantics<RootDatabase>, 100 sema: &Semantics<RootDatabase>,
90 syntax: &SyntaxNode, 101 syntax: &SyntaxNode,
91 position: FilePosition, 102 position: FilePosition,
92 opt_name: Option<ast::Name>,
93) -> Option<Definition> { 103) -> Option<Definition> {
94 if let Some(name) = opt_name { 104 if let Some(name) = sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset)
105 {
95 let class = NameClass::classify(sema, &name)?; 106 let class = NameClass::classify(sema, &name)?;
96 Some(class.referenced_or_defined(sema.db)) 107 Some(class.referenced_or_defined(sema.db))
97 } else if let Some(lifetime) = 108 } else if let Some(lifetime) =
@@ -134,95 +145,85 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio
134 None 145 None
135} 146}
136 147
137fn get_struct_def_name_for_struct_literal_search( 148fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
138 sema: &Semantics<RootDatabase>, 149 let token = syntax.token_at_offset(position.offset).right_biased()?;
139 syntax: &SyntaxNode, 150 let kind = token.kind();
140 position: FilePosition, 151 if kind == T![;] {
141) -> Option<ast::Name> { 152 ast::Struct::cast(token.parent())
142 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 153 .filter(|struct_| struct_.field_list().is_none())
143 if right.kind() != T!['{'] && right.kind() != T!['('] { 154 .and_then(|struct_| struct_.name())
144 return None; 155 } else if kind == T!['{'] {
145 } 156 match_ast! {
146 if let Some(name) = 157 match (token.parent()) {
147 sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) 158 ast::RecordFieldList(rfl) => match_ast! {
148 { 159 match (rfl.syntax().parent()?) {
149 return name.syntax().ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); 160 ast::Variant(it) => it.name(),
161 ast::Struct(it) => it.name(),
162 ast::Union(it) => it.name(),
163 _ => None,
164 }
165 },
166 ast::VariantList(vl) => ast::Enum::cast(vl.syntax().parent()?)?.name(),
167 _ => None,
168 }
150 } 169 }
151 if sema 170 } else if kind == T!['('] {
152 .find_node_at_offset_with_descend::<ast::GenericParamList>( 171 let tfl = ast::TupleFieldList::cast(token.parent())?;
153 &syntax, 172 match_ast! {
154 left.text_range().start(), 173 match (tfl.syntax().parent()?) {
155 ) 174 ast::Variant(it) => it.name(),
156 .is_some() 175 ast::Struct(it) => it.name(),
157 { 176 _ => None,
158 return left.ancestors().find_map(ast::Struct::cast).and_then(|l| l.name()); 177 }
159 } 178 }
179 } else {
180 None
160 } 181 }
161 None
162} 182}
163 183
164fn get_enum_def_name_for_struct_literal_search( 184fn is_enum_lit_name_ref(
165 sema: &Semantics<RootDatabase>, 185 sema: &Semantics<RootDatabase>,
166 syntax: &SyntaxNode, 186 enum_: hir::Enum,
167 position: FilePosition, 187 name_ref: &ast::NameRef,
168) -> Option<ast::Name> { 188) -> bool {
169 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 189 let path_is_variant_of_enum = |path: ast::Path| {
170 if right.kind() != T!['{'] && right.kind() != T!['('] { 190 matches!(
171 return None; 191 sema.resolve_path(&path),
172 } 192 Some(PathResolution::Def(hir::ModuleDef::Variant(variant)))
173 if let Some(name) = 193 if variant.parent_enum(sema.db) == enum_
174 sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, left.text_range().start()) 194 )
175 { 195 };
176 return name.syntax().ancestors().find_map(ast::Enum::cast).and_then(|l| l.name());
177 }
178 if sema
179 .find_node_at_offset_with_descend::<ast::GenericParamList>(
180 &syntax,
181 left.text_range().start(),
182 )
183 .is_some()
184 {
185 return left.ancestors().find_map(ast::Enum::cast).and_then(|l| l.name());
186 }
187 }
188 None
189}
190
191fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
192 name_ref 196 name_ref
193 .syntax() 197 .syntax()
194 .ancestors() 198 .ancestors()
195 .find_map(ast::CallExpr::cast) 199 .find_map(|ancestor| {
196 .and_then(|c| match c.expr()? { 200 match_ast! {
197 ast::Expr::PathExpr(p) => { 201 match ancestor {
198 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) 202 ast::PathExpr(path_expr) => path_expr.path().map(path_is_variant_of_enum),
203 ast::RecordExpr(record_expr) => record_expr.path().map(path_is_variant_of_enum),
204 _ => None,
205 }
199 } 206 }
200 _ => None,
201 }) 207 })
202 .unwrap_or(false) 208 .unwrap_or(false)
203} 209}
204 210
205fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { 211fn path_ends_with(path: Option<ast::Path>, name_ref: &ast::NameRef) -> bool {
206 name_ref 212 path.and_then(|path| path.segment())
207 .syntax() 213 .and_then(|segment| segment.name_ref())
208 .ancestors() 214 .map_or(false, |segment| segment == *name_ref)
209 .find_map(ast::RecordExpr::cast)
210 .and_then(|l| l.path())
211 .and_then(|p| p.segment())
212 .map(|p| p.name_ref().as_ref() == Some(name_ref))
213 .unwrap_or(false)
214} 215}
215 216
216fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool { 217fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool {
217 name_ref 218 name_ref.syntax().ancestors().find_map(|ancestor| {
218 .syntax() 219 match_ast! {
219 .ancestors() 220 match ancestor {
220 .find_map(ast::PathExpr::cast) 221 ast::PathExpr(path_expr) => Some(path_ends_with(path_expr.path(), name_ref)),
221 .and_then(|p| p.path()) 222 ast::RecordExpr(record_expr) => Some(path_ends_with(record_expr.path(), name_ref)),
222 .and_then(|p| p.qualifier()) 223 _ => None,
223 .and_then(|p| p.segment()) 224 }
224 .map(|p| p.name_ref().as_ref() == Some(name_ref)) 225 }
225 .unwrap_or(false) 226 }).unwrap_or(false)
226} 227}
227 228
228#[cfg(test)] 229#[cfg(test)]
@@ -313,22 +314,91 @@ fn main() {
313 } 314 }
314 315
315 #[test] 316 #[test]
317 fn test_struct_literal_for_union() {
318 check(
319 r#"
320union Foo $0{
321 x: u32
322}
323
324fn main() {
325 let f: Foo;
326 f = Foo { x: 1 };
327}
328"#,
329 expect![[r#"
330 Foo Union FileId(0) 0..24 6..9
331
332 FileId(0) 62..65
333 "#]],
334 );
335 }
336
337 #[test]
316 fn test_enum_after_space() { 338 fn test_enum_after_space() {
317 check( 339 check(
318 r#" 340 r#"
319enum Foo $0{ 341enum Foo $0{
320 A, 342 A,
321 B, 343 B(),
344 C{},
322} 345}
323fn main() { 346fn main() {
324 let f: Foo; 347 let f: Foo;
325 f = Foo::A; 348 f = Foo::A;
349 f = Foo::B();
350 f = Foo::C{};
326} 351}
327"#, 352"#,
328 expect![[r#" 353 expect![[r#"
329 Foo Enum FileId(0) 0..26 5..8 354 Foo Enum FileId(0) 0..37 5..8
330 355
331 FileId(0) 63..66 356 FileId(0) 74..77
357 FileId(0) 90..93
358 FileId(0) 108..111
359 "#]],
360 );
361 }
362
363 #[test]
364 fn test_variant_record_after_space() {
365 check(
366 r#"
367enum Foo {
368 A $0{ n: i32 },
369 B,
370}
371fn main() {
372 let f: Foo;
373 f = Foo::B;
374 f = Foo::A { n: 92 };
375}
376"#,
377 expect![[r#"
378 A Variant FileId(0) 15..27 15..16
379
380 FileId(0) 95..96
381 "#]],
382 );
383 }
384 #[test]
385 fn test_variant_tuple_before_paren() {
386 check(
387 r#"
388enum Foo {
389 A$0(i32),
390 B,
391}
392fn main() {
393 let f: Foo;
394 f = Foo::B;
395 f = Foo::A(92);
396}
397"#,
398 expect![[r#"
399 A Variant FileId(0) 15..21 15..16
400
401 FileId(0) 89..90
332 "#]], 402 "#]],
333 ); 403 );
334 } 404 }
@@ -1127,4 +1197,39 @@ impl Foo {
1127 "#]], 1197 "#]],
1128 ); 1198 );
1129 } 1199 }
1200
1201 #[test]
1202 fn test_attr_differs_from_fn_with_same_name() {
1203 check(
1204 r#"
1205#[test]
1206fn test$0() {
1207 test();
1208}
1209"#,
1210 expect![[r#"
1211 test Function FileId(0) 0..33 11..15
1212
1213 FileId(0) 24..28
1214 "#]],
1215 );
1216 }
1217
1218 #[test]
1219 fn test_attr_matches_proc_macro_fn() {
1220 check(
1221 r#"
1222#[proc_macro_attribute]
1223fn my_proc_macro() {}
1224
1225#[my_proc_macro$0]
1226fn test() {}
1227"#,
1228 expect![[r#"
1229 my_proc_macro Function FileId(0) 0..45 27..40
1230
1231 FileId(0) 49..62
1232 "#]],
1233 );
1234 }
1130} 1235}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index b04214291..08f16b54d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -75,8 +75,7 @@ pub(crate) fn rename_with_semantics(
75 let source_file = sema.parse(position.file_id); 75 let source_file = sema.parse(position.file_id);
76 let syntax = source_file.syntax(); 76 let syntax = source_file.syntax();
77 77
78 let def = find_definition(sema, syntax, position) 78 let def = find_definition(sema, syntax, position)?;
79 .ok_or_else(|| format_err!("No references found at position"))?;
80 match def { 79 match def {
81 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name), 80 Definition::ModuleDef(ModuleDef::Module(module)) => rename_mod(&sema, module, new_name),
82 def => rename_reference(sema, def, new_name), 81 def => rename_reference(sema, def, new_name),
@@ -149,18 +148,30 @@ fn find_definition(
149 sema: &Semantics<RootDatabase>, 148 sema: &Semantics<RootDatabase>,
150 syntax: &SyntaxNode, 149 syntax: &SyntaxNode,
151 position: FilePosition, 150 position: FilePosition,
152) -> Option<Definition> { 151) -> RenameResult<Definition> {
153 let def = match find_name_like(sema, syntax, position)? { 152 match find_name_like(sema, syntax, position)
154 NameLike::Name(name) => NameClass::classify(sema, &name)?.referenced_or_defined(sema.db), 153 .ok_or_else(|| format_err!("No references found at position"))?
155 NameLike::NameRef(name_ref) => NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), 154 {
155 // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
156 NameLike::Name(name)
157 if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
158 {
159 bail!("Renaming aliases is currently unsupported")
160 }
161 NameLike::Name(name) => {
162 NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db))
163 }
164 NameLike::NameRef(name_ref) => {
165 NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db))
166 }
156 NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) 167 NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
157 .map(|class| NameRefClass::referenced(class, sema.db)) 168 .map(|class| NameRefClass::referenced(class, sema.db))
158 .or_else(|| { 169 .or_else(|| {
159 NameClass::classify_lifetime(sema, &lifetime) 170 NameClass::classify_lifetime(sema, &lifetime)
160 .map(|it| it.referenced_or_defined(sema.db)) 171 .map(|it| it.referenced_or_defined(sema.db))
161 })?, 172 }),
162 }; 173 }
163 Some(def) 174 .ok_or_else(|| format_err!("No references found at position"))
164} 175}
165 176
166fn source_edit_from_references( 177fn source_edit_from_references(
@@ -173,21 +184,40 @@ fn source_edit_from_references(
173 let mut edit = TextEdit::builder(); 184 let mut edit = TextEdit::builder();
174 for reference in references { 185 for reference in references {
175 let (range, replacement) = match &reference.name { 186 let (range, replacement) = match &reference.name {
176 NameLike::Name(_) => (None, format!("{}", new_name)), 187 // if the ranges differ then the node is inside a macro call, we can't really attempt
177 NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def), 188 // to make special rewrites like shorthand syntax and such, so just rename the node in
178 NameLike::Lifetime(_) => (None, format!("{}", new_name)), 189 // the macro input
179 }; 190 NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => {
180 // FIXME: Some(range) will be incorrect when we are inside macros 191 source_edit_from_name_ref(name_ref, new_name, def)
181 edit.replace(range.unwrap_or(reference.range), replacement); 192 }
193 NameLike::Name(name) if name.syntax().text_range() == reference.range => {
194 source_edit_from_name(name, new_name)
195 }
196 _ => None,
197 }
198 .unwrap_or_else(|| (reference.range, new_name.to_string()));
199 edit.replace(range, replacement);
182 } 200 }
183 (file_id, edit.finish()) 201 (file_id, edit.finish())
184} 202}
185 203
204fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
205 if let Some(_) = ast::RecordPatField::for_field_name(name) {
206 if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
207 return Some((
208 TextRange::empty(ident_pat.syntax().text_range().start()),
209 format!("{}: ", new_name),
210 ));
211 }
212 }
213 None
214}
215
186fn source_edit_from_name_ref( 216fn source_edit_from_name_ref(
187 name_ref: &ast::NameRef, 217 name_ref: &ast::NameRef,
188 new_name: &str, 218 new_name: &str,
189 def: Definition, 219 def: Definition,
190) -> (Option<TextRange>, String) { 220) -> Option<(TextRange, String)> {
191 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { 221 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
192 let rcf_name_ref = record_field.name_ref(); 222 let rcf_name_ref = record_field.name_ref();
193 let rcf_expr = record_field.expr(); 223 let rcf_expr = record_field.expr();
@@ -197,45 +227,40 @@ fn source_edit_from_name_ref(
197 if field_name == *name_ref { 227 if field_name == *name_ref {
198 if init.text() == new_name { 228 if init.text() == new_name {
199 mark::hit!(test_rename_field_put_init_shorthand); 229 mark::hit!(test_rename_field_put_init_shorthand);
200 // same names, we can use a shorthand here instead 230 // same names, we can use a shorthand here instead.
201 // we do not want to erase attributes hence this range start 231 // we do not want to erase attributes hence this range start
202 let s = field_name.syntax().text_range().start(); 232 let s = field_name.syntax().text_range().start();
203 let e = record_field.syntax().text_range().end(); 233 let e = record_field.syntax().text_range().end();
204 return (Some(TextRange::new(s, e)), format!("{}", new_name)); 234 return Some((TextRange::new(s, e), new_name.to_owned()));
205 } 235 }
206 } else if init == *name_ref { 236 } else if init == *name_ref {
207 if field_name.text() == new_name { 237 if field_name.text() == new_name {
208 mark::hit!(test_rename_local_put_init_shorthand); 238 mark::hit!(test_rename_local_put_init_shorthand);
209 // same names, we can use a shorthand here instead 239 // same names, we can use a shorthand here instead.
210 // we do not want to erase attributes hence this range start 240 // we do not want to erase attributes hence this range start
211 let s = field_name.syntax().text_range().start(); 241 let s = field_name.syntax().text_range().start();
212 let e = record_field.syntax().text_range().end(); 242 let e = record_field.syntax().text_range().end();
213 return (Some(TextRange::new(s, e)), format!("{}", new_name)); 243 return Some((TextRange::new(s, e), new_name.to_owned()));
214 } 244 }
215 } 245 }
246 None
216 } 247 }
217 // init shorthand 248 // init shorthand
218 (None, Some(_)) => { 249 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
219 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 250 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
220 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 251 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
221 match def { 252 mark::hit!(test_rename_field_in_field_shorthand);
222 Definition::Field(_) => { 253 let s = name_ref.syntax().text_range().start();
223 mark::hit!(test_rename_field_in_field_shorthand); 254 Some((TextRange::empty(s), format!("{}: ", new_name)))
224 let s = name_ref.syntax().text_range().start();
225 return (Some(TextRange::empty(s)), format!("{}: ", new_name));
226 }
227 Definition::Local(_) => {
228 mark::hit!(test_rename_local_in_field_shorthand);
229 let s = name_ref.syntax().text_range().end();
230 return (Some(TextRange::empty(s)), format!(": {}", new_name));
231 }
232 _ => {}
233 }
234 } 255 }
235 _ => {} 256 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
257 mark::hit!(test_rename_local_in_field_shorthand);
258 let s = name_ref.syntax().text_range().end();
259 Some((TextRange::empty(s), format!(": {}", new_name)))
260 }
261 _ => None,
236 } 262 }
237 } 263 } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
238 if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
239 let rcf_name_ref = record_field.name_ref(); 264 let rcf_name_ref = record_field.name_ref();
240 let rcf_pat = record_field.pat(); 265 let rcf_pat = record_field.pat();
241 match (rcf_name_ref, rcf_pat) { 266 match (rcf_name_ref, rcf_pat) {
@@ -244,17 +269,20 @@ fn source_edit_from_name_ref(
244 // field name is being renamed 269 // field name is being renamed
245 if pat.name().map_or(false, |it| it.text() == new_name) { 270 if pat.name().map_or(false, |it| it.text() == new_name) {
246 mark::hit!(test_rename_field_put_init_shorthand_pat); 271 mark::hit!(test_rename_field_put_init_shorthand_pat);
247 // same names, we can use a shorthand here instead 272 // same names, we can use a shorthand here instead/
248 // we do not want to erase attributes hence this range start 273 // we do not want to erase attributes hence this range start
249 let s = field_name.syntax().text_range().start(); 274 let s = field_name.syntax().text_range().start();
250 let e = record_field.syntax().text_range().end(); 275 let e = record_field.syntax().text_range().end();
251 return (Some(TextRange::new(s, e)), format!("{}", new_name)); 276 Some((TextRange::new(s, e), pat.to_string()))
277 } else {
278 None
252 } 279 }
253 } 280 }
254 _ => {} 281 _ => None,
255 } 282 }
283 } else {
284 None
256 } 285 }
257 (None, format!("{}", new_name))
258} 286}
259 287
260fn rename_mod( 288fn rename_mod(
@@ -1477,7 +1505,7 @@ fn foo(i: i32) -> Foo {
1477 } 1505 }
1478 1506
1479 #[test] 1507 #[test]
1480 fn test_struct_field_destructure_into_shorthand() { 1508 fn test_struct_field_pat_into_shorthand() {
1481 mark::check!(test_rename_field_put_init_shorthand_pat); 1509 mark::check!(test_rename_field_put_init_shorthand_pat);
1482 check( 1510 check(
1483 "baz", 1511 "baz",
@@ -1485,16 +1513,16 @@ fn foo(i: i32) -> Foo {
1485struct Foo { i$0: i32 } 1513struct Foo { i$0: i32 }
1486 1514
1487fn foo(foo: Foo) { 1515fn foo(foo: Foo) {
1488 let Foo { i: baz } = foo; 1516 let Foo { i: ref baz @ qux } = foo;
1489 let _ = baz; 1517 let _ = qux;
1490} 1518}
1491"#, 1519"#,
1492 r#" 1520 r#"
1493struct Foo { baz: i32 } 1521struct Foo { baz: i32 }
1494 1522
1495fn foo(foo: Foo) { 1523fn foo(foo: Foo) {
1496 let Foo { baz } = foo; 1524 let Foo { ref baz @ qux } = foo;
1497 let _ = baz; 1525 let _ = qux;
1498} 1526}
1499"#, 1527"#,
1500 ); 1528 );
@@ -1568,6 +1596,27 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1568 } 1596 }
1569 1597
1570 #[test] 1598 #[test]
1599 fn test_struct_field_complex_ident_pat() {
1600 check(
1601 "baz",
1602 r#"
1603struct Foo { i$0: i32 }
1604
1605fn foo(foo: Foo) {
1606 let Foo { ref i } = foo;
1607}
1608"#,
1609 r#"
1610struct Foo { baz: i32 }
1611
1612fn foo(foo: Foo) {
1613 let Foo { baz: ref i } = foo;
1614}
1615"#,
1616 );
1617 }
1618
1619 #[test]
1571 fn test_rename_lifetimes() { 1620 fn test_rename_lifetimes() {
1572 mark::check!(rename_lifetime); 1621 mark::check!(rename_lifetime);
1573 check( 1622 check(
@@ -1674,4 +1723,38 @@ impl Foo {
1674"#, 1723"#,
1675 ) 1724 )
1676 } 1725 }
1726
1727 #[test]
1728 fn test_rename_field_in_pat_in_macro_doesnt_shorthand() {
1729 // ideally we would be able to make this emit a short hand, but I doubt this is easily possible
1730 check(
1731 "baz",
1732 r#"
1733macro_rules! foo {
1734 ($pattern:pat) => {
1735 let $pattern = loop {};
1736 };
1737}
1738struct Foo {
1739 bar$0: u32,
1740}
1741fn foo() {
1742 foo!(Foo { bar: baz });
1743}
1744"#,
1745 r#"
1746macro_rules! foo {
1747 ($pattern:pat) => {
1748 let $pattern = loop {};
1749 };
1750}
1751struct Foo {
1752 baz: u32,
1753}
1754fn foo() {
1755 foo!(Foo { baz: baz });
1756}
1757"#,
1758 )
1759 }
1677} 1760}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index a8091dbee..ff612b7d0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef, 9 db::HirDatabase, Crate, Field, GenericParam, HasAttrs, HasVisibility, Impl, Label, Local,
10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility, 10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode, PathSegmentKind}, 13 ast::{self, AstNode, PathSegmentKind},
@@ -366,7 +366,15 @@ impl NameRefClass {
366 366
367 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { 367 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
368 if let Some(resolved) = sema.resolve_path(&path) { 368 if let Some(resolved) = sema.resolve_path(&path) {
369 return Some(NameRefClass::Definition(resolved.into())); 369 if path.syntax().parent().and_then(ast::Attr::cast).is_some() {
370 if let PathResolution::Def(ModuleDef::Function(func)) = resolved {
371 if func.attrs(sema.db).by_key("proc_macro_attribute").exists() {
372 return Some(NameRefClass::Definition(resolved.into()));
373 }
374 }
375 } else {
376 return Some(NameRefClass::Definition(resolved.into()));
377 }
370 } 378 }
371 } 379 }
372 380
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index f47898b9b..1d8d34a0b 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -44,6 +44,15 @@ impl ops::Index<Target> for CargoWorkspace {
44 } 44 }
45} 45}
46 46
47/// Describes how to set the rustc source directory.
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub enum RustcSource {
50 /// Explicit path for the rustc source directory.
51 Path(AbsPathBuf),
52 /// Try to automatically detect where the rustc source directory is.
53 Discover,
54}
55
47#[derive(Default, Clone, Debug, PartialEq, Eq)] 56#[derive(Default, Clone, Debug, PartialEq, Eq)]
48pub struct CargoConfig { 57pub struct CargoConfig {
49 /// Do not activate the `default` feature. 58 /// Do not activate the `default` feature.
@@ -64,7 +73,7 @@ pub struct CargoConfig {
64 pub no_sysroot: bool, 73 pub no_sysroot: bool,
65 74
66 /// rustc private crate source 75 /// rustc private crate source
67 pub rustc_source: Option<AbsPathBuf>, 76 pub rustc_source: Option<RustcSource>,
68} 77}
69 78
70pub type Package = Idx<PackageData>; 79pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index d712095a6..a5b35ed95 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -21,8 +21,8 @@ use rustc_hash::FxHashSet;
21pub use crate::{ 21pub use crate::{
22 build_data::{BuildDataCollector, BuildDataResult}, 22 build_data::{BuildDataCollector, BuildDataResult},
23 cargo_workspace::{ 23 cargo_workspace::{
24 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, 24 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target,
25 TargetKind, 25 TargetData, TargetKind,
26 }, 26 },
27 project_json::{ProjectJson, ProjectJsonData}, 27 project_json::{ProjectJson, ProjectJsonData},
28 sysroot::Sysroot, 28 sysroot::Sysroot,
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs
index ff44dae4a..3b0ff506d 100644
--- a/crates/project_model/src/sysroot.rs
+++ b/crates/project_model/src/sysroot.rs
@@ -51,11 +51,18 @@ impl Sysroot {
51 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { 51 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
52 log::debug!("Discovering sysroot for {}", cargo_toml.display()); 52 log::debug!("Discovering sysroot for {}", cargo_toml.display());
53 let current_dir = cargo_toml.parent().unwrap(); 53 let current_dir = cargo_toml.parent().unwrap();
54 let sysroot_src_dir = discover_sysroot_src_dir(current_dir)?; 54 let sysroot_dir = discover_sysroot_dir(current_dir)?;
55 let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
55 let res = Sysroot::load(&sysroot_src_dir)?; 56 let res = Sysroot::load(&sysroot_src_dir)?;
56 Ok(res) 57 Ok(res)
57 } 58 }
58 59
60 pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
61 log::debug!("Discovering rustc source for {}", cargo_toml.display());
62 let current_dir = cargo_toml.parent().unwrap();
63 discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
64 }
65
59 pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> { 66 pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
60 let mut sysroot = Sysroot { crates: Arena::default() }; 67 let mut sysroot = Sysroot { crates: Arena::default() };
61 68
@@ -110,7 +117,18 @@ impl Sysroot {
110 } 117 }
111} 118}
112 119
113fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { 120fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
121 let mut rustc = Command::new(toolchain::rustc());
122 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
123 log::debug!("Discovering sysroot by {:?}", rustc);
124 let stdout = utf8_stdout(rustc)?;
125 Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
126}
127
128fn discover_sysroot_src_dir(
129 sysroot_path: &AbsPathBuf,
130 current_dir: &AbsPath,
131) -> Result<AbsPathBuf> {
114 if let Ok(path) = env::var("RUST_SRC_PATH") { 132 if let Ok(path) = env::var("RUST_SRC_PATH") {
115 let path = AbsPathBuf::try_from(path.as_str()) 133 let path = AbsPathBuf::try_from(path.as_str())
116 .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; 134 .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
@@ -122,14 +140,6 @@ fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
122 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); 140 log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
123 } 141 }
124 142
125 let sysroot_path = {
126 let mut rustc = Command::new(toolchain::rustc());
127 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
128 log::debug!("Discovering sysroot by {:?}", rustc);
129 let stdout = utf8_stdout(rustc)?;
130 AbsPathBuf::assert(PathBuf::from(stdout))
131 };
132
133 get_rust_src(&sysroot_path) 143 get_rust_src(&sysroot_path)
134 .or_else(|| { 144 .or_else(|| {
135 let mut rustup = Command::new(toolchain::rustup()); 145 let mut rustup = Command::new(toolchain::rustup());
@@ -149,6 +159,16 @@ try installing the Rust source the same way you installed rustc",
149 }) 159 })
150} 160}
151 161
162fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
163 let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
164 log::debug!("Checking for rustc source code: {}", rustc_src.display());
165 if rustc_src.exists() {
166 Some(rustc_src)
167 } else {
168 None
169 }
170}
171
152fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { 172fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
153 // Try the new path first since the old one still exists. 173 // Try the new path first since the old one still exists.
154 let rust_src = sysroot_path.join("lib/rustlib/src/rust"); 174 let rust_src = sysroot_path.join("lib/rustlib/src/rust");
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index c30861976..0220efdb4 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -114,6 +114,7 @@ impl ProjectWorkspace {
114 cargo_version 114 cargo_version
115 ) 115 )
116 })?; 116 })?;
117
117 let sysroot = if config.no_sysroot { 118 let sysroot = if config.no_sysroot {
118 Sysroot::default() 119 Sysroot::default()
119 } else { 120 } else {
@@ -125,7 +126,17 @@ impl ProjectWorkspace {
125 })? 126 })?
126 }; 127 };
127 128
128 let rustc = if let Some(rustc_dir) = &config.rustc_source { 129 let rustc_dir = if let Some(rustc_source) = &config.rustc_source {
130 use cargo_workspace::RustcSource;
131 match rustc_source {
132 RustcSource::Path(path) => Some(path.clone()),
133 RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml),
134 }
135 } else {
136 None
137 };
138
139 let rustc = if let Some(rustc_dir) = rustc_dir {
129 Some( 140 Some(
130 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) 141 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
131 .with_context(|| { 142 .with_context(|| {
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 82ea76666..746b4cdaa 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -22,7 +22,7 @@ env_logger = { version = "0.8.1", default-features = false }
22itertools = "0.10.0" 22itertools = "0.10.0"
23jod-thread = "0.1.0" 23jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.86.0", features = ["proposed"] } 25lsp-types = { version = "0.87.0", features = ["proposed"] }
26parking_lot = "0.11.0" 26parking_lot = "0.11.0"
27pico-args = "0.4.0" 27pico-args = "0.4.0"
28oorandom = "11.1.2" 28oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 088b17b03..93d0ad4ec 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -127,6 +127,7 @@ fn run_server() -> Result<()> {
127 name: String::from("rust-analyzer"), 127 name: String::from("rust-analyzer"),
128 version: Some(String::from(env!("REV"))), 128 version: Some(String::from(env!("REV"))),
129 }), 129 }),
130 offset_encoding: None,
130 }; 131 };
131 132
132 let initialize_result = serde_json::to_value(initialize_result).unwrap(); 133 let initialize_result = serde_json::to_value(initialize_result).unwrap();
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index f148521a2..aa48c455c 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -34,6 +34,8 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
34 completion_provider: Some(CompletionOptions { 34 completion_provider: Some(CompletionOptions {
35 resolve_provider: completions_resolve_provider(client_caps), 35 resolve_provider: completions_resolve_provider(client_caps),
36 trigger_characters: Some(vec![":".to_string(), ".".to_string()]), 36 trigger_characters: Some(vec![":".to_string(), ".".to_string()]),
37 all_commit_characters: None,
38 completion_item: None,
37 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, 39 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
38 }), 40 }),
39 signature_help_provider: Some(SignatureHelpOptions { 41 signature_help_provider: Some(SignatureHelpOptions {
@@ -58,7 +60,6 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
58 more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), 60 more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
59 }), 61 }),
60 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 62 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
61 semantic_highlighting: None,
62 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 63 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
63 rename_provider: Some(OneOf::Right(RenameOptions { 64 rename_provider: Some(OneOf::Right(RenameOptions {
64 prepare_provider: Some(true), 65 prepare_provider: Some(true),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cc0b22bff..f9098968a 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -18,7 +18,7 @@ use ide_db::helpers::{
18}; 18};
19use itertools::Itertools; 19use itertools::Itertools;
20use lsp_types::{ClientCapabilities, MarkupKind}; 20use lsp_types::{ClientCapabilities, MarkupKind};
21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
22use rustc_hash::FxHashSet; 22use rustc_hash::FxHashSet;
23use serde::{de::DeserializeOwned, Deserialize}; 23use serde::{de::DeserializeOwned, Deserialize};
24use vfs::AbsPathBuf; 24use vfs::AbsPathBuf;
@@ -177,8 +177,9 @@ config_data! {
177 /// tests or binaries.\nFor example, it may be `--release`. 177 /// tests or binaries.\nFor example, it may be `--release`.
178 runnables_cargoExtraArgs: Vec<String> = "[]", 178 runnables_cargoExtraArgs: Vec<String> = "[]",
179 179
180 /// Path to the rust compiler sources, for usage in rustc_private projects. 180 /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover"
181 rustcSource : Option<PathBuf> = "null", 181 /// to try to automatically find it.
182 rustcSource : Option<String> = "null",
182 183
183 /// Additional arguments to `rustfmt`. 184 /// Additional arguments to `rustfmt`.
184 rustfmt_extraArgs: Vec<String> = "[]", 185 rustfmt_extraArgs: Vec<String> = "[]",
@@ -473,7 +474,13 @@ impl Config {
473 self.data.cargo_loadOutDirsFromCheck 474 self.data.cargo_loadOutDirsFromCheck
474 } 475 }
475 pub fn cargo(&self) -> CargoConfig { 476 pub fn cargo(&self) -> CargoConfig {
476 let rustc_source = self.data.rustcSource.as_ref().map(|it| self.root_path.join(&it)); 477 let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| {
478 if rustc_src == "discover" {
479 RustcSource::Discover
480 } else {
481 RustcSource::Path(self.root_path.join(rustc_src))
482 }
483 });
477 484
478 CargoConfig { 485 CargoConfig {
479 no_default_features: self.data.cargo_noDefaultFeatures, 486 no_default_features: self.data.cargo_noDefaultFeatures,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index b051c8f6c..2d697c75f 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -10,8 +10,7 @@ use std::{
10 10
11use ide::{ 11use ide::{
12 AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, 12 AnnotationConfig, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex,
13 NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, 13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, TextEdit,
14 TextEdit,
15}; 14};
16use ide_db::SymbolKind; 15use ide_db::SymbolKind;
17use itertools::Itertools; 16use itertools::Itertools;
@@ -19,12 +18,12 @@ use lsp_server::ErrorCode;
19use lsp_types::{ 18use lsp_types::{
20 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 19 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
21 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 20 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
22 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, 21 CodeActionKind, CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams,
23 DocumentFormattingParams, DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, 22 DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString,
24 Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, 23 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
25 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, 24 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
26 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, 25 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
27 SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit, 26 TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
28}; 27};
29use project_model::TargetKind; 28use project_model::TargetKind;
30use serde::{Deserialize, Serialize}; 29use serde::{Deserialize, Serialize};
@@ -1422,40 +1421,7 @@ pub(crate) fn handle_open_cargo_toml(
1422 Ok(Some(res)) 1421 Ok(Some(res))
1423} 1422}
1424 1423
1425fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command { 1424fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
1426 Command {
1427 title: title.to_string(),
1428 command: "rust-analyzer.runSingle".into(),
1429 arguments: Some(vec![to_value(runnable).unwrap()]),
1430 }
1431}
1432
1433fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1434 Command {
1435 title: "Debug".into(),
1436 command: "rust-analyzer.debugSingle".into(),
1437 arguments: Some(vec![to_value(runnable).unwrap()]),
1438 }
1439}
1440
1441fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1442 let value = if snap.config.location_link() {
1443 let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1444 to_value(link).ok()?
1445 } else {
1446 let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
1447 let location = to_proto::location(snap, range).ok()?;
1448 to_value(location).ok()?
1449 };
1450
1451 Some(Command {
1452 title: nav.name.to_string(),
1453 command: "rust-analyzer.gotoLocation".into(),
1454 arguments: Some(vec![value]),
1455 })
1456}
1457
1458fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1459 lsp_ext::CommandLink { tooltip: Some(tooltip), command } 1425 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1460} 1426}
1461 1427
@@ -1474,7 +1440,7 @@ fn show_impl_command_link(
1474 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok()) 1440 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1475 .collect(); 1441 .collect();
1476 let title = to_proto::implementation_title(locations.len()); 1442 let title = to_proto::implementation_title(locations.len());
1477 let command = to_proto::show_references_command(title, &uri, position, locations); 1443 let command = to_proto::command::show_references(title, &uri, position, locations);
1478 1444
1479 return Some(lsp_ext::CommandLinkGroup { 1445 return Some(lsp_ext::CommandLinkGroup {
1480 commands: vec![to_command_link(command, "Go to implementations".into())], 1446 commands: vec![to_command_link(command, "Go to implementations".into())],
@@ -1501,12 +1467,12 @@ fn runnable_action_links(
1501 let mut group = lsp_ext::CommandLinkGroup::default(); 1467 let mut group = lsp_ext::CommandLinkGroup::default();
1502 1468
1503 if hover_config.run { 1469 if hover_config.run {
1504 let run_command = run_single_command(&r, action.run_title); 1470 let run_command = to_proto::command::run_single(&r, action.run_title);
1505 group.commands.push(to_command_link(run_command, r.label.clone())); 1471 group.commands.push(to_command_link(run_command, r.label.clone()));
1506 } 1472 }
1507 1473
1508 if hover_config.debug { 1474 if hover_config.debug {
1509 let dbg_command = debug_single_command(&r); 1475 let dbg_command = to_proto::command::debug_single(&r);
1510 group.commands.push(to_command_link(dbg_command, r.label)); 1476 group.commands.push(to_command_link(dbg_command, r.label));
1511 } 1477 }
1512 1478
@@ -1527,7 +1493,7 @@ fn goto_type_action_links(
1527 commands: nav_targets 1493 commands: nav_targets
1528 .iter() 1494 .iter()
1529 .filter_map(|it| { 1495 .filter_map(|it| {
1530 goto_location_command(snap, &it.nav) 1496 to_proto::command::goto_location(snap, &it.nav)
1531 .map(|cmd| to_command_link(cmd, it.mod_path.clone())) 1497 .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1532 }) 1498 })
1533 .collect(), 1499 .collect(),
diff --git a/crates/rust-analyzer/src/line_endings.rs b/crates/rust-analyzer/src/line_endings.rs
index 9f892f32e..bf0e255d9 100644
--- a/crates/rust-analyzer/src/line_endings.rs
+++ b/crates/rust-analyzer/src/line_endings.rs
@@ -46,7 +46,7 @@ impl LineEndings {
46 return (src, LineEndings::Dos); 46 return (src, LineEndings::Dos);
47 47
48 fn find_crlf(src: &[u8]) -> Option<usize> { 48 fn find_crlf(src: &[u8]) -> Option<usize> {
49 src.iter().zip(src.iter().skip(1)).position(|it| it == (&b'\r', &b'\n')) 49 src.windows(2).position(|it| it == b"\r\n")
50 } 50 }
51 } 51 }
52} 52}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 29fac96fb..8a2b4d9bd 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -229,11 +229,7 @@ pub(crate) fn completion_item(
229 } 229 }
230 230
231 if completion_item.trigger_call_info() { 231 if completion_item.trigger_call_info() {
232 res.command = Some(lsp_types::Command { 232 res.command = Some(command::trigger_parameter_hints());
233 title: "triggerParameterHints".into(),
234 command: "editor.action.triggerParameterHints".into(),
235 arguments: None,
236 });
237 } 233 }
238 234
239 let mut all_results = match completion_item.ref_match() { 235 let mut all_results = match completion_item.ref_match() {
@@ -878,17 +874,10 @@ pub(crate) fn code_lens(
878 let r = runnable(&snap, run.nav.file_id, run)?; 874 let r = runnable(&snap, run.nav.file_id, run)?;
879 875
880 let command = if debug { 876 let command = if debug {
881 lsp_types::Command { 877 command::debug_single(&r)
882 title: action.run_title.to_string(),
883 command: "rust-analyzer.runSingle".into(),
884 arguments: Some(vec![to_value(r).unwrap()]),
885 }
886 } else { 878 } else {
887 lsp_types::Command { 879 let title = action.run_title.to_string();
888 title: "Debug".into(), 880 command::run_single(&r, &title)
889 command: "rust-analyzer.debugSingle".into(),
890 arguments: Some(vec![to_value(r).unwrap()]),
891 }
892 }; 881 };
893 882
894 Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None }) 883 Ok(lsp_types::CodeLens { range: annotation_range, command: Some(command), data: None })
@@ -922,7 +911,7 @@ pub(crate) fn code_lens(
922 }) 911 })
923 .collect(); 912 .collect();
924 913
925 show_references_command( 914 command::show_references(
926 implementation_title(locations.len()), 915 implementation_title(locations.len()),
927 &url, 916 &url,
928 position, 917 position,
@@ -951,7 +940,12 @@ pub(crate) fn code_lens(
951 let locations: Vec<lsp_types::Location> = 940 let locations: Vec<lsp_types::Location> =
952 ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect(); 941 ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
953 942
954 show_references_command(reference_title(locations.len()), &url, position, locations) 943 command::show_references(
944 reference_title(locations.len()),
945 &url,
946 position,
947 locations,
948 )
955 }); 949 });
956 950
957 Ok(lsp_types::CodeLens { 951 Ok(lsp_types::CodeLens {
@@ -963,24 +957,79 @@ pub(crate) fn code_lens(
963 } 957 }
964} 958}
965 959
966pub(crate) fn show_references_command( 960pub(crate) mod command {
967 title: String, 961 use ide::{FileRange, NavigationTarget};
968 uri: &lsp_types::Url, 962 use serde_json::to_value;
969 position: lsp_types::Position, 963
970 locations: Vec<lsp_types::Location>, 964 use crate::{
971) -> lsp_types::Command { 965 global_state::GlobalStateSnapshot,
972 // We cannot use the 'editor.action.showReferences' command directly 966 lsp_ext,
973 // because that command requires vscode types which we convert in the handler 967 to_proto::{location, location_link},
974 // on the client side. 968 };
975 969
976 lsp_types::Command { 970 pub(crate) fn show_references(
977 title, 971 title: String,
978 command: "rust-analyzer.showReferences".into(), 972 uri: &lsp_types::Url,
979 arguments: Some(vec![ 973 position: lsp_types::Position,
980 to_value(uri).unwrap(), 974 locations: Vec<lsp_types::Location>,
981 to_value(position).unwrap(), 975 ) -> lsp_types::Command {
982 to_value(locations).unwrap(), 976 // We cannot use the 'editor.action.showReferences' command directly
983 ]), 977 // because that command requires vscode types which we convert in the handler
978 // on the client side.
979
980 lsp_types::Command {
981 title,
982 command: "rust-analyzer.showReferences".into(),
983 arguments: Some(vec![
984 to_value(uri).unwrap(),
985 to_value(position).unwrap(),
986 to_value(locations).unwrap(),
987 ]),
988 }
989 }
990
991 pub(crate) fn run_single(runnable: &lsp_ext::Runnable, title: &str) -> lsp_types::Command {
992 lsp_types::Command {
993 title: title.to_string(),
994 command: "rust-analyzer.runSingle".into(),
995 arguments: Some(vec![to_value(runnable).unwrap()]),
996 }
997 }
998
999 pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
1000 lsp_types::Command {
1001 title: "Debug".into(),
1002 command: "rust-analyzer.debugSingle".into(),
1003 arguments: Some(vec![to_value(runnable).unwrap()]),
1004 }
1005 }
1006
1007 pub(crate) fn goto_location(
1008 snap: &GlobalStateSnapshot,
1009 nav: &NavigationTarget,
1010 ) -> Option<lsp_types::Command> {
1011 let value = if snap.config.location_link() {
1012 let link = location_link(snap, None, nav.clone()).ok()?;
1013 to_value(link).ok()?
1014 } else {
1015 let range = FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() };
1016 let location = location(snap, range).ok()?;
1017 to_value(location).ok()?
1018 };
1019
1020 Some(lsp_types::Command {
1021 title: nav.name.to_string(),
1022 command: "rust-analyzer.gotoLocation".into(),
1023 arguments: Some(vec![value]),
1024 })
1025 }
1026
1027 pub(crate) fn trigger_parameter_hints() -> lsp_types::Command {
1028 lsp_types::Command {
1029 title: "triggerParameterHints".into(),
1030 command: "editor.action.triggerParameterHints".into(),
1031 arguments: None,
1032 }
984 } 1033 }
985} 1034}
986 1035
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index b105cb0e0..307e150e9 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -3,12 +3,11 @@
3 3
4use std::fmt; 4use std::fmt;
5 5
6use ast::AttrsOwner;
7use itertools::Itertools; 6use itertools::Itertools;
8use parser::SyntaxKind; 7use parser::SyntaxKind;
9 8
10use crate::{ 9use crate::{
11 ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode}, 10 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
12 SmolStr, SyntaxElement, SyntaxToken, T, 11 SmolStr, SyntaxElement, SyntaxToken, T,
13}; 12};
14 13
@@ -324,7 +323,7 @@ impl ast::RecordPatField {
324 323
325 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { 324 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
326 let candidate = 325 let candidate =
327 field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?; 326 field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
328 match candidate.field_name()? { 327 match candidate.field_name()? {
329 NameOrNameRef::Name(name) if name == *field_name => Some(candidate), 328 NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
330 _ => None, 329 _ => None,
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 55178c84c..f91e04c31 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -105,7 +105,7 @@
105[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: 105[[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`)::
106 Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. 106 Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`.
107[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: 107[[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`)::
108 Path to the rust compiler sources, for usage in rustc_private projects. 108 Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it.
109[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: 109[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`)::
110 Additional arguments to `rustfmt`. 110 Additional arguments to `rustfmt`.
111[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: 111[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`)::
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 2d717e366..42077a7fa 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -195,9 +195,9 @@
195 } 195 }
196 }, 196 },
197 "@types/vscode": { 197 "@types/vscode": {
198 "version": "1.52.0", 198 "version": "1.53.0",
199 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.52.0.tgz", 199 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.53.0.tgz",
200 "integrity": "sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==", 200 "integrity": "sha512-XjFWbSPOM0EKIT2XhhYm3D3cx3nn3lshMUcWNy1eqefk+oqRuBq8unVb6BYIZqXy9lQZyeUl7eaBCOZWv+LcXQ==",
201 "dev": true 201 "dev": true
202 }, 202 },
203 "@typescript-eslint/eslint-plugin": { 203 "@typescript-eslint/eslint-plugin": {
@@ -2364,33 +2364,33 @@
2364 } 2364 }
2365 }, 2365 },
2366 "vscode-jsonrpc": { 2366 "vscode-jsonrpc": {
2367 "version": "6.0.0", 2367 "version": "6.1.0-next.1",
2368 "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", 2368 "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.1.0-next.1.tgz",
2369 "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" 2369 "integrity": "sha512-2s1qEg8N97p8pooHnA8xgR4vnslCTJuE3rQqSQeJuSDklFcQSn9yW8gOinH/1vKeWiPuzHTG9FFty91v4v9Pag=="
2370 }, 2370 },
2371 "vscode-languageclient": { 2371 "vscode-languageclient": {
2372 "version": "7.0.0", 2372 "version": "7.1.0-next.1",
2373 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", 2373 "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.1.0-next.1.tgz",
2374 "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", 2374 "integrity": "sha512-pMdMyJ5e3CfdyvtDsj6ytTO+WcmrvtrFJWvBk4rZnIKmA3aCjrqTYI0XSWN/Cf4kcaV1J2+fTZf/2RcP2SzDiQ==",
2375 "requires": { 2375 "requires": {
2376 "minimatch": "^3.0.4", 2376 "minimatch": "^3.0.4",
2377 "semver": "^7.3.4", 2377 "semver": "^7.3.4",
2378 "vscode-languageserver-protocol": "3.16.0" 2378 "vscode-languageserver-protocol": "3.17.0-next.1"
2379 } 2379 }
2380 }, 2380 },
2381 "vscode-languageserver-protocol": { 2381 "vscode-languageserver-protocol": {
2382 "version": "3.16.0", 2382 "version": "3.17.0-next.1",
2383 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", 2383 "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.1.tgz",
2384 "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", 2384 "integrity": "sha512-cbFKGk+Q7HQ5RDdf6qiQrRv9qxR/87hBm/GrfLcQ5rmHPJOXg//ZYCXEQF+wSJNmcc6IrnET2cHJUxlZYHUEXA==",
2385 "requires": { 2385 "requires": {
2386 "vscode-jsonrpc": "6.0.0", 2386 "vscode-jsonrpc": "6.1.0-next.1",
2387 "vscode-languageserver-types": "3.16.0" 2387 "vscode-languageserver-types": "3.17.0-next.1"
2388 } 2388 }
2389 }, 2389 },
2390 "vscode-languageserver-types": { 2390 "vscode-languageserver-types": {
2391 "version": "3.16.0", 2391 "version": "3.17.0-next.1",
2392 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", 2392 "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.1.tgz",
2393 "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" 2393 "integrity": "sha512-VGzh06oynbYa6JbTKUbxOEZN7CYEtWhN7DK5wfzUpeCJl8X8xZX39g2PVfpqXrIEduu7dcJgK007KgnX9tHNKA=="
2394 }, 2394 },
2395 "vscode-test": { 2395 "vscode-test": {
2396 "version": "1.4.1", 2396 "version": "1.4.1",
diff --git a/editors/code/package.json b/editors/code/package.json
index 55825456e..d26c64a57 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
21 "Programming Languages" 21 "Programming Languages"
22 ], 22 ],
23 "engines": { 23 "engines": {
24 "vscode": "^1.52.0" 24 "vscode": "^1.53.0"
25 }, 25 },
26 "enableProposedApi": true, 26 "enableProposedApi": true,
27 "scripts": { 27 "scripts": {
@@ -36,7 +36,7 @@
36 }, 36 },
37 "dependencies": { 37 "dependencies": {
38 "node-fetch": "^2.6.1", 38 "node-fetch": "^2.6.1",
39 "vscode-languageclient": "7.0.0" 39 "vscode-languageclient": "^7.1.0-next.1"
40 }, 40 },
41 "devDependencies": { 41 "devDependencies": {
42 "@rollup/plugin-commonjs": "^17.0.0", 42 "@rollup/plugin-commonjs": "^17.0.0",
@@ -45,7 +45,7 @@
45 "@types/mocha": "^8.0.4", 45 "@types/mocha": "^8.0.4",
46 "@types/node": "~12.12.6", 46 "@types/node": "~12.12.6",
47 "@types/node-fetch": "^2.5.7", 47 "@types/node-fetch": "^2.5.7",
48 "@types/vscode": "^1.52.0", 48 "@types/vscode": "^1.53.0",
49 "@typescript-eslint/eslint-plugin": "^4.9.0", 49 "@typescript-eslint/eslint-plugin": "^4.9.0",
50 "@typescript-eslint/parser": "^4.9.0", 50 "@typescript-eslint/parser": "^4.9.0",
51 "eslint": "^7.15.0", 51 "eslint": "^7.15.0",
@@ -707,7 +707,7 @@
707 } 707 }
708 }, 708 },
709 "rust-analyzer.rustcSource": { 709 "rust-analyzer.rustcSource": {
710 "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects.", 710 "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.",
711 "default": null, 711 "default": null,
712 "type": [ 712 "type": [
713 "null", 713 "null",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index aec5c70c0..db5f4c023 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -79,7 +79,7 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
79 return hover; 79 return hover;
80 }, 80 },
81 (error) => { 81 (error) => {
82 client.handleFailedRequest(lc.HoverRequest.type, error, null); 82 client.handleFailedRequest(lc.HoverRequest.type, token, error, null);
83 return Promise.resolve(null); 83 return Promise.resolve(null);
84 }); 84 });
85 }, 85 },
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index d18d6c8a9..620810d72 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -246,10 +246,10 @@ async function patchelf(dest: PathLike): Promise<void> {
246 }, 246 },
247 async (progress, _) => { 247 async (progress, _) => {
248 const expression = ` 248 const expression = `
249 {src, pkgs ? import <nixpkgs> {}}: 249 {srcStr, pkgs ? import <nixpkgs> {}}:
250 pkgs.stdenv.mkDerivation { 250 pkgs.stdenv.mkDerivation {
251 name = "rust-analyzer"; 251 name = "rust-analyzer";
252 inherit src; 252 src = /. + srcStr;
253 phases = [ "installPhase" "fixupPhase" ]; 253 phases = [ "installPhase" "fixupPhase" ];
254 installPhase = "cp $src $out"; 254 installPhase = "cp $src $out";
255 fixupPhase = '' 255 fixupPhase = ''
@@ -262,7 +262,7 @@ async function patchelf(dest: PathLike): Promise<void> {
262 await fs.rename(dest, origFile); 262 await fs.rename(dest, origFile);
263 progress.report({ message: "Patching executable", increment: 20 }); 263 progress.report({ message: "Patching executable", increment: 20 });
264 await new Promise((resolve, reject) => { 264 await new Promise((resolve, reject) => {
265 const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`, 265 const handle = exec(`nix-build -E - --argstr srcStr '${origFile}' -o '${dest}'`,
266 (err, stdout, stderr) => { 266 (err, stdout, stderr) => {
267 if (err != null) { 267 if (err != null) {
268 reject(Error(stderr)); 268 reject(Error(stderr));