diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 39 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references/classify.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references/name_definition.rs | 5 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references/search_scope.rs | 81 |
5 files changed, 73 insertions, 72 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index d14908b25..1f3fa6c57 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -206,7 +206,7 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> | |||
206 | 206 | ||
207 | #[cfg(test)] | 207 | #[cfg(test)] |
208 | mod tests { | 208 | mod tests { |
209 | // use test_utils::covers; | 209 | use test_utils::covers; |
210 | 210 | ||
211 | use crate::mock_analysis::analysis_and_position; | 211 | use crate::mock_analysis::analysis_and_position; |
212 | 212 | ||
@@ -274,7 +274,7 @@ mod tests { | |||
274 | 274 | ||
275 | #[test] | 275 | #[test] |
276 | fn goto_definition_works_for_macros() { | 276 | fn goto_definition_works_for_macros() { |
277 | // covers!(goto_definition_works_for_macros); | 277 | covers!(goto_definition_works_for_macros); |
278 | check_goto( | 278 | check_goto( |
279 | " | 279 | " |
280 | //- /lib.rs | 280 | //- /lib.rs |
@@ -294,7 +294,7 @@ mod tests { | |||
294 | 294 | ||
295 | #[test] | 295 | #[test] |
296 | fn goto_definition_works_for_macros_from_other_crates() { | 296 | fn goto_definition_works_for_macros_from_other_crates() { |
297 | // covers!(goto_definition_works_for_macros); | 297 | covers!(goto_definition_works_for_macros); |
298 | check_goto( | 298 | check_goto( |
299 | " | 299 | " |
300 | //- /lib.rs | 300 | //- /lib.rs |
@@ -317,7 +317,7 @@ mod tests { | |||
317 | 317 | ||
318 | #[test] | 318 | #[test] |
319 | fn goto_definition_works_for_methods() { | 319 | fn goto_definition_works_for_methods() { |
320 | // covers!(goto_definition_works_for_methods); | 320 | covers!(goto_definition_works_for_methods); |
321 | check_goto( | 321 | check_goto( |
322 | " | 322 | " |
323 | //- /lib.rs | 323 | //- /lib.rs |
@@ -336,7 +336,7 @@ mod tests { | |||
336 | 336 | ||
337 | #[test] | 337 | #[test] |
338 | fn goto_definition_works_for_fields() { | 338 | fn goto_definition_works_for_fields() { |
339 | // covers!(goto_definition_works_for_fields); | 339 | covers!(goto_definition_works_for_fields); |
340 | check_goto( | 340 | check_goto( |
341 | " | 341 | " |
342 | //- /lib.rs | 342 | //- /lib.rs |
@@ -354,7 +354,7 @@ mod tests { | |||
354 | 354 | ||
355 | #[test] | 355 | #[test] |
356 | fn goto_definition_works_for_record_fields() { | 356 | fn goto_definition_works_for_record_fields() { |
357 | // covers!(goto_definition_works_for_record_fields); | 357 | covers!(goto_definition_works_for_record_fields); |
358 | check_goto( | 358 | check_goto( |
359 | " | 359 | " |
360 | //- /lib.rs | 360 | //- /lib.rs |
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index aadd52616..f35d835ac 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -1,4 +1,13 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! This module implements a reference search. |
2 | //! First, the element at the cursor position must be either an `ast::Name` | ||
3 | //! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we | ||
4 | //! try to resolve the direct tree parent of this element, otherwise we | ||
5 | //! already have a definition and just need to get its HIR together with | ||
6 | //! some information that is needed for futher steps of searching. | ||
7 | //! After that, we collect files that might contain references and look | ||
8 | //! for text occurrences of the identifier. If there's an `ast::NameRef` | ||
9 | //! at the index that the match starts at and its tree parent is | ||
10 | //! resolved to the search element definition, we get a reference. | ||
2 | 11 | ||
3 | mod classify; | 12 | mod classify; |
4 | mod name_definition; | 13 | mod name_definition; |
@@ -9,7 +18,7 @@ use once_cell::unsync::Lazy; | |||
9 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | 18 | use ra_db::{SourceDatabase, SourceDatabaseExt}; |
10 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; | 19 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; |
11 | 20 | ||
12 | use crate::{db::RootDatabase, FileId, FilePosition, FileRange, NavigationTarget, RangeInfo}; | 21 | use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo}; |
13 | 22 | ||
14 | pub(crate) use self::{ | 23 | pub(crate) use self::{ |
15 | classify::{classify_name, classify_name_ref}, | 24 | classify::{classify_name, classify_name_ref}, |
@@ -102,16 +111,7 @@ fn process_definition(db: &RootDatabase, def: NameDefinition, name: String) -> V | |||
102 | let scope = def.search_scope(db); | 111 | let scope = def.search_scope(db); |
103 | let mut refs = vec![]; | 112 | let mut refs = vec![]; |
104 | 113 | ||
105 | let is_match = |file_id: FileId, name_ref: &ast::NameRef| -> bool { | 114 | for (file_id, search_range) in scope { |
106 | let classified = classify_name_ref(db, file_id, &name_ref); | ||
107 | if let Some(d) = classified { | ||
108 | d == def | ||
109 | } else { | ||
110 | false | ||
111 | } | ||
112 | }; | ||
113 | |||
114 | for (file_id, text_range) in scope { | ||
115 | let text = db.file_text(file_id); | 115 | let text = db.file_text(file_id); |
116 | let parse = Lazy::new(|| SourceFile::parse(&text)); | 116 | let parse = Lazy::new(|| SourceFile::parse(&text)); |
117 | 117 | ||
@@ -122,19 +122,20 @@ fn process_definition(db: &RootDatabase, def: NameDefinition, name: String) -> V | |||
122 | find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) | 122 | find_node_at_offset::<ast::NameRef>(parse.tree().syntax(), offset) |
123 | { | 123 | { |
124 | let range = name_ref.syntax().text_range(); | 124 | let range = name_ref.syntax().text_range(); |
125 | 125 | if let Some(search_range) = search_range { | |
126 | if let Some(text_range) = text_range { | 126 | if !range.is_subrange(&search_range) { |
127 | if range.is_subrange(&text_range) && is_match(file_id, &name_ref) { | 127 | continue; |
128 | } | ||
129 | } | ||
130 | if let Some(d) = classify_name_ref(db, file_id, &name_ref) { | ||
131 | if d == def { | ||
128 | refs.push(FileRange { file_id, range }); | 132 | refs.push(FileRange { file_id, range }); |
129 | } | 133 | } |
130 | } else if is_match(file_id, &name_ref) { | ||
131 | refs.push(FileRange { file_id, range }); | ||
132 | } | 134 | } |
133 | } | 135 | } |
134 | } | 136 | } |
135 | } | 137 | } |
136 | 138 | refs | |
137 | return refs; | ||
138 | } | 139 | } |
139 | 140 | ||
140 | #[cfg(test)] | 141 | #[cfg(test)] |
diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs index 3beab9861..c8daff9b1 100644 --- a/crates/ra_ide_api/src/references/classify.rs +++ b/crates/ra_ide_api/src/references/classify.rs | |||
@@ -1,8 +1,9 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Functions that are used to classify an element from its definition or reference. |
2 | 2 | ||
3 | use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; | 3 | use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; |
4 | use ra_db::FileId; | 4 | use ra_db::FileId; |
5 | use ra_syntax::{ast, match_ast, AstNode, AstPtr}; | 5 | use ra_syntax::{ast, match_ast, AstNode, AstPtr}; |
6 | use test_utils::tested_by; | ||
6 | 7 | ||
7 | use super::{ | 8 | use super::{ |
8 | name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field}, | 9 | name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field}, |
@@ -111,18 +112,21 @@ pub(crate) fn classify_name_ref( | |||
111 | let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 112 | let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); |
112 | 113 | ||
113 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 114 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
115 | tested_by!(goto_definition_works_for_methods); | ||
114 | if let Some(func) = analyzer.resolve_method_call(&method_call) { | 116 | if let Some(func) = analyzer.resolve_method_call(&method_call) { |
115 | return Some(from_assoc_item(db, func.into())); | 117 | return Some(from_assoc_item(db, func.into())); |
116 | } | 118 | } |
117 | } | 119 | } |
118 | 120 | ||
119 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 121 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
122 | tested_by!(goto_definition_works_for_fields); | ||
120 | if let Some(field) = analyzer.resolve_field(&field_expr) { | 123 | if let Some(field) = analyzer.resolve_field(&field_expr) { |
121 | return Some(from_struct_field(db, field)); | 124 | return Some(from_struct_field(db, field)); |
122 | } | 125 | } |
123 | } | 126 | } |
124 | 127 | ||
125 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { | 128 | if let Some(record_field) = ast::RecordField::cast(parent.clone()) { |
129 | tested_by!(goto_definition_works_for_record_fields); | ||
126 | if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) { | 130 | if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) { |
127 | let variant_def = analyzer.resolve_record_literal(&record_lit)?; | 131 | let variant_def = analyzer.resolve_record_literal(&record_lit)?; |
128 | let hir_path = Path::from_name_ref(name_ref); | 132 | let hir_path = Path::from_name_ref(name_ref); |
@@ -139,6 +143,7 @@ pub(crate) fn classify_name_ref( | |||
139 | let visibility = None; | 143 | let visibility = None; |
140 | 144 | ||
141 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 145 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
146 | tested_by!(goto_definition_works_for_macros); | ||
142 | if let Some(macro_def) = analyzer.resolve_macro_call(db, ¯o_call) { | 147 | if let Some(macro_def) = analyzer.resolve_macro_call(db, ¯o_call) { |
143 | let kind = NameKind::Macro(macro_def); | 148 | let kind = NameKind::Macro(macro_def); |
144 | return Some(NameDefinition { kind, container, visibility }); | 149 | return Some(NameDefinition { kind, container, visibility }); |
@@ -152,7 +157,6 @@ pub(crate) fn classify_name_ref( | |||
152 | AssocItem(item) => Some(from_assoc_item(db, item)), | 157 | AssocItem(item) => Some(from_assoc_item(db, item)), |
153 | LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat), | 158 | LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat), |
154 | LocalBinding(Either::B(par)) => { | 159 | LocalBinding(Either::B(par)) => { |
155 | // Not really supported | ||
156 | let kind = NameKind::SelfParam(par); | 160 | let kind = NameKind::SelfParam(par); |
157 | Some(NameDefinition { kind, container, visibility }) | 161 | Some(NameDefinition { kind, container, visibility }) |
158 | } | 162 | } |
diff --git a/crates/ra_ide_api/src/references/name_definition.rs b/crates/ra_ide_api/src/references/name_definition.rs index 723d97237..4580bc789 100644 --- a/crates/ra_ide_api/src/references/name_definition.rs +++ b/crates/ra_ide_api/src/references/name_definition.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! `NameDefinition` keeps information about the element we want to search references for. |
2 | //! The element is represented by `NameKind`. It's located inside some `container` and | ||
3 | //! has a `visibility`, which defines a search scope. | ||
4 | //! Note that the reference search is possible for not all of the classified items. | ||
2 | 5 | ||
3 | use hir::{ | 6 | use hir::{ |
4 | db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef, | 7 | db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef, |
diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs index 8495a92a5..5cb69b8fc 100644 --- a/crates/ra_ide_api/src/references/search_scope.rs +++ b/crates/ra_ide_api/src/references/search_scope.rs | |||
@@ -1,72 +1,66 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Generally, `search_scope` returns files that might contain references for the element. |
2 | 2 | //! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. | |
3 | use std::collections::HashSet; | 3 | //! In some cases, the location of the references is known to within a `TextRange`, |
4 | //! e.g. for things like local variables. | ||
4 | 5 | ||
5 | use hir::{DefWithBody, HasSource, ModuleSource}; | 6 | use hir::{DefWithBody, HasSource, ModuleSource}; |
6 | use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; | 7 | use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; |
7 | use ra_syntax::{AstNode, TextRange}; | 8 | use ra_syntax::{AstNode, TextRange}; |
9 | use rustc_hash::FxHashSet; | ||
8 | 10 | ||
9 | use crate::db::RootDatabase; | 11 | use crate::db::RootDatabase; |
10 | 12 | ||
11 | use super::{NameDefinition, NameKind}; | 13 | use super::{NameDefinition, NameKind}; |
12 | 14 | ||
13 | impl NameDefinition { | 15 | impl NameDefinition { |
14 | pub(crate) fn search_scope(&self, db: &RootDatabase) -> HashSet<(FileId, Option<TextRange>)> { | 16 | pub(crate) fn search_scope(&self, db: &RootDatabase) -> FxHashSet<(FileId, Option<TextRange>)> { |
15 | let module_src = self.container.definition_source(db); | 17 | let module_src = self.container.definition_source(db); |
16 | let file_id = module_src.file_id.original_file(db); | 18 | let file_id = module_src.file_id.original_file(db); |
17 | 19 | ||
18 | if let NameKind::Pat((def, _)) = self.kind { | 20 | if let NameKind::Pat((def, _)) = self.kind { |
21 | let mut res = FxHashSet::default(); | ||
19 | let range = match def { | 22 | let range = match def { |
20 | DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(), | 23 | DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(), |
21 | DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), | 24 | DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), |
22 | DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), | 25 | DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), |
23 | }; | 26 | }; |
24 | return [(file_id, Some(range))].iter().cloned().collect(); | 27 | res.insert((file_id, Some(range))); |
28 | return res; | ||
25 | } | 29 | } |
26 | 30 | ||
27 | if let Some(ref vis) = self.visibility { | 31 | let vis = |
28 | let vis = vis.syntax().to_string(); | 32 | self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or("".to_string()); |
29 | |||
30 | // FIXME: add "pub(in path)" | ||
31 | |||
32 | if vis.as_str() == "pub(super)" { | ||
33 | if let Some(parent_module) = self.container.parent(db) { | ||
34 | let mut files = HashSet::new(); | ||
35 | 33 | ||
36 | let parent_src = parent_module.definition_source(db); | 34 | if vis.as_str() == "pub(super)" { |
37 | let file_id = parent_src.file_id.original_file(db); | 35 | if let Some(parent_module) = self.container.parent(db) { |
36 | let mut files = FxHashSet::default(); | ||
37 | let parent_src = parent_module.definition_source(db); | ||
38 | let file_id = parent_src.file_id.original_file(db); | ||
38 | 39 | ||
39 | match parent_src.ast { | 40 | match parent_src.ast { |
40 | ModuleSource::Module(m) => { | 41 | ModuleSource::Module(m) => { |
41 | let range = Some(m.syntax().text_range()); | 42 | let range = Some(m.syntax().text_range()); |
42 | files.insert((file_id, range)); | 43 | files.insert((file_id, range)); |
43 | } | 44 | } |
44 | ModuleSource::SourceFile(_) => { | 45 | ModuleSource::SourceFile(_) => { |
45 | files.insert((file_id, None)); | 46 | files.insert((file_id, None)); |
46 | files.extend( | 47 | files.extend(parent_module.children(db).map(|m| { |
47 | parent_module | 48 | let src = m.definition_source(db); |
48 | .children(db) | 49 | (src.file_id.original_file(db), None) |
49 | .map(|m| { | 50 | })); |
50 | let src = m.definition_source(db); | ||
51 | (src.file_id.original_file(db), None) | ||
52 | }) | ||
53 | .collect::<HashSet<_>>(), | ||
54 | ); | ||
55 | } | ||
56 | } | 51 | } |
57 | return files; | ||
58 | } else { | ||
59 | let range = match module_src.ast { | ||
60 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | ||
61 | ModuleSource::SourceFile(_) => None, | ||
62 | }; | ||
63 | return [(file_id, range)].iter().cloned().collect(); | ||
64 | } | 52 | } |
53 | return files; | ||
65 | } | 54 | } |
55 | } | ||
66 | 56 | ||
57 | if vis.as_str() != "" { | ||
67 | let source_root_id = db.file_source_root(file_id); | 58 | let source_root_id = db.file_source_root(file_id); |
68 | let source_root = db.source_root(source_root_id); | 59 | let source_root = db.source_root(source_root_id); |
69 | let mut files = source_root.walk().map(|id| (id.into(), None)).collect::<HashSet<_>>(); | 60 | let mut files = |
61 | source_root.walk().map(|id| (id.into(), None)).collect::<FxHashSet<_>>(); | ||
62 | |||
63 | // FIXME: add "pub(in path)" | ||
70 | 64 | ||
71 | if vis.as_str() == "pub(crate)" { | 65 | if vis.as_str() == "pub(crate)" { |
72 | return files; | 66 | return files; |
@@ -74,10 +68,8 @@ impl NameDefinition { | |||
74 | if vis.as_str() == "pub" { | 68 | if vis.as_str() == "pub" { |
75 | let krate = self.container.krate(db).unwrap(); | 69 | let krate = self.container.krate(db).unwrap(); |
76 | let crate_graph = db.crate_graph(); | 70 | let crate_graph = db.crate_graph(); |
77 | |||
78 | for crate_id in crate_graph.iter() { | 71 | for crate_id in crate_graph.iter() { |
79 | let mut crate_deps = crate_graph.dependencies(crate_id); | 72 | let mut crate_deps = crate_graph.dependencies(crate_id); |
80 | |||
81 | if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { | 73 | if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { |
82 | let root_file = crate_graph.crate_root(crate_id); | 74 | let root_file = crate_graph.crate_root(crate_id); |
83 | let source_root_id = db.file_source_root(root_file); | 75 | let source_root_id = db.file_source_root(root_file); |
@@ -85,15 +77,16 @@ impl NameDefinition { | |||
85 | files.extend(source_root.walk().map(|id| (id.into(), None))); | 77 | files.extend(source_root.walk().map(|id| (id.into(), None))); |
86 | } | 78 | } |
87 | } | 79 | } |
88 | |||
89 | return files; | 80 | return files; |
90 | } | 81 | } |
91 | } | 82 | } |
92 | 83 | ||
84 | let mut res = FxHashSet::default(); | ||
93 | let range = match module_src.ast { | 85 | let range = match module_src.ast { |
94 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | 86 | ModuleSource::Module(m) => Some(m.syntax().text_range()), |
95 | ModuleSource::SourceFile(_) => None, | 87 | ModuleSource::SourceFile(_) => None, |
96 | }; | 88 | }; |
97 | [(file_id, range)].iter().cloned().collect() | 89 | res.insert((file_id, range)); |
90 | res | ||
98 | } | 91 | } |
99 | } | 92 | } |