diff options
Diffstat (limited to 'crates/ra_ide/src/references.rs')
-rw-r--r-- | crates/ra_ide/src/references.rs | 157 |
1 files changed, 119 insertions, 38 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 5e2fe1905..a6320bd2f 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -10,13 +10,13 @@ | |||
10 | //! resolved to the search element definition, we get a reference. | 10 | //! resolved to the search element definition, we get a reference. |
11 | 11 | ||
12 | mod classify; | 12 | mod classify; |
13 | mod name_definition; | ||
14 | mod rename; | 13 | mod rename; |
15 | mod search_scope; | 14 | mod search_scope; |
16 | 15 | ||
17 | use hir::{InFile, SourceBinder}; | 16 | use hir::{InFile, SourceBinder}; |
18 | use once_cell::unsync::Lazy; | 17 | use once_cell::unsync::Lazy; |
19 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 18 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
19 | use ra_ide_db::RootDatabase; | ||
20 | use ra_prof::profile; | 20 | use ra_prof::profile; |
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
@@ -24,15 +24,13 @@ use ra_syntax::{ | |||
24 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, | 24 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::{ | 27 | use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
28 | db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, | ||
29 | }; | ||
30 | 28 | ||
31 | pub(crate) use self::{ | 29 | pub(crate) use self::{ |
32 | classify::{classify_name, classify_name_ref}, | 30 | classify::{classify_name, classify_name_ref}, |
33 | name_definition::{NameDefinition, NameKind}, | ||
34 | rename::rename, | 31 | rename::rename, |
35 | }; | 32 | }; |
33 | pub(crate) use ra_ide_db::defs::{NameDefinition, NameKind}; | ||
36 | 34 | ||
37 | pub use self::search_scope::SearchScope; | 35 | pub use self::search_scope::SearchScope; |
38 | 36 | ||
@@ -112,38 +110,32 @@ impl IntoIterator for ReferenceSearchResult { | |||
112 | 110 | ||
113 | pub(crate) fn find_all_refs( | 111 | pub(crate) fn find_all_refs( |
114 | db: &RootDatabase, | 112 | db: &RootDatabase, |
115 | mut position: FilePosition, | 113 | position: FilePosition, |
116 | search_scope: Option<SearchScope>, | 114 | search_scope: Option<SearchScope>, |
117 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 115 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
118 | let parse = db.parse(position.file_id); | 116 | let parse = db.parse(position.file_id); |
119 | let syntax = parse.tree().syntax().clone(); | 117 | let syntax = parse.tree().syntax().clone(); |
120 | 118 | ||
121 | let token = syntax.token_at_offset(position.offset); | 119 | let (opt_name, search_kind) = |
122 | let mut search_kind = ReferenceKind::Other; | 120 | if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) { |
121 | (Some(name), ReferenceKind::StructLiteral) | ||
122 | } else { | ||
123 | (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other) | ||
124 | }; | ||
123 | 125 | ||
124 | if let TokenAtOffset::Between(ref left, ref right) = token { | 126 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?; |
125 | if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN) | ||
126 | && left.kind() != SyntaxKind::IDENT | ||
127 | { | ||
128 | position = FilePosition { offset: left.text_range().start(), ..position }; | ||
129 | search_kind = ReferenceKind::StructLiteral; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; | ||
134 | 127 | ||
135 | let declaration = match def.kind { | 128 | let declaration = match def.kind { |
136 | NameKind::Macro(mac) => mac.to_nav(db), | 129 | NameKind::Macro(mac) => mac.to_nav(db), |
137 | NameKind::Field(field) => field.to_nav(db), | 130 | NameKind::StructField(field) => field.to_nav(db), |
138 | NameKind::AssocItem(assoc) => assoc.to_nav(db), | 131 | NameKind::ModuleDef(def) => NavigationTarget::from_def(db, def)?, |
139 | NameKind::Def(def) => NavigationTarget::from_def(db, def)?, | ||
140 | NameKind::SelfType(imp) => imp.to_nav(db), | 132 | NameKind::SelfType(imp) => imp.to_nav(db), |
141 | NameKind::Local(local) => local.to_nav(db), | 133 | NameKind::Local(local) => local.to_nav(db), |
142 | NameKind::TypeParam(_) => return None, | 134 | NameKind::TypeParam(_) => return None, |
143 | }; | 135 | }; |
144 | 136 | ||
145 | let search_scope = { | 137 | let search_scope = { |
146 | let base = def.search_scope(db); | 138 | let base = SearchScope::for_def(&def, db); |
147 | match search_scope { | 139 | match search_scope { |
148 | None => base, | 140 | None => base, |
149 | Some(scope) => base.intersection(&scope), | 141 | Some(scope) => base.intersection(&scope), |
@@ -170,9 +162,10 @@ fn find_name( | |||
170 | db: &RootDatabase, | 162 | db: &RootDatabase, |
171 | syntax: &SyntaxNode, | 163 | syntax: &SyntaxNode, |
172 | position: FilePosition, | 164 | position: FilePosition, |
165 | opt_name: Option<ast::Name>, | ||
173 | ) -> Option<RangeInfo<(String, NameDefinition)>> { | 166 | ) -> Option<RangeInfo<(String, NameDefinition)>> { |
174 | let mut sb = SourceBinder::new(db); | 167 | let mut sb = SourceBinder::new(db); |
175 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 168 | if let Some(name) = opt_name { |
176 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; | 169 | let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; |
177 | let range = name.syntax().text_range(); | 170 | let range = name.syntax().text_range(); |
178 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); | 171 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
@@ -218,15 +211,8 @@ fn process_definition( | |||
218 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) | 211 | if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) |
219 | { | 212 | { |
220 | if d == def { | 213 | if d == def { |
221 | let kind = if name_ref | 214 | let kind = if is_record_lit_name_ref(&name_ref) |
222 | .syntax() | 215 | || is_call_expr_name_ref(&name_ref) |
223 | .ancestors() | ||
224 | .find_map(ast::RecordLit::cast) | ||
225 | .and_then(|l| l.path()) | ||
226 | .and_then(|p| p.segment()) | ||
227 | .and_then(|p| p.name_ref()) | ||
228 | .map(|n| n == name_ref) | ||
229 | .unwrap_or(false) | ||
230 | { | 216 | { |
231 | ReferenceKind::StructLiteral | 217 | ReferenceKind::StructLiteral |
232 | } else { | 218 | } else { |
@@ -253,7 +239,7 @@ fn decl_access( | |||
253 | range: TextRange, | 239 | range: TextRange, |
254 | ) -> Option<ReferenceAccess> { | 240 | ) -> Option<ReferenceAccess> { |
255 | match kind { | 241 | match kind { |
256 | NameKind::Local(_) | NameKind::Field(_) => {} | 242 | NameKind::Local(_) | NameKind::StructField(_) => {} |
257 | _ => return None, | 243 | _ => return None, |
258 | }; | 244 | }; |
259 | 245 | ||
@@ -273,7 +259,7 @@ fn decl_access( | |||
273 | fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { | 259 | fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { |
274 | // Only Locals and Fields have accesses for now. | 260 | // Only Locals and Fields have accesses for now. |
275 | match kind { | 261 | match kind { |
276 | NameKind::Local(_) | NameKind::Field(_) => {} | 262 | NameKind::Local(_) | NameKind::StructField(_) => {} |
277 | _ => return None, | 263 | _ => return None, |
278 | }; | 264 | }; |
279 | 265 | ||
@@ -301,6 +287,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc | |||
301 | mode.or(Some(ReferenceAccess::Read)) | 287 | mode.or(Some(ReferenceAccess::Read)) |
302 | } | 288 | } |
303 | 289 | ||
290 | fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { | ||
291 | name_ref | ||
292 | .syntax() | ||
293 | .ancestors() | ||
294 | .find_map(ast::RecordLit::cast) | ||
295 | .and_then(|l| l.path()) | ||
296 | .and_then(|p| p.segment()) | ||
297 | .map(|p| p.name_ref().as_ref() == Some(name_ref)) | ||
298 | .unwrap_or(false) | ||
299 | } | ||
300 | |||
301 | fn get_struct_def_name_for_struc_litetal_search( | ||
302 | syntax: &SyntaxNode, | ||
303 | position: FilePosition, | ||
304 | ) -> Option<ast::Name> { | ||
305 | if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { | ||
306 | if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { | ||
307 | return None; | ||
308 | } | ||
309 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) { | ||
310 | return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
311 | } | ||
312 | if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() { | ||
313 | return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name()); | ||
314 | } | ||
315 | } | ||
316 | None | ||
317 | } | ||
318 | |||
319 | fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { | ||
320 | name_ref | ||
321 | .syntax() | ||
322 | .ancestors() | ||
323 | .find_map(ast::CallExpr::cast) | ||
324 | .and_then(|c| match c.expr()? { | ||
325 | ast::Expr::PathExpr(p) => { | ||
326 | Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) | ||
327 | } | ||
328 | _ => None, | ||
329 | }) | ||
330 | .unwrap_or(false) | ||
331 | } | ||
332 | |||
304 | #[cfg(test)] | 333 | #[cfg(test)] |
305 | mod tests { | 334 | mod tests { |
306 | use crate::{ | 335 | use crate::{ |
@@ -309,7 +338,7 @@ mod tests { | |||
309 | }; | 338 | }; |
310 | 339 | ||
311 | #[test] | 340 | #[test] |
312 | fn test_struct_literal() { | 341 | fn test_struct_literal_after_space() { |
313 | let code = r#" | 342 | let code = r#" |
314 | struct Foo <|>{ | 343 | struct Foo <|>{ |
315 | a: i32, | 344 | a: i32, |
@@ -331,6 +360,58 @@ mod tests { | |||
331 | } | 360 | } |
332 | 361 | ||
333 | #[test] | 362 | #[test] |
363 | fn test_struct_literal_befor_space() { | ||
364 | let code = r#" | ||
365 | struct Foo<|> {} | ||
366 | fn main() { | ||
367 | let f: Foo; | ||
368 | f = Foo {}; | ||
369 | }"#; | ||
370 | |||
371 | let refs = get_all_refs(code); | ||
372 | check_result( | ||
373 | refs, | ||
374 | "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other", | ||
375 | &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"], | ||
376 | ); | ||
377 | } | ||
378 | |||
379 | #[test] | ||
380 | fn test_struct_literal_with_generic_type() { | ||
381 | let code = r#" | ||
382 | struct Foo<T> <|>{} | ||
383 | fn main() { | ||
384 | let f: Foo::<i32>; | ||
385 | f = Foo {}; | ||
386 | }"#; | ||
387 | |||
388 | let refs = get_all_refs(code); | ||
389 | check_result( | ||
390 | refs, | ||
391 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
392 | &["FileId(1) [81; 84) StructLiteral"], | ||
393 | ); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn test_struct_literal_for_tuple() { | ||
398 | let code = r#" | ||
399 | struct Foo<|>(i32); | ||
400 | |||
401 | fn main() { | ||
402 | let f: Foo; | ||
403 | f = Foo(1); | ||
404 | }"#; | ||
405 | |||
406 | let refs = get_all_refs(code); | ||
407 | check_result( | ||
408 | refs, | ||
409 | "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other", | ||
410 | &["FileId(1) [71; 74) StructLiteral"], | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
334 | fn test_find_all_refs_for_local() { | 415 | fn test_find_all_refs_for_local() { |
335 | let code = r#" | 416 | let code = r#" |
336 | fn main() { | 417 | fn main() { |
@@ -564,7 +645,7 @@ mod tests { | |||
564 | check_result( | 645 | check_result( |
565 | refs, | 646 | refs, |
566 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 647 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
567 | &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], | 648 | &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"], |
568 | ); | 649 | ); |
569 | 650 | ||
570 | let refs = | 651 | let refs = |
@@ -572,7 +653,7 @@ mod tests { | |||
572 | check_result( | 653 | check_result( |
573 | refs, | 654 | refs, |
574 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", | 655 | "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", |
575 | &["FileId(3) [16; 20) Other"], | 656 | &["FileId(3) [16; 20) StructLiteral"], |
576 | ); | 657 | ); |
577 | } | 658 | } |
578 | 659 | ||
@@ -591,7 +672,7 @@ mod tests { | |||
591 | check_result( | 672 | check_result( |
592 | refs, | 673 | refs, |
593 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", | 674 | "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", |
594 | &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], | 675 | &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"], |
595 | ); | 676 | ); |
596 | } | 677 | } |
597 | 678 | ||