aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs12
-rw-r--r--crates/ra_ide_api/src/references.rs39
-rw-r--r--crates/ra_ide_api/src/references/classify.rs8
-rw-r--r--crates/ra_ide_api/src/references/name_definition.rs5
-rw-r--r--crates/ra_ide_api/src/references/search_scope.rs81
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)]
208mod tests { 208mod 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
3mod classify; 12mod classify;
4mod name_definition; 13mod name_definition;
@@ -9,7 +18,7 @@ use once_cell::unsync::Lazy;
9use ra_db::{SourceDatabase, SourceDatabaseExt}; 18use ra_db::{SourceDatabase, SourceDatabaseExt};
10use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit}; 19use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit};
11 20
12use crate::{db::RootDatabase, FileId, FilePosition, FileRange, NavigationTarget, RangeInfo}; 21use crate::{db::RootDatabase, FilePosition, FileRange, NavigationTarget, RangeInfo};
13 22
14pub(crate) use self::{ 23pub(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
3use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer}; 3use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
4use ra_db::FileId; 4use ra_db::FileId;
5use ra_syntax::{ast, match_ast, AstNode, AstPtr}; 5use ra_syntax::{ast, match_ast, AstNode, AstPtr};
6use test_utils::tested_by;
6 7
7use super::{ 8use 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, &macro_call) { 147 if let Some(macro_def) = analyzer.resolve_macro_call(db, &macro_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
3use hir::{ 6use 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.
3use 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
5use hir::{DefWithBody, HasSource, ModuleSource}; 6use hir::{DefWithBody, HasSource, ModuleSource};
6use ra_db::{FileId, SourceDatabase, SourceDatabaseExt}; 7use ra_db::{FileId, SourceDatabase, SourceDatabaseExt};
7use ra_syntax::{AstNode, TextRange}; 8use ra_syntax::{AstNode, TextRange};
9use rustc_hash::FxHashSet;
8 10
9use crate::db::RootDatabase; 11use crate::db::RootDatabase;
10 12
11use super::{NameDefinition, NameKind}; 13use super::{NameDefinition, NameKind};
12 14
13impl NameDefinition { 15impl 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}