aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/generate_enum_match_method.rs41
-rw-r--r--crates/assists/src/handlers/generate_getter.rs40
-rw-r--r--crates/assists/src/handlers/generate_getter_mut.rs40
-rw-r--r--crates/assists/src/handlers/generate_new.rs4
-rw-r--r--crates/assists/src/handlers/generate_setter.rs40
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs109
-rw-r--r--crates/assists/src/utils.rs24
-rw-r--r--crates/ide/src/call_hierarchy.rs8
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/references.rs370
-rw-r--r--crates/ide/src/references/rename.rs197
-rw-r--r--crates/ide_db/src/search.rs119
-rw-r--r--crates/rust-analyzer/src/handlers.rs49
-rw-r--r--crates/syntax/src/ast/node_ext.rs30
14 files changed, 631 insertions, 444 deletions
diff --git a/crates/assists/src/handlers/generate_enum_match_method.rs b/crates/assists/src/handlers/generate_enum_match_method.rs
index c3ff38b66..aeb887e71 100644
--- a/crates/assists/src/handlers/generate_enum_match_method.rs
+++ b/crates/assists/src/handlers/generate_enum_match_method.rs
@@ -4,7 +4,7 @@ use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark; 4use test_utils::mark;
5 5
6use crate::{ 6use crate::{
7 utils::{find_impl_block, find_struct_impl, generate_impl_text}, 7 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
8 AssistContext, AssistId, AssistKind, Assists, 8 AssistContext, AssistId, AssistKind, Assists,
9}; 9};
10 10
@@ -80,7 +80,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
80 ); 80 );
81 81
82 let start_offset = impl_def 82 let start_offset = impl_def
83 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 83 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
84 .unwrap_or_else(|| { 84 .unwrap_or_else(|| {
85 buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf); 85 buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf);
86 parent_enum.syntax().text_range().end() 86 parent_enum.syntax().text_range().end()
@@ -200,4 +200,41 @@ impl Variant {
200}"#, 200}"#,
201 ); 201 );
202 } 202 }
203
204 #[test]
205 fn test_multiple_generate_enum_match_from_variant() {
206 check_assist(
207 generate_enum_match_method,
208 r#"
209enum Variant {
210 Undefined,
211 Minor,
212 Major$0,
213}
214
215impl Variant {
216 /// Returns `true` if the variant is [`Minor`].
217 fn is_minor(&self) -> bool {
218 matches!(self, Self::Minor)
219 }
220}"#,
221 r#"enum Variant {
222 Undefined,
223 Minor,
224 Major,
225}
226
227impl Variant {
228 /// Returns `true` if the variant is [`Minor`].
229 fn is_minor(&self) -> bool {
230 matches!(self, Self::Minor)
231 }
232
233 /// Returns `true` if the variant is [`Major`].
234 fn is_major(&self) -> bool {
235 matches!(self, Self::Major)
236 }
237}"#,
238 );
239 }
203} 240}
diff --git a/crates/assists/src/handlers/generate_getter.rs b/crates/assists/src/handlers/generate_getter.rs
index b63dfce41..fbcf8b069 100644
--- a/crates/assists/src/handlers/generate_getter.rs
+++ b/crates/assists/src/handlers/generate_getter.rs
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4 4
5use crate::{ 5use crate::{
6 utils::{find_impl_block, find_struct_impl, generate_impl_text}, 6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 7 AssistContext, AssistId, AssistKind, Assists,
8}; 8};
9 9
@@ -73,7 +73,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
73 ); 73 );
74 74
75 let start_offset = impl_def 75 let start_offset = impl_def
76 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 76 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
77 .unwrap_or_else(|| { 77 .unwrap_or_else(|| {
78 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); 78 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
79 strukt.syntax().text_range().end() 79 strukt.syntax().text_range().end()
@@ -153,4 +153,40 @@ impl<T: Clone> Context<T> {
153}"#, 153}"#,
154 ); 154 );
155 } 155 }
156
157 #[test]
158 fn test_multiple_generate_getter() {
159 check_assist(
160 generate_getter,
161 r#"
162struct Context<T: Clone> {
163 data: T,
164 cou$0nt: usize,
165}
166
167impl<T: Clone> Context<T> {
168 /// Get a reference to the context's data.
169 fn data(&self) -> &T {
170 &self.data
171 }
172}"#,
173 r#"
174struct Context<T: Clone> {
175 data: T,
176 count: usize,
177}
178
179impl<T: Clone> Context<T> {
180 /// Get a reference to the context's data.
181 fn data(&self) -> &T {
182 &self.data
183 }
184
185 /// Get a reference to the context's count.
186 fn count(&self) -> &usize {
187 &self.count
188 }
189}"#,
190 );
191 }
156} 192}
diff --git a/crates/assists/src/handlers/generate_getter_mut.rs b/crates/assists/src/handlers/generate_getter_mut.rs
index b5085035e..bf0d99881 100644
--- a/crates/assists/src/handlers/generate_getter_mut.rs
+++ b/crates/assists/src/handlers/generate_getter_mut.rs
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4 4
5use crate::{ 5use crate::{
6 utils::{find_impl_block, find_struct_impl, generate_impl_text}, 6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 7 AssistContext, AssistId, AssistKind, Assists,
8}; 8};
9 9
@@ -76,7 +76,7 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Opt
76 ); 76 );
77 77
78 let start_offset = impl_def 78 let start_offset = impl_def
79 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 79 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
80 .unwrap_or_else(|| { 80 .unwrap_or_else(|| {
81 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); 81 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
82 strukt.syntax().text_range().end() 82 strukt.syntax().text_range().end()
@@ -156,4 +156,40 @@ impl<T: Clone> Context<T> {
156}"#, 156}"#,
157 ); 157 );
158 } 158 }
159
160 #[test]
161 fn test_multiple_generate_getter_mut() {
162 check_assist(
163 generate_getter_mut,
164 r#"
165struct Context<T: Clone> {
166 data: T,
167 cou$0nt: usize,
168}
169
170impl<T: Clone> Context<T> {
171 /// Get a mutable reference to the context's data.
172 fn data_mut(&mut self) -> &mut T {
173 &mut self.data
174 }
175}"#,
176 r#"
177struct Context<T: Clone> {
178 data: T,
179 count: usize,
180}
181
182impl<T: Clone> Context<T> {
183 /// Get a mutable reference to the context's data.
184 fn data_mut(&mut self) -> &mut T {
185 &mut self.data
186 }
187
188 /// Get a mutable reference to the context's count.
189 fn count_mut(&mut self) -> &mut usize {
190 &mut self.count
191 }
192}"#,
193 );
194 }
159} 195}
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs
index c29077225..8ce5930b7 100644
--- a/crates/assists/src/handlers/generate_new.rs
+++ b/crates/assists/src/handlers/generate_new.rs
@@ -4,7 +4,7 @@ use stdx::format_to;
4use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; 4use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
5 5
6use crate::{ 6use crate::{
7 utils::{find_impl_block, find_struct_impl, generate_impl_text}, 7 utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
8 AssistContext, AssistId, AssistKind, Assists, 8 AssistContext, AssistId, AssistKind, Assists,
9}; 9};
10 10
@@ -58,7 +58,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
58 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); 58 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
59 59
60 let start_offset = impl_def 60 let start_offset = impl_def
61 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 61 .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
62 .unwrap_or_else(|| { 62 .unwrap_or_else(|| {
63 buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); 63 buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
64 strukt.syntax().text_range().end() 64 strukt.syntax().text_range().end()
diff --git a/crates/assists/src/handlers/generate_setter.rs b/crates/assists/src/handlers/generate_setter.rs
index c9043a162..b655f9b9c 100644
--- a/crates/assists/src/handlers/generate_setter.rs
+++ b/crates/assists/src/handlers/generate_setter.rs
@@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4 4
5use crate::{ 5use crate::{
6 utils::{find_impl_block, find_struct_impl, generate_impl_text}, 6 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists, 7 AssistContext, AssistId, AssistKind, Assists,
8}; 8};
9 9
@@ -79,7 +79,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
79 ); 79 );
80 80
81 let start_offset = impl_def 81 let start_offset = impl_def
82 .and_then(|impl_def| find_impl_block(impl_def, &mut buf)) 82 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
83 .unwrap_or_else(|| { 83 .unwrap_or_else(|| {
84 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); 84 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
85 strukt.syntax().text_range().end() 85 strukt.syntax().text_range().end()
@@ -159,4 +159,40 @@ impl<T: Clone> Person<T> {
159}"#, 159}"#,
160 ); 160 );
161 } 161 }
162
163 #[test]
164 fn test_multiple_generate_setter() {
165 check_assist(
166 generate_setter,
167 r#"
168struct Context<T: Clone> {
169 data: T,
170 cou$0nt: usize,
171}
172
173impl<T: Clone> Context<T> {
174 /// Set the context's data.
175 fn set_data(&mut self, data: T) {
176 self.data = data;
177 }
178}"#,
179 r#"
180struct Context<T: Clone> {
181 data: T,
182 count: usize,
183}
184
185impl<T: Clone> Context<T> {
186 /// Set the context's data.
187 fn set_data(&mut self, data: T) {
188 self.data = data;
189 }
190
191 /// Set the context's count.
192 fn set_count(&mut self, count: usize) {
193 self.count = count;
194 }
195}"#,
196 );
197 }
162} 198}
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index 0e63a60e8..8d28431cf 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,7 +1,6 @@
1use ide_db::{ 1use std::collections::HashMap;
2 defs::Definition, 2
3 search::{FileReference, ReferenceKind}, 3use ide_db::{defs::Definition, search::FileReference};
4};
5use syntax::{ 4use syntax::{
6 ast::{self, AstNode, AstToken}, 5 ast::{self, AstNode, AstToken},
7 TextRange, 6 TextRange,
@@ -68,44 +67,51 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
68 67
69 let wrap_in_parens = usages 68 let wrap_in_parens = usages
70 .references 69 .references
71 .values() 70 .iter()
72 .flatten() 71 .map(|(&file_id, refs)| {
73 .map(|&FileReference { range, .. }| { 72 refs.iter()
74 let usage_node = 73 .map(|&FileReference { range, .. }| {
75 ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; 74 let usage_node = ctx
76 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); 75 .covering_node_for_range(range)
77 let usage_parent = match usage_parent_option { 76 .ancestors()
78 Some(u) => u, 77 .find_map(ast::PathExpr::cast)?;
79 None => return Ok(false), 78 let usage_parent_option =
80 }; 79 usage_node.syntax().parent().and_then(ast::Expr::cast);
81 80 let usage_parent = match usage_parent_option {
82 Ok(!matches!( 81 Some(u) => u,
83 (&initializer_expr, usage_parent), 82 None => return Ok(false),
84 (ast::Expr::CallExpr(_), _) 83 };
85 | (ast::Expr::IndexExpr(_), _) 84
86 | (ast::Expr::MethodCallExpr(_), _) 85 Ok(!matches!(
87 | (ast::Expr::FieldExpr(_), _) 86 (&initializer_expr, usage_parent),
88 | (ast::Expr::TryExpr(_), _) 87 (ast::Expr::CallExpr(_), _)
89 | (ast::Expr::RefExpr(_), _) 88 | (ast::Expr::IndexExpr(_), _)
90 | (ast::Expr::Literal(_), _) 89 | (ast::Expr::MethodCallExpr(_), _)
91 | (ast::Expr::TupleExpr(_), _) 90 | (ast::Expr::FieldExpr(_), _)
92 | (ast::Expr::ArrayExpr(_), _) 91 | (ast::Expr::TryExpr(_), _)
93 | (ast::Expr::ParenExpr(_), _) 92 | (ast::Expr::RefExpr(_), _)
94 | (ast::Expr::PathExpr(_), _) 93 | (ast::Expr::Literal(_), _)
95 | (ast::Expr::BlockExpr(_), _) 94 | (ast::Expr::TupleExpr(_), _)
96 | (ast::Expr::EffectExpr(_), _) 95 | (ast::Expr::ArrayExpr(_), _)
97 | (_, ast::Expr::CallExpr(_)) 96 | (ast::Expr::ParenExpr(_), _)
98 | (_, ast::Expr::TupleExpr(_)) 97 | (ast::Expr::PathExpr(_), _)
99 | (_, ast::Expr::ArrayExpr(_)) 98 | (ast::Expr::BlockExpr(_), _)
100 | (_, ast::Expr::ParenExpr(_)) 99 | (ast::Expr::EffectExpr(_), _)
101 | (_, ast::Expr::ForExpr(_)) 100 | (_, ast::Expr::CallExpr(_))
102 | (_, ast::Expr::WhileExpr(_)) 101 | (_, ast::Expr::TupleExpr(_))
103 | (_, ast::Expr::BreakExpr(_)) 102 | (_, ast::Expr::ArrayExpr(_))
104 | (_, ast::Expr::ReturnExpr(_)) 103 | (_, ast::Expr::ParenExpr(_))
105 | (_, ast::Expr::MatchExpr(_)) 104 | (_, ast::Expr::ForExpr(_))
106 )) 105 | (_, ast::Expr::WhileExpr(_))
106 | (_, ast::Expr::BreakExpr(_))
107 | (_, ast::Expr::ReturnExpr(_))
108 | (_, ast::Expr::MatchExpr(_))
109 ))
110 })
111 .collect::<Result<_, _>>()
112 .map(|b| (file_id, b))
107 }) 113 })
108 .collect::<Result<Vec<_>, _>>()?; 114 .collect::<Result<HashMap<_, Vec<_>>, _>>()?;
109 115
110 let init_str = initializer_expr.syntax().text().to_string(); 116 let init_str = initializer_expr.syntax().text().to_string();
111 let init_in_paren = format!("({})", &init_str); 117 let init_in_paren = format!("({})", &init_str);
@@ -117,16 +123,19 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
117 target, 123 target,
118 move |builder| { 124 move |builder| {
119 builder.delete(delete_range); 125 builder.delete(delete_range);
120 for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens) 126 for (file_id, references) in usages.references {
121 { 127 for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) {
122 let replacement = 128 let replacement =
123 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 129 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
124 match reference.kind { 130 match reference.name.as_name_ref() {
125 ReferenceKind::FieldShorthandForLocal => { 131 Some(name_ref)
126 mark::hit!(inline_field_shorthand); 132 if ast::RecordExprField::for_field_name(name_ref).is_some() =>
127 builder.insert(reference.range.end(), format!(": {}", replacement)) 133 {
134 mark::hit!(inline_field_shorthand);
135 builder.insert(reference.range.end(), format!(": {}", replacement));
136 }
137 _ => builder.replace(reference.range, replacement),
128 } 138 }
129 _ => builder.replace(reference.range, replacement),
130 } 139 }
131 } 140 }
132 }, 141 },
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 643dade23..5dd32aef1 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -279,7 +279,7 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
279// 279//
280// FIXME: change the new fn checking to a more semantic approach when that's more 280// FIXME: change the new fn checking to a more semantic approach when that's more
281// viable (e.g. we process proc macros, etc) 281// viable (e.g. we process proc macros, etc)
282// FIXME: this partially overlaps with `find_impl_block` 282// FIXME: this partially overlaps with `find_impl_block_*`
283pub(crate) fn find_struct_impl( 283pub(crate) fn find_struct_impl(
284 ctx: &AssistContext, 284 ctx: &AssistContext,
285 strukt: &ast::Adt, 285 strukt: &ast::Adt,
@@ -343,17 +343,25 @@ fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
343 343
344/// Find the start of the `impl` block for the given `ast::Impl`. 344/// Find the start of the `impl` block for the given `ast::Impl`.
345// 345//
346// FIXME: add a way to find the end of the `impl` block.
347// FIXME: this partially overlaps with `find_struct_impl` 346// FIXME: this partially overlaps with `find_struct_impl`
348pub(crate) fn find_impl_block(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> { 347pub(crate) fn find_impl_block_start(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
349 buf.push('\n'); 348 buf.push('\n');
350 let start = impl_def 349 let start = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())?.text_range().end();
351 .syntax() 350 Some(start)
352 .descendants_with_tokens() 351}
353 .find(|t| t.kind() == T!['{'])? 352
353/// Find the end of the `impl` block for the given `ast::Impl`.
354//
355// FIXME: this partially overlaps with `find_struct_impl`
356pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
357 buf.push('\n');
358 let end = impl_def
359 .assoc_item_list()
360 .and_then(|it| it.r_curly_token())?
361 .prev_sibling_or_token()?
354 .text_range() 362 .text_range()
355 .end(); 363 .end();
356 Some(start) 364 Some(end)
357} 365}
358 366
359// Generates the surrounding `impl Type { <code> }` including type and lifetime 367// Generates the surrounding `impl Type { <code> }` including type and lifetime
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index b10a0a78b..b848945d7 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for (&file_id, references) in refs.references().iter() { 50 for (file_id, references) in refs.references {
51 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
52 let file = file.syntax(); 52 let file = file.syntax();
53 for reference in references { 53 for (r_range, _) in references {
54 let token = file.token_at_offset(reference.range.start()).next()?; 54 let token = file.token_at_offset(r_range.start()).next()?;
55 let token = sema.descend_into_macros(token); 55 let token = sema.descend_into_macros(token);
56 let syntax = token.parent(); 56 let syntax = token.parent();
57 57
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
61 let def = sema.to_def(&fn_)?; 61 let def = sema.to_def(&fn_)?;
62 def.try_to_nav(sema.db) 62 def.try_to_nav(sema.db)
63 }) { 63 }) {
64 let relative_range = reference.range; 64 let relative_range = r_range;
65 calls.add(&nav, relative_range); 65 calls.add(&nav, relative_range);
66 } 66 }
67 } 67 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 989e94a31..592b12925 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -73,7 +73,7 @@ pub use crate::{
73 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 73 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
74 markup::Markup, 74 markup::Markup,
75 prime_caches::PrimeCachesProgress, 75 prime_caches::PrimeCachesProgress,
76 references::{rename::RenameError, Declaration, ReferenceSearchResult}, 76 references::{rename::RenameError, ReferenceSearchResult},
77 runnables::{Runnable, RunnableKind, TestId}, 77 runnables::{Runnable, RunnableKind, TestId},
78 syntax_highlighting::{ 78 syntax_highlighting::{
79 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, 79 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
@@ -94,7 +94,7 @@ pub use ide_db::{
94 call_info::CallInfo, 94 call_info::CallInfo,
95 label::Label, 95 label::Label,
96 line_index::{LineCol, LineIndex}, 96 line_index::{LineCol, LineIndex},
97 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope}, 97 search::{ReferenceAccess, SearchScope},
98 source_change::{FileSystemEdit, SourceChange}, 98 source_change::{FileSystemEdit, SourceChange},
99 symbol_index::Query, 99 symbol_index::Query,
100 RootDatabase, 100 RootDatabase,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 6999dacee..c7cefb3b6 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -11,14 +11,14 @@
11 11
12pub(crate) mod rename; 12pub(crate) mod rename;
13 13
14use either::Either;
15use hir::Semantics; 14use hir::Semantics;
16use ide_db::{ 15use ide_db::{
17 base_db::FileId, 16 base_db::FileId,
18 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
19 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, 18 search::{ReferenceAccess, SearchScope},
20 RootDatabase, 19 RootDatabase,
21}; 20};
21use 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},
@@ -29,52 +29,16 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget};
29 29
30#[derive(Debug, Clone)] 30#[derive(Debug, Clone)]
31pub struct ReferenceSearchResult { 31pub struct ReferenceSearchResult {
32 declaration: Declaration, 32 pub declaration: Declaration,
33 references: UsageSearchResult, 33 pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>,
34} 34}
35 35
36#[derive(Debug, Clone)] 36#[derive(Debug, Clone)]
37pub struct Declaration { 37pub struct Declaration {
38 pub nav: NavigationTarget, 38 pub nav: NavigationTarget,
39 pub kind: ReferenceKind,
40 pub access: Option<ReferenceAccess>, 39 pub access: Option<ReferenceAccess>,
41} 40}
42 41
43impl ReferenceSearchResult {
44 pub fn references(&self) -> &UsageSearchResult {
45 &self.references
46 }
47
48 pub fn references_with_declaration(mut self) -> UsageSearchResult {
49 let decl_ref = FileReference {
50 range: self.declaration.nav.focus_or_full_range(),
51 kind: self.declaration.kind,
52 access: self.declaration.access,
53 };
54 let file_id = self.declaration.nav.file_id;
55 self.references.references.entry(file_id).or_default().push(decl_ref);
56 self.references
57 }
58
59 /// Total number of references
60 /// At least 1 since all valid references should
61 /// Have a declaration
62 pub fn len(&self) -> usize {
63 self.references.len() + 1
64 }
65}
66
67// allow turning ReferenceSearchResult into an iterator
68// over References
69impl IntoIterator for ReferenceSearchResult {
70 type Item = (FileId, Vec<FileReference>);
71 type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
72
73 fn into_iter(self) -> Self::IntoIter {
74 self.references_with_declaration().into_iter()
75 }
76}
77
78pub(crate) fn find_all_refs( 42pub(crate) fn find_all_refs(
79 sema: &Semantics<RootDatabase>, 43 sema: &Semantics<RootDatabase>,
80 position: FilePosition, 44 position: FilePosition,
@@ -83,83 +47,72 @@ pub(crate) fn find_all_refs(
83 let _p = profile::span("find_all_refs"); 47 let _p = profile::span("find_all_refs");
84 let syntax = sema.parse(position.file_id).syntax().clone(); 48 let syntax = sema.parse(position.file_id).syntax().clone();
85 49
86 let (opt_name, search_kind) = if let Some(name) = 50 let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) =
87 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position) 51 get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
88 { 52 {
89 (Some(name), ReferenceKind::StructLiteral) 53 (
54 Some(name),
55 Some(|name_ref| is_record_lit_name_ref(name_ref) || is_call_expr_name_ref(name_ref)),
56 )
90 } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position) 57 } else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position)
91 { 58 {
92 (Some(name), ReferenceKind::EnumLiteral) 59 (Some(name), Some(is_enum_lit_name_ref))
93 } else { 60 } else {
94 ( 61 (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None)
95 sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset),
96 ReferenceKind::Other,
97 )
98 }; 62 };
99 63
100 let def = find_name(&sema, &syntax, position, opt_name)?; 64 let def = find_def(&sema, &syntax, position, opt_name)?;
101 65
102 let mut usages = def.usages(sema).set_scope(search_scope).all(); 66 let mut usages = def.usages(sema).set_scope(search_scope).all();
103 usages 67 if let Some(ctor_filter) = ctor_filter {
104 .references 68 // filter for constructor-literals
105 .values_mut() 69 usages.references.values_mut().for_each(|it| {
106 .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); 70 it.retain(|reference| reference.name.as_name_ref().map_or(false, ctor_filter));
107 usages.references.retain(|_, it| !it.is_empty()); 71 });
108 72 usages.references.retain(|_, it| !it.is_empty());
73 }
109 let nav = def.try_to_nav(sema.db)?; 74 let nav = def.try_to_nav(sema.db)?;
110 let decl_range = nav.focus_or_full_range(); 75 let decl_range = nav.focus_or_full_range();
111 76
112 let mut kind = ReferenceKind::Other; 77 let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) };
113 if let Definition::Local(local) = def { 78 let references = usages
114 match local.source(sema.db).value { 79 .into_iter()
115 Either::Left(pat) => { 80 .map(|(file_id, refs)| {
116 if matches!( 81 (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect())
117 pat.syntax().parent().and_then(ast::RecordPatField::cast), 82 })
118 Some(pat_field) if pat_field.name_ref().is_none() 83 .collect();
119 ) {
120 kind = ReferenceKind::FieldShorthandForLocal;
121 }
122 }
123 Either::Right(_) => kind = ReferenceKind::SelfParam,
124 }
125 } else if matches!(
126 def,
127 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
128 ) {
129 kind = ReferenceKind::Lifetime;
130 };
131
132 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
133 84
134 Some(ReferenceSearchResult { declaration, references: usages }) 85 Some(ReferenceSearchResult { declaration, references })
135} 86}
136 87
137fn find_name( 88fn find_def(
138 sema: &Semantics<RootDatabase>, 89 sema: &Semantics<RootDatabase>,
139 syntax: &SyntaxNode, 90 syntax: &SyntaxNode,
140 position: FilePosition, 91 position: FilePosition,
141 opt_name: Option<ast::Name>, 92 opt_name: Option<ast::Name>,
142) -> Option<Definition> { 93) -> Option<Definition> {
143 let def = if let Some(name) = opt_name { 94 if let Some(name) = opt_name {
144 NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) 95 let class = NameClass::classify(sema, &name)?;
96 Some(class.referenced_or_defined(sema.db))
145 } else if let Some(lifetime) = 97 } else if let Some(lifetime) =
146 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) 98 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
147 { 99 {
148 if let Some(def) = 100 let def = if let Some(def) =
149 NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db)) 101 NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db))
150 { 102 {
151 def 103 def
152 } else { 104 } else {
153 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) 105 NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db)
154 } 106 };
107 Some(def)
155 } else if let Some(name_ref) = 108 } else if let Some(name_ref) =
156 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) 109 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)
157 { 110 {
158 NameRefClass::classify(sema, &name_ref)?.referenced(sema.db) 111 let class = NameRefClass::classify(sema, &name_ref)?;
112 Some(class.referenced(sema.db))
159 } else { 113 } else {
160 return None; 114 None
161 }; 115 }
162 Some(def)
163} 116}
164 117
165fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 118fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -235,6 +188,43 @@ fn get_enum_def_name_for_struct_literal_search(
235 None 188 None
236} 189}
237 190
191fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
192 name_ref
193 .syntax()
194 .ancestors()
195 .find_map(ast::CallExpr::cast)
196 .and_then(|c| match c.expr()? {
197 ast::Expr::PathExpr(p) => {
198 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
199 }
200 _ => None,
201 })
202 .unwrap_or(false)
203}
204
205fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
206 name_ref
207 .syntax()
208 .ancestors()
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
216fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool {
217 name_ref
218 .syntax()
219 .ancestors()
220 .find_map(ast::PathExpr::cast)
221 .and_then(|p| p.path())
222 .and_then(|p| p.qualifier())
223 .and_then(|p| p.segment())
224 .map(|p| p.name_ref().as_ref() == Some(name_ref))
225 .unwrap_or(false)
226}
227
238#[cfg(test)] 228#[cfg(test)]
239mod tests { 229mod tests {
240 use expect_test::{expect, Expect}; 230 use expect_test::{expect, Expect};
@@ -259,9 +249,9 @@ fn main() {
259} 249}
260"#, 250"#,
261 expect![[r#" 251 expect![[r#"
262 Foo Struct FileId(0) 0..26 7..10 Other 252 Foo Struct FileId(0) 0..26 7..10
263 253
264 FileId(0) 101..104 StructLiteral 254 FileId(0) 101..104
265 "#]], 255 "#]],
266 ); 256 );
267 } 257 }
@@ -277,10 +267,10 @@ struct Foo$0 {}
277} 267}
278"#, 268"#,
279 expect![[r#" 269 expect![[r#"
280 Foo Struct FileId(0) 0..13 7..10 Other 270 Foo Struct FileId(0) 0..13 7..10
281 271
282 FileId(0) 41..44 Other 272 FileId(0) 41..44
283 FileId(0) 54..57 StructLiteral 273 FileId(0) 54..57
284 "#]], 274 "#]],
285 ); 275 );
286 } 276 }
@@ -296,9 +286,9 @@ struct Foo<T> $0{}
296} 286}
297"#, 287"#,
298 expect![[r#" 288 expect![[r#"
299 Foo Struct FileId(0) 0..16 7..10 Other 289 Foo Struct FileId(0) 0..16 7..10
300 290
301 FileId(0) 64..67 StructLiteral 291 FileId(0) 64..67
302 "#]], 292 "#]],
303 ); 293 );
304 } 294 }
@@ -315,9 +305,9 @@ fn main() {
315} 305}
316"#, 306"#,
317 expect![[r#" 307 expect![[r#"
318 Foo Struct FileId(0) 0..16 7..10 Other 308 Foo Struct FileId(0) 0..16 7..10
319 309
320 FileId(0) 54..57 StructLiteral 310 FileId(0) 54..57
321 "#]], 311 "#]],
322 ); 312 );
323 } 313 }
@@ -336,9 +326,9 @@ fn main() {
336} 326}
337"#, 327"#,
338 expect![[r#" 328 expect![[r#"
339 Foo Enum FileId(0) 0..26 5..8 Other 329 Foo Enum FileId(0) 0..26 5..8
340 330
341 FileId(0) 63..66 EnumLiteral 331 FileId(0) 63..66
342 "#]], 332 "#]],
343 ); 333 );
344 } 334 }
@@ -357,10 +347,10 @@ fn main() {
357} 347}
358"#, 348"#,
359 expect![[r#" 349 expect![[r#"
360 Foo Enum FileId(0) 0..26 5..8 Other 350 Foo Enum FileId(0) 0..26 5..8
361 351
362 FileId(0) 50..53 Other 352 FileId(0) 50..53
363 FileId(0) 63..66 EnumLiteral 353 FileId(0) 63..66
364 "#]], 354 "#]],
365 ); 355 );
366 } 356 }
@@ -379,9 +369,9 @@ fn main() {
379} 369}
380"#, 370"#,
381 expect![[r#" 371 expect![[r#"
382 Foo Enum FileId(0) 0..32 5..8 Other 372 Foo Enum FileId(0) 0..32 5..8
383 373
384 FileId(0) 73..76 EnumLiteral 374 FileId(0) 73..76
385 "#]], 375 "#]],
386 ); 376 );
387 } 377 }
@@ -400,9 +390,9 @@ fn main() {
400} 390}
401"#, 391"#,
402 expect![[r#" 392 expect![[r#"
403 Foo Enum FileId(0) 0..33 5..8 Other 393 Foo Enum FileId(0) 0..33 5..8
404 394
405 FileId(0) 70..73 EnumLiteral 395 FileId(0) 70..73
406 "#]], 396 "#]],
407 ); 397 );
408 } 398 }
@@ -423,12 +413,12 @@ fn main() {
423 i = 5; 413 i = 5;
424}"#, 414}"#,
425 expect![[r#" 415 expect![[r#"
426 i Local FileId(0) 20..25 24..25 Other Write 416 i Local FileId(0) 20..25 24..25 Write
427 417
428 FileId(0) 50..51 Other Write 418 FileId(0) 50..51 Write
429 FileId(0) 54..55 Other Read 419 FileId(0) 54..55 Read
430 FileId(0) 76..77 Other Write 420 FileId(0) 76..77 Write
431 FileId(0) 94..95 Other Write 421 FileId(0) 94..95 Write
432 "#]], 422 "#]],
433 ); 423 );
434 } 424 }
@@ -447,10 +437,10 @@ fn bar() {
447} 437}
448"#, 438"#,
449 expect![[r#" 439 expect![[r#"
450 spam Local FileId(0) 19..23 19..23 Other 440 spam Local FileId(0) 19..23 19..23
451 441
452 FileId(0) 34..38 Other Read 442 FileId(0) 34..38 Read
453 FileId(0) 41..45 Other Read 443 FileId(0) 41..45 Read
454 "#]], 444 "#]],
455 ); 445 );
456 } 446 }
@@ -462,9 +452,9 @@ fn bar() {
462fn foo(i : u32) -> u32 { i$0 } 452fn foo(i : u32) -> u32 { i$0 }
463"#, 453"#,
464 expect![[r#" 454 expect![[r#"
465 i ValueParam FileId(0) 7..8 7..8 Other 455 i ValueParam FileId(0) 7..8 7..8
466 456
467 FileId(0) 25..26 Other Read 457 FileId(0) 25..26 Read
468 "#]], 458 "#]],
469 ); 459 );
470 } 460 }
@@ -476,9 +466,9 @@ fn foo(i : u32) -> u32 { i$0 }
476fn foo(i$0 : u32) -> u32 { i } 466fn foo(i$0 : u32) -> u32 { i }
477"#, 467"#,
478 expect![[r#" 468 expect![[r#"
479 i ValueParam FileId(0) 7..8 7..8 Other 469 i ValueParam FileId(0) 7..8 7..8
480 470
481 FileId(0) 25..26 Other Read 471 FileId(0) 25..26 Read
482 "#]], 472 "#]],
483 ); 473 );
484 } 474 }
@@ -497,9 +487,9 @@ fn main(s: Foo) {
497} 487}
498"#, 488"#,
499 expect![[r#" 489 expect![[r#"
500 spam Field FileId(0) 17..30 21..25 Other 490 spam Field FileId(0) 17..30 21..25
501 491
502 FileId(0) 67..71 Other Read 492 FileId(0) 67..71 Read
503 "#]], 493 "#]],
504 ); 494 );
505 } 495 }
@@ -514,7 +504,7 @@ impl Foo {
514} 504}
515"#, 505"#,
516 expect![[r#" 506 expect![[r#"
517 f Function FileId(0) 27..43 30..31 Other 507 f Function FileId(0) 27..43 30..31
518 508
519 "#]], 509 "#]],
520 ); 510 );
@@ -531,7 +521,7 @@ enum Foo {
531} 521}
532"#, 522"#,
533 expect![[r#" 523 expect![[r#"
534 B Variant FileId(0) 22..23 22..23 Other 524 B Variant FileId(0) 22..23 22..23
535 525
536 "#]], 526 "#]],
537 ); 527 );
@@ -548,7 +538,7 @@ enum Foo {
548} 538}
549"#, 539"#,
550 expect![[r#" 540 expect![[r#"
551 field Field FileId(0) 26..35 26..31 Other 541 field Field FileId(0) 26..35 26..31
552 542
553 "#]], 543 "#]],
554 ); 544 );
@@ -589,10 +579,10 @@ fn f() {
589} 579}
590"#, 580"#,
591 expect![[r#" 581 expect![[r#"
592 Foo Struct FileId(1) 17..51 28..31 Other 582 Foo Struct FileId(1) 17..51 28..31
593 583
594 FileId(0) 53..56 StructLiteral 584 FileId(0) 53..56
595 FileId(2) 79..82 StructLiteral 585 FileId(2) 79..82
596 "#]], 586 "#]],
597 ); 587 );
598 } 588 }
@@ -619,9 +609,9 @@ pub struct Foo {
619} 609}
620"#, 610"#,
621 expect![[r#" 611 expect![[r#"
622 foo Module FileId(1) 0..35 Other 612 foo Module FileId(1) 0..35
623 613
624 FileId(0) 14..17 Other 614 FileId(0) 14..17
625 "#]], 615 "#]],
626 ); 616 );
627 } 617 }
@@ -647,10 +637,10 @@ pub(super) struct Foo$0 {
647} 637}
648"#, 638"#,
649 expect![[r#" 639 expect![[r#"
650 Foo Struct FileId(2) 0..41 18..21 Other 640 Foo Struct FileId(2) 0..41 18..21
651 641
652 FileId(1) 20..23 Other 642 FileId(1) 20..23
653 FileId(1) 47..50 StructLiteral 643 FileId(1) 47..50
654 "#]], 644 "#]],
655 ); 645 );
656 } 646 }
@@ -675,10 +665,10 @@ pub(super) struct Foo$0 {
675 code, 665 code,
676 None, 666 None,
677 expect![[r#" 667 expect![[r#"
678 quux Function FileId(0) 19..35 26..30 Other 668 quux Function FileId(0) 19..35 26..30
679 669
680 FileId(1) 16..20 StructLiteral 670 FileId(1) 16..20
681 FileId(2) 16..20 StructLiteral 671 FileId(2) 16..20
682 "#]], 672 "#]],
683 ); 673 );
684 674
@@ -686,9 +676,9 @@ pub(super) struct Foo$0 {
686 code, 676 code,
687 Some(SearchScope::single_file(FileId(2))), 677 Some(SearchScope::single_file(FileId(2))),
688 expect![[r#" 678 expect![[r#"
689 quux Function FileId(0) 19..35 26..30 Other 679 quux Function FileId(0) 19..35 26..30
690 680
691 FileId(2) 16..20 StructLiteral 681 FileId(2) 16..20
692 "#]], 682 "#]],
693 ); 683 );
694 } 684 }
@@ -706,10 +696,10 @@ fn foo() {
706} 696}
707"#, 697"#,
708 expect![[r#" 698 expect![[r#"
709 m1 Macro FileId(0) 0..46 29..31 Other 699 m1 Macro FileId(0) 0..46 29..31
710 700
711 FileId(0) 63..65 StructLiteral 701 FileId(0) 63..65
712 FileId(0) 73..75 StructLiteral 702 FileId(0) 73..75
713 "#]], 703 "#]],
714 ); 704 );
715 } 705 }
@@ -724,10 +714,10 @@ fn foo() {
724} 714}
725"#, 715"#,
726 expect![[r#" 716 expect![[r#"
727 i Local FileId(0) 19..24 23..24 Other Write 717 i Local FileId(0) 19..24 23..24 Write
728 718
729 FileId(0) 34..35 Other Write 719 FileId(0) 34..35 Write
730 FileId(0) 38..39 Other Read 720 FileId(0) 38..39 Read
731 "#]], 721 "#]],
732 ); 722 );
733 } 723 }
@@ -746,10 +736,10 @@ fn foo() {
746} 736}
747"#, 737"#,
748 expect![[r#" 738 expect![[r#"
749 f Field FileId(0) 15..21 15..16 Other 739 f Field FileId(0) 15..21 15..16
750 740
751 FileId(0) 55..56 RecordFieldExprOrPat Read 741 FileId(0) 55..56 Read
752 FileId(0) 68..69 Other Write 742 FileId(0) 68..69 Write
753 "#]], 743 "#]],
754 ); 744 );
755 } 745 }
@@ -764,9 +754,9 @@ fn foo() {
764} 754}
765"#, 755"#,
766 expect![[r#" 756 expect![[r#"
767 i Local FileId(0) 19..20 19..20 Other 757 i Local FileId(0) 19..20 19..20
768 758
769 FileId(0) 26..27 Other Write 759 FileId(0) 26..27 Write
770 "#]], 760 "#]],
771 ); 761 );
772 } 762 }
@@ -788,9 +778,9 @@ fn main() {
788} 778}
789"#, 779"#,
790 expect![[r#" 780 expect![[r#"
791 new Function FileId(0) 54..81 61..64 Other 781 new Function FileId(0) 54..81 61..64
792 782
793 FileId(0) 126..129 StructLiteral 783 FileId(0) 126..129
794 "#]], 784 "#]],
795 ); 785 );
796 } 786 }
@@ -810,10 +800,10 @@ use crate::f;
810fn g() { f(); } 800fn g() { f(); }
811"#, 801"#,
812 expect![[r#" 802 expect![[r#"
813 f Function FileId(0) 22..31 25..26 Other 803 f Function FileId(0) 22..31 25..26
814 804
815 FileId(1) 11..12 Other 805 FileId(1) 11..12
816 FileId(1) 24..25 StructLiteral 806 FileId(1) 24..25
817 "#]], 807 "#]],
818 ); 808 );
819 } 809 }
@@ -833,9 +823,9 @@ fn f(s: S) {
833} 823}
834"#, 824"#,
835 expect![[r#" 825 expect![[r#"
836 field Field FileId(0) 15..24 15..20 Other 826 field Field FileId(0) 15..24 15..20
837 827
838 FileId(0) 68..73 FieldShorthandForField Read 828 FileId(0) 68..73 Read
839 "#]], 829 "#]],
840 ); 830 );
841 } 831 }
@@ -857,9 +847,9 @@ fn f(e: En) {
857} 847}
858"#, 848"#,
859 expect![[r#" 849 expect![[r#"
860 field Field FileId(0) 32..41 32..37 Other 850 field Field FileId(0) 32..41 32..37
861 851
862 FileId(0) 102..107 FieldShorthandForField Read 852 FileId(0) 102..107 Read
863 "#]], 853 "#]],
864 ); 854 );
865 } 855 }
@@ -881,9 +871,9 @@ fn f() -> m::En {
881} 871}
882"#, 872"#,
883 expect![[r#" 873 expect![[r#"
884 field Field FileId(0) 56..65 56..61 Other 874 field Field FileId(0) 56..65 56..61
885 875
886 FileId(0) 125..130 RecordFieldExprOrPat Read 876 FileId(0) 125..130 Read
887 "#]], 877 "#]],
888 ); 878 );
889 } 879 }
@@ -906,10 +896,10 @@ impl Foo {
906} 896}
907"#, 897"#,
908 expect![[r#" 898 expect![[r#"
909 self SelfParam FileId(0) 47..51 47..51 SelfParam 899 self SelfParam FileId(0) 47..51 47..51
910 900
911 FileId(0) 71..75 Other Read 901 FileId(0) 71..75 Read
912 FileId(0) 152..156 Other Read 902 FileId(0) 152..156 Read
913 "#]], 903 "#]],
914 ); 904 );
915 } 905 }
@@ -927,9 +917,9 @@ impl Foo {
927} 917}
928"#, 918"#,
929 expect![[r#" 919 expect![[r#"
930 self SelfParam FileId(0) 47..51 47..51 SelfParam 920 self SelfParam FileId(0) 47..51 47..51
931 921
932 FileId(0) 63..67 Other Read 922 FileId(0) 63..67 Read
933 "#]], 923 "#]],
934 ); 924 );
935 } 925 }
@@ -945,7 +935,7 @@ impl Foo {
945 let mut actual = String::new(); 935 let mut actual = String::new();
946 { 936 {
947 let decl = refs.declaration; 937 let decl = refs.declaration;
948 format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); 938 format_to!(actual, "{}", decl.nav.debug_render());
949 if let Some(access) = decl.access { 939 if let Some(access) = decl.access {
950 format_to!(actual, " {:?}", access) 940 format_to!(actual, " {:?}", access)
951 } 941 }
@@ -953,9 +943,9 @@ impl Foo {
953 } 943 }
954 944
955 for (file_id, references) in refs.references { 945 for (file_id, references) in refs.references {
956 for r in references { 946 for (range, access) in references {
957 format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); 947 format_to!(actual, "{:?} {:?}", file_id, range);
958 if let Some(access) = r.access { 948 if let Some(access) = access {
959 format_to!(actual, " {:?}", access); 949 format_to!(actual, " {:?}", access);
960 } 950 }
961 actual += "\n"; 951 actual += "\n";
@@ -976,13 +966,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
976} 966}
977"#, 967"#,
978 expect![[r#" 968 expect![[r#"
979 'a LifetimeParam FileId(0) 55..57 55..57 Lifetime 969 'a LifetimeParam FileId(0) 55..57 55..57
980 970
981 FileId(0) 63..65 Lifetime 971 FileId(0) 63..65
982 FileId(0) 71..73 Lifetime 972 FileId(0) 71..73
983 FileId(0) 82..84 Lifetime 973 FileId(0) 82..84
984 FileId(0) 95..97 Lifetime 974 FileId(0) 95..97
985 FileId(0) 106..108 Lifetime 975 FileId(0) 106..108
986 "#]], 976 "#]],
987 ); 977 );
988 } 978 }
@@ -994,10 +984,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
994type Foo<'a, T> where T: 'a$0 = &'a T; 984type Foo<'a, T> where T: 'a$0 = &'a T;
995"#, 985"#,
996 expect![[r#" 986 expect![[r#"
997 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime 987 'a LifetimeParam FileId(0) 9..11 9..11
998 988
999 FileId(0) 25..27 Lifetime 989 FileId(0) 25..27
1000 FileId(0) 31..33 Lifetime 990 FileId(0) 31..33
1001 "#]], 991 "#]],
1002 ); 992 );
1003 } 993 }
@@ -1016,11 +1006,11 @@ impl<'a> Foo<'a> for &'a () {
1016} 1006}
1017"#, 1007"#,
1018 expect![[r#" 1008 expect![[r#"
1019 'a LifetimeParam FileId(0) 47..49 47..49 Lifetime 1009 'a LifetimeParam FileId(0) 47..49 47..49
1020 1010
1021 FileId(0) 55..57 Lifetime 1011 FileId(0) 55..57
1022 FileId(0) 64..66 Lifetime 1012 FileId(0) 64..66
1023 FileId(0) 89..91 Lifetime 1013 FileId(0) 89..91
1024 "#]], 1014 "#]],
1025 ); 1015 );
1026 } 1016 }
@@ -1036,9 +1026,9 @@ fn main() {
1036} 1026}
1037"#, 1027"#,
1038 expect![[r#" 1028 expect![[r#"
1039 a Local FileId(0) 59..60 59..60 Other 1029 a Local FileId(0) 59..60 59..60
1040 1030
1041 FileId(0) 80..81 Other Read 1031 FileId(0) 80..81 Read
1042 "#]], 1032 "#]],
1043 ); 1033 );
1044 } 1034 }
@@ -1054,9 +1044,9 @@ fn main() {
1054} 1044}
1055"#, 1045"#,
1056 expect![[r#" 1046 expect![[r#"
1057 a Local FileId(0) 59..60 59..60 Other 1047 a Local FileId(0) 59..60 59..60
1058 1048
1059 FileId(0) 80..81 Other Read 1049 FileId(0) 80..81 Read
1060 "#]], 1050 "#]],
1061 ); 1051 );
1062 } 1052 }
@@ -1075,10 +1065,10 @@ fn foo<'a>() -> &'a () {
1075} 1065}
1076"#, 1066"#,
1077 expect![[r#" 1067 expect![[r#"
1078 'a Label FileId(0) 29..32 29..31 Lifetime 1068 'a Label FileId(0) 29..32 29..31
1079 1069
1080 FileId(0) 80..82 Lifetime 1070 FileId(0) 80..82
1081 FileId(0) 108..110 Lifetime 1071 FileId(0) 108..110
1082 "#]], 1072 "#]],
1083 ); 1073 );
1084 } 1074 }
@@ -1092,9 +1082,9 @@ fn foo<const FOO$0: usize>() -> usize {
1092} 1082}
1093"#, 1083"#,
1094 expect![[r#" 1084 expect![[r#"
1095 FOO ConstParam FileId(0) 7..23 13..16 Other 1085 FOO ConstParam FileId(0) 7..23 13..16
1096 1086
1097 FileId(0) 42..45 Other 1087 FileId(0) 42..45
1098 "#]], 1088 "#]],
1099 ); 1089 );
1100 } 1090 }
@@ -1108,9 +1098,9 @@ trait Foo {
1108} 1098}
1109"#, 1099"#,
1110 expect![[r#" 1100 expect![[r#"
1111 Self TypeParam FileId(0) 6..9 6..9 Other 1101 Self TypeParam FileId(0) 6..9 6..9
1112 1102
1113 FileId(0) 26..30 Other 1103 FileId(0) 26..30
1114 "#]], 1104 "#]],
1115 ); 1105 );
1116 } 1106 }
@@ -1131,9 +1121,9 @@ impl Foo {
1131 1121
1132"#, 1122"#,
1133 expect![[r#" 1123 expect![[r#"
1134 Bar Variant FileId(0) 11..16 11..14 Other 1124 Bar Variant FileId(0) 11..16 11..14
1135 1125
1136 FileId(0) 89..92 Other 1126 FileId(0) 89..92
1137 "#]], 1127 "#]],
1138 ); 1128 );
1139 } 1129 }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index ebb1ce7dd..b04214291 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -4,9 +4,9 @@ use std::fmt::{self, Display};
4use either::Either; 4use either::Either;
5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; 5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
6use ide_db::{ 6use ide_db::{
7 base_db::{AnchoredPathBuf, FileId, FileRange}, 7 base_db::{AnchoredPathBuf, FileId},
8 defs::{Definition, NameClass, NameRefClass}, 8 defs::{Definition, NameClass, NameRefClass},
9 search::FileReference, 9 search::{FileReference, NameLike},
10 RootDatabase, 10 RootDatabase,
11}; 11};
12use stdx::never; 12use stdx::never;
@@ -17,10 +17,7 @@ use syntax::{
17use test_utils::mark; 17use test_utils::mark;
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{ 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
21 display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange,
22 TextRange,
23};
24 21
25type RenameResult<T> = Result<T, RenameError>; 22type RenameResult<T> = Result<T, RenameError>;
26#[derive(Debug)] 23#[derive(Debug)]
@@ -41,6 +38,8 @@ macro_rules! bail {
41 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} 38 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
42} 39}
43 40
41/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
42/// being targeted for a rename.
44pub(crate) fn prepare_rename( 43pub(crate) fn prepare_rename(
45 db: &RootDatabase, 44 db: &RootDatabase,
46 position: FilePosition, 45 position: FilePosition,
@@ -123,12 +122,6 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
123 } 122 }
124} 123}
125 124
126enum NameLike {
127 Name(ast::Name),
128 NameRef(ast::NameRef),
129 Lifetime(ast::Lifetime),
130}
131
132fn find_name_like( 125fn find_name_like(
133 sema: &Semantics<RootDatabase>, 126 sema: &Semantics<RootDatabase>,
134 syntax: &SyntaxNode, 127 syntax: &SyntaxNode,
@@ -171,72 +164,97 @@ fn find_definition(
171} 164}
172 165
173fn source_edit_from_references( 166fn source_edit_from_references(
174 sema: &Semantics<RootDatabase>, 167 _sema: &Semantics<RootDatabase>,
175 file_id: FileId, 168 file_id: FileId,
176 references: &[FileReference], 169 references: &[FileReference],
170 def: Definition,
177 new_name: &str, 171 new_name: &str,
178) -> (FileId, TextEdit) { 172) -> (FileId, TextEdit) {
179 let mut edit = TextEdit::builder(); 173 let mut edit = TextEdit::builder();
180 for reference in references { 174 for reference in references {
181 let mut replacement_text = String::new(); 175 let (range, replacement) = match &reference.name {
182 let range = match reference.kind { 176 NameLike::Name(_) => (None, format!("{}", new_name)),
183 ReferenceKind::FieldShorthandForField => { 177 NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def),
184 mark::hit!(test_rename_struct_field_for_shorthand); 178 NameLike::Lifetime(_) => (None, format!("{}", new_name)),
185 replacement_text.push_str(new_name);
186 replacement_text.push_str(": ");
187 TextRange::new(reference.range.start(), reference.range.start())
188 }
189 ReferenceKind::FieldShorthandForLocal => {
190 mark::hit!(test_rename_local_for_field_shorthand);
191 replacement_text.push_str(": ");
192 replacement_text.push_str(new_name);
193 TextRange::new(reference.range.end(), reference.range.end())
194 }
195 ReferenceKind::RecordFieldExprOrPat => {
196 mark::hit!(test_rename_field_expr_pat);
197 replacement_text.push_str(new_name);
198 edit_text_range_for_record_field_expr_or_pat(
199 sema,
200 FileRange { file_id, range: reference.range },
201 new_name,
202 )
203 }
204 _ => {
205 replacement_text.push_str(new_name);
206 reference.range
207 }
208 }; 179 };
209 edit.replace(range, replacement_text); 180 // FIXME: Some(range) will be incorrect when we are inside macros
181 edit.replace(range.unwrap_or(reference.range), replacement);
210 } 182 }
211 (file_id, edit.finish()) 183 (file_id, edit.finish())
212} 184}
213 185
214fn edit_text_range_for_record_field_expr_or_pat( 186fn source_edit_from_name_ref(
215 sema: &Semantics<RootDatabase>, 187 name_ref: &ast::NameRef,
216 file_range: FileRange,
217 new_name: &str, 188 new_name: &str,
218) -> TextRange { 189 def: Definition,
219 let source_file = sema.parse(file_range.file_id); 190) -> (Option<TextRange>, String) {
220 let file_syntax = source_file.syntax(); 191 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
221 let original_range = file_range.range; 192 let rcf_name_ref = record_field.name_ref();
222 193 let rcf_expr = record_field.expr();
223 syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) 194 match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
224 .and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) { 195 // field: init-expr, check if we can use a field init shorthand
225 Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()), 196 (Some(field_name), Some(init)) => {
226 _ => None, 197 if field_name == *name_ref {
227 }) 198 if init.text() == new_name {
228 .or_else(|| { 199 mark::hit!(test_rename_field_put_init_shorthand);
229 syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) 200 // same names, we can use a shorthand here instead
230 .and_then(|field_pat| match field_pat.pat() { 201 // we do not want to erase attributes hence this range start
231 Some(ast::Pat::IdentPat(pat)) 202 let s = field_name.syntax().text_range().start();
232 if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => 203 let e = record_field.syntax().text_range().end();
233 { 204 return (Some(TextRange::new(s, e)), format!("{}", new_name));
234 Some(field_pat.syntax().text_range())
235 } 205 }
236 _ => None, 206 } else if init == *name_ref {
237 }) 207 if field_name.text() == new_name {
238 }) 208 mark::hit!(test_rename_local_put_init_shorthand);
239 .unwrap_or(original_range) 209 // same names, we can use a shorthand here instead
210 // we do not want to erase attributes hence this range start
211 let s = field_name.syntax().text_range().start();
212 let e = record_field.syntax().text_range().end();
213 return (Some(TextRange::new(s, e)), format!("{}", new_name));
214 }
215 }
216 }
217 // init shorthand
218 (None, Some(_)) => {
219 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
220 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
221 match def {
222 Definition::Field(_) => {
223 mark::hit!(test_rename_field_in_field_shorthand);
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 }
235 _ => {}
236 }
237 }
238 if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
239 let rcf_name_ref = record_field.name_ref();
240 let rcf_pat = record_field.pat();
241 match (rcf_name_ref, rcf_pat) {
242 // field: rename
243 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
244 // field name is being renamed
245 if pat.name().map_or(false, |it| it.text() == new_name) {
246 mark::hit!(test_rename_field_put_init_shorthand_pat);
247 // same names, we can use a shorthand here instead
248 // we do not want to erase attributes hence this range start
249 let s = field_name.syntax().text_range().start();
250 let e = record_field.syntax().text_range().end();
251 return (Some(TextRange::new(s, e)), format!("{}", new_name));
252 }
253 }
254 _ => {}
255 }
256 }
257 (None, format!("{}", new_name))
240} 258}
241 259
242fn rename_mod( 260fn rename_mod(
@@ -277,7 +295,7 @@ fn rename_mod(
277 let def = Definition::ModuleDef(ModuleDef::Module(module)); 295 let def = Definition::ModuleDef(ModuleDef::Module(module));
278 let usages = def.usages(sema).all(); 296 let usages = def.usages(sema).all();
279 let ref_edits = usages.iter().map(|(&file_id, references)| { 297 let ref_edits = usages.iter().map(|(&file_id, references)| {
280 source_edit_from_references(sema, file_id, references, new_name) 298 source_edit_from_references(sema, file_id, references, def, new_name)
281 }); 299 });
282 source_change.extend(ref_edits); 300 source_change.extend(ref_edits);
283 301
@@ -346,7 +364,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
346 let usages = def.usages(sema).all(); 364 let usages = def.usages(sema).all();
347 let mut source_change = SourceChange::default(); 365 let mut source_change = SourceChange::default();
348 source_change.extend(usages.iter().map(|(&file_id, references)| { 366 source_change.extend(usages.iter().map(|(&file_id, references)| {
349 source_edit_from_references(sema, file_id, references, "self") 367 source_edit_from_references(sema, file_id, references, def, "self")
350 })); 368 }));
351 source_change.insert_source_edit( 369 source_change.insert_source_edit(
352 file_id.original_file(sema.db), 370 file_id.original_file(sema.db),
@@ -403,7 +421,7 @@ fn rename_self_to_param(
403 let mut source_change = SourceChange::default(); 421 let mut source_change = SourceChange::default();
404 source_change.insert_source_edit(file_id.original_file(sema.db), edit); 422 source_change.insert_source_edit(file_id.original_file(sema.db), edit);
405 source_change.extend(usages.iter().map(|(&file_id, references)| { 423 source_change.extend(usages.iter().map(|(&file_id, references)| {
406 source_edit_from_references(sema, file_id, &references, new_name) 424 source_edit_from_references(sema, file_id, &references, def, new_name)
407 })); 425 }));
408 Ok(source_change) 426 Ok(source_change)
409} 427}
@@ -457,7 +475,7 @@ fn rename_reference(
457 } 475 }
458 let mut source_change = SourceChange::default(); 476 let mut source_change = SourceChange::default();
459 source_change.extend(usages.iter().map(|(&file_id, references)| { 477 source_change.extend(usages.iter().map(|(&file_id, references)| {
460 source_edit_from_references(sema, file_id, &references, new_name) 478 source_edit_from_references(sema, file_id, &references, def, new_name)
461 })); 479 }));
462 480
463 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?; 481 let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
@@ -545,10 +563,8 @@ mod tests {
545 563
546 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { 564 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
547 let (analysis, position) = fixture::position(ra_fixture); 565 let (analysis, position) = fixture::position(ra_fixture);
548 let source_change = analysis 566 let source_change =
549 .rename(position, new_name) 567 analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
550 .unwrap()
551 .expect("Expect returned RangeInfo to be Some, but was None");
552 expect.assert_debug_eq(&source_change) 568 expect.assert_debug_eq(&source_change)
553 } 569 }
554 570
@@ -792,8 +808,8 @@ impl Foo {
792 } 808 }
793 809
794 #[test] 810 #[test]
795 fn test_rename_struct_field_for_shorthand() { 811 fn test_rename_field_in_field_shorthand() {
796 mark::check!(test_rename_struct_field_for_shorthand); 812 mark::check!(test_rename_field_in_field_shorthand);
797 check( 813 check(
798 "j", 814 "j",
799 r#" 815 r#"
@@ -818,8 +834,8 @@ impl Foo {
818 } 834 }
819 835
820 #[test] 836 #[test]
821 fn test_rename_local_for_field_shorthand() { 837 fn test_rename_local_in_field_shorthand() {
822 mark::check!(test_rename_local_for_field_shorthand); 838 mark::check!(test_rename_local_in_field_shorthand);
823 check( 839 check(
824 "j", 840 "j",
825 r#" 841 r#"
@@ -1417,8 +1433,8 @@ impl Foo {
1417 } 1433 }
1418 1434
1419 #[test] 1435 #[test]
1420 fn test_initializer_use_field_init_shorthand() { 1436 fn test_rename_field_put_init_shorthand() {
1421 mark::check!(test_rename_field_expr_pat); 1437 mark::check!(test_rename_field_put_init_shorthand);
1422 check( 1438 check(
1423 "bar", 1439 "bar",
1424 r#" 1440 r#"
@@ -1439,7 +1455,30 @@ fn foo(bar: i32) -> Foo {
1439 } 1455 }
1440 1456
1441 #[test] 1457 #[test]
1458 fn test_rename_local_put_init_shorthand() {
1459 mark::check!(test_rename_local_put_init_shorthand);
1460 check(
1461 "i",
1462 r#"
1463struct Foo { i: i32 }
1464
1465fn foo(bar$0: i32) -> Foo {
1466 Foo { i: bar }
1467}
1468"#,
1469 r#"
1470struct Foo { i: i32 }
1471
1472fn foo(i: i32) -> Foo {
1473 Foo { i }
1474}
1475"#,
1476 );
1477 }
1478
1479 #[test]
1442 fn test_struct_field_destructure_into_shorthand() { 1480 fn test_struct_field_destructure_into_shorthand() {
1481 mark::check!(test_rename_field_put_init_shorthand_pat);
1443 check( 1482 check(
1444 "baz", 1483 "baz",
1445 r#" 1484 r#"
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index b9ba0aed5..38b20f2dc 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -53,24 +53,36 @@ impl IntoIterator for UsageSearchResult {
53} 53}
54 54
55#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
56pub enum NameLike {
57 NameRef(ast::NameRef),
58 Name(ast::Name),
59 Lifetime(ast::Lifetime),
60}
61
62impl NameLike {
63 pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
64 match self {
65 NameLike::NameRef(name_ref) => Some(name_ref),
66 _ => None,
67 }
68 }
69}
70
71mod __ {
72 use super::{
73 ast::{Lifetime, Name, NameRef},
74 NameLike,
75 };
76 stdx::impl_from!(NameRef, Name, Lifetime for NameLike);
77}
78
79#[derive(Debug, Clone)]
56pub struct FileReference { 80pub struct FileReference {
57 pub range: TextRange, 81 pub range: TextRange,
58 pub kind: ReferenceKind, 82 pub name: NameLike,
59 pub access: Option<ReferenceAccess>, 83 pub access: Option<ReferenceAccess>,
60} 84}
61 85
62#[derive(Debug, Clone, PartialEq)]
63pub enum ReferenceKind {
64 FieldShorthandForField,
65 FieldShorthandForLocal,
66 StructLiteral,
67 RecordFieldExprOrPat,
68 SelfParam,
69 EnumLiteral,
70 Lifetime,
71 Other,
72}
73
74#[derive(Debug, Copy, Clone, PartialEq)] 86#[derive(Debug, Copy, Clone, PartialEq)]
75pub enum ReferenceAccess { 87pub enum ReferenceAccess {
76 Read, 88 Read,
@@ -369,8 +381,11 @@ impl<'a> FindUsages<'a> {
369 match NameRefClass::classify_lifetime(self.sema, lifetime) { 381 match NameRefClass::classify_lifetime(self.sema, lifetime) {
370 Some(NameRefClass::Definition(def)) if &def == self.def => { 382 Some(NameRefClass::Definition(def)) if &def == self.def => {
371 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); 383 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
372 let reference = 384 let reference = FileReference {
373 FileReference { range, kind: ReferenceKind::Lifetime, access: None }; 385 range,
386 name: NameLike::Lifetime(lifetime.clone()),
387 access: None,
388 };
374 sink(file_id, reference) 389 sink(file_id, reference)
375 } 390 }
376 _ => false, // not a usage 391 _ => false, // not a usage
@@ -384,19 +399,12 @@ impl<'a> FindUsages<'a> {
384 ) -> bool { 399 ) -> bool {
385 match NameRefClass::classify(self.sema, &name_ref) { 400 match NameRefClass::classify(self.sema, &name_ref) {
386 Some(NameRefClass::Definition(def)) if &def == self.def => { 401 Some(NameRefClass::Definition(def)) if &def == self.def => {
387 let kind = if is_record_field_expr_or_pat(&name_ref) {
388 ReferenceKind::RecordFieldExprOrPat
389 } else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
390 ReferenceKind::StructLiteral
391 } else if is_enum_lit_name_ref(&name_ref) {
392 ReferenceKind::EnumLiteral
393 } else {
394 ReferenceKind::Other
395 };
396
397 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 402 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
398 let reference = 403 let reference = FileReference {
399 FileReference { range, kind, access: reference_access(&def, &name_ref) }; 404 range,
405 name: NameLike::NameRef(name_ref.clone()),
406 access: reference_access(&def, &name_ref),
407 };
400 sink(file_id, reference) 408 sink(file_id, reference)
401 } 409 }
402 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 410 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
@@ -404,12 +412,12 @@ impl<'a> FindUsages<'a> {
404 let reference = match self.def { 412 let reference = match self.def {
405 Definition::Field(_) if &field == self.def => FileReference { 413 Definition::Field(_) if &field == self.def => FileReference {
406 range, 414 range,
407 kind: ReferenceKind::FieldShorthandForField, 415 name: NameLike::NameRef(name_ref.clone()),
408 access: reference_access(&field, &name_ref), 416 access: reference_access(&field, &name_ref),
409 }, 417 },
410 Definition::Local(l) if &local == l => FileReference { 418 Definition::Local(l) if &local == l => FileReference {
411 range, 419 range,
412 kind: ReferenceKind::FieldShorthandForLocal, 420 name: NameLike::NameRef(name_ref.clone()),
413 access: reference_access(&Definition::Local(local), &name_ref), 421 access: reference_access(&Definition::Local(local), &name_ref),
414 }, 422 },
415 _ => return false, // not a usage 423 _ => return false, // not a usage
@@ -433,7 +441,7 @@ impl<'a> FindUsages<'a> {
433 let FileRange { file_id, range } = self.sema.original_range(name.syntax()); 441 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
434 let reference = FileReference { 442 let reference = FileReference {
435 range, 443 range,
436 kind: ReferenceKind::FieldShorthandForField, 444 name: NameLike::Name(name.clone()),
437 // FIXME: mutable patterns should have `Write` access 445 // FIXME: mutable patterns should have `Write` access
438 access: Some(ReferenceAccess::Read), 446 access: Some(ReferenceAccess::Read),
439 }; 447 };
@@ -473,54 +481,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen
473 // Default Locals and Fields to read 481 // Default Locals and Fields to read
474 mode.or(Some(ReferenceAccess::Read)) 482 mode.or(Some(ReferenceAccess::Read))
475} 483}
476
477fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
478 name_ref
479 .syntax()
480 .ancestors()
481 .find_map(ast::CallExpr::cast)
482 .and_then(|c| match c.expr()? {
483 ast::Expr::PathExpr(p) => {
484 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
485 }
486 _ => None,
487 })
488 .unwrap_or(false)
489}
490
491fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
492 name_ref
493 .syntax()
494 .ancestors()
495 .find_map(ast::RecordExpr::cast)
496 .and_then(|l| l.path())
497 .and_then(|p| p.segment())
498 .map(|p| p.name_ref().as_ref() == Some(name_ref))
499 .unwrap_or(false)
500}
501
502fn is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
503 if let Some(parent) = name_ref.syntax().parent() {
504 match_ast! {
505 match parent {
506 ast::RecordExprField(it) => true,
507 ast::RecordPatField(_it) => true,
508 _ => false,
509 }
510 }
511 } else {
512 false
513 }
514}
515
516fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool {
517 name_ref
518 .syntax()
519 .ancestors()
520 .find_map(ast::PathExpr::cast)
521 .and_then(|p| p.path())
522 .and_then(|p| p.qualifier())
523 .and_then(|p| p.segment())
524 .map(|p| p.name_ref().as_ref() == Some(name_ref))
525 .unwrap_or(false)
526}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 5a6501216..8898c12e3 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -827,18 +827,23 @@ pub(crate) fn handle_references(
827 Some(refs) => refs, 827 Some(refs) => refs,
828 }; 828 };
829 829
830 let locations = if params.context.include_declaration { 830 let decl = if params.context.include_declaration {
831 refs.references_with_declaration() 831 Some(FileRange {
832 .file_ranges() 832 file_id: refs.declaration.nav.file_id,
833 .filter_map(|frange| to_proto::location(&snap, frange).ok()) 833 range: refs.declaration.nav.focus_or_full_range(),
834 .collect() 834 })
835 } else { 835 } else {
836 // Only iterate over the references if include_declaration was false 836 None
837 refs.references()
838 .file_ranges()
839 .filter_map(|frange| to_proto::location(&snap, frange).ok())
840 .collect()
841 }; 837 };
838 let locations = refs
839 .references
840 .into_iter()
841 .flat_map(|(file_id, refs)| {
842 refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
843 })
844 .chain(decl)
845 .filter_map(|frange| to_proto::location(&snap, frange).ok())
846 .collect();
842 847
843 Ok(Some(locations)) 848 Ok(Some(locations))
844} 849}
@@ -1214,8 +1219,11 @@ pub(crate) fn handle_code_lens_resolve(
1214 .find_all_refs(position, None) 1219 .find_all_refs(position, None)
1215 .unwrap_or(None) 1220 .unwrap_or(None)
1216 .map(|r| { 1221 .map(|r| {
1217 r.references() 1222 r.references
1218 .file_ranges() 1223 .into_iter()
1224 .flat_map(|(file_id, ranges)| {
1225 ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
1226 })
1219 .filter_map(|frange| to_proto::location(&snap, frange).ok()) 1227 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1220 .collect_vec() 1228 .collect_vec()
1221 }) 1229 })
@@ -1259,17 +1267,26 @@ pub(crate) fn handle_document_highlight(
1259 Some(refs) => refs, 1267 Some(refs) => refs,
1260 }; 1268 };
1261 1269
1270 let decl = if refs.declaration.nav.file_id == position.file_id {
1271 Some(DocumentHighlight {
1272 range: to_proto::range(&line_index, refs.declaration.nav.focus_or_full_range()),
1273 kind: refs.declaration.access.map(to_proto::document_highlight_kind),
1274 })
1275 } else {
1276 None
1277 };
1278
1262 let res = refs 1279 let res = refs
1263 .references_with_declaration()
1264 .references 1280 .references
1265 .get(&position.file_id) 1281 .get(&position.file_id)
1266 .map(|file_refs| { 1282 .map(|file_refs| {
1267 file_refs 1283 file_refs
1268 .into_iter() 1284 .into_iter()
1269 .map(|r| DocumentHighlight { 1285 .map(|&(range, access)| DocumentHighlight {
1270 range: to_proto::range(&line_index, r.range), 1286 range: to_proto::range(&line_index, range),
1271 kind: r.access.map(to_proto::document_highlight_kind), 1287 kind: access.map(to_proto::document_highlight_kind),
1272 }) 1288 })
1289 .chain(decl)
1273 .collect() 1290 .collect()
1274 }) 1291 })
1275 .unwrap_or_default(); 1292 .unwrap_or_default();
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 5c8cf900f..b105cb0e0 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -274,10 +274,7 @@ impl ast::Struct {
274 274
275impl ast::RecordExprField { 275impl ast::RecordExprField {
276 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> { 276 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
277 let candidate = 277 let candidate = Self::for_name_ref(field_name)?;
278 field_name.syntax().parent().and_then(ast::RecordExprField::cast).or_else(|| {
279 field_name.syntax().ancestors().nth(4).and_then(ast::RecordExprField::cast)
280 })?;
281 if candidate.field_name().as_ref() == Some(field_name) { 278 if candidate.field_name().as_ref() == Some(field_name) {
282 Some(candidate) 279 Some(candidate)
283 } else { 280 } else {
@@ -285,6 +282,13 @@ impl ast::RecordExprField {
285 } 282 }
286 } 283 }
287 284
285 pub fn for_name_ref(name_ref: &ast::NameRef) -> Option<ast::RecordExprField> {
286 let syn = name_ref.syntax();
287 syn.parent()
288 .and_then(ast::RecordExprField::cast)
289 .or_else(|| syn.ancestors().nth(4).and_then(ast::RecordExprField::cast))
290 }
291
288 /// Deals with field init shorthand 292 /// Deals with field init shorthand
289 pub fn field_name(&self) -> Option<ast::NameRef> { 293 pub fn field_name(&self) -> Option<ast::NameRef> {
290 if let Some(name_ref) = self.name_ref() { 294 if let Some(name_ref) = self.name_ref() {
@@ -294,6 +298,7 @@ impl ast::RecordExprField {
294 } 298 }
295} 299}
296 300
301#[derive(Debug, Clone, PartialEq)]
297pub enum NameOrNameRef { 302pub enum NameOrNameRef {
298 Name(ast::Name), 303 Name(ast::Name),
299 NameRef(ast::NameRef), 304 NameRef(ast::NameRef),
@@ -309,6 +314,23 @@ impl fmt::Display for NameOrNameRef {
309} 314}
310 315
311impl ast::RecordPatField { 316impl ast::RecordPatField {
317 pub fn for_field_name_ref(field_name: &ast::NameRef) -> Option<ast::RecordPatField> {
318 let candidate = field_name.syntax().parent().and_then(ast::RecordPatField::cast)?;
319 match candidate.field_name()? {
320 NameOrNameRef::NameRef(name_ref) if name_ref == *field_name => Some(candidate),
321 _ => None,
322 }
323 }
324
325 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
326 let candidate =
327 field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?;
328 match candidate.field_name()? {
329 NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
330 _ => None,
331 }
332 }
333
312 /// Deals with field init shorthand 334 /// Deals with field init shorthand
313 pub fn field_name(&self) -> Option<NameOrNameRef> { 335 pub fn field_name(&self) -> Option<NameOrNameRef> {
314 if let Some(name_ref) = self.name_ref() { 336 if let Some(name_ref) = self.name_ref() {