aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/references
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/references')
-rw-r--r--crates/ra_ide_api/src/references/classify.rs179
-rw-r--r--crates/ra_ide_api/src/references/name_definition.rs113
-rw-r--r--crates/ra_ide_api/src/references/rename.rs469
-rw-r--r--crates/ra_ide_api/src/references/search_scope.rs92
4 files changed, 853 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs
new file mode 100644
index 000000000..c8daff9b1
--- /dev/null
+++ b/crates/ra_ide_api/src/references/classify.rs
@@ -0,0 +1,179 @@
1//! Functions that are used to classify an element from its definition or reference.
2
3use hir::{Either, FromSource, Module, ModuleSource, Path, PathResolution, Source, SourceAnalyzer};
4use ra_db::FileId;
5use ra_syntax::{ast, match_ast, AstNode, AstPtr};
6use test_utils::tested_by;
7
8use super::{
9 name_definition::{from_assoc_item, from_module_def, from_pat, from_struct_field},
10 NameDefinition, NameKind,
11};
12use crate::db::RootDatabase;
13
14pub(crate) fn classify_name(
15 db: &RootDatabase,
16 file_id: FileId,
17 name: &ast::Name,
18) -> Option<NameDefinition> {
19 let parent = name.syntax().parent()?;
20 let file_id = file_id.into();
21
22 // FIXME: add ast::MacroCall(it)
23 match_ast! {
24 match parent {
25 ast::BindPat(it) => {
26 from_pat(db, file_id, AstPtr::new(&it))
27 },
28 ast::RecordFieldDef(it) => {
29 let ast = hir::FieldSource::Named(it);
30 let src = hir::Source { file_id, ast };
31 let field = hir::StructField::from_source(db, src)?;
32 Some(from_struct_field(db, field))
33 },
34 ast::Module(it) => {
35 let def = {
36 if !it.has_semi() {
37 let ast = hir::ModuleSource::Module(it);
38 let src = hir::Source { file_id, ast };
39 hir::Module::from_definition(db, src)
40 } else {
41 let src = hir::Source { file_id, ast: it };
42 hir::Module::from_declaration(db, src)
43 }
44 }?;
45 Some(from_module_def(db, def.into(), None))
46 },
47 ast::StructDef(it) => {
48 let src = hir::Source { file_id, ast: it };
49 let def = hir::Struct::from_source(db, src)?;
50 Some(from_module_def(db, def.into(), None))
51 },
52 ast::EnumDef(it) => {
53 let src = hir::Source { file_id, ast: it };
54 let def = hir::Enum::from_source(db, src)?;
55 Some(from_module_def(db, def.into(), None))
56 },
57 ast::TraitDef(it) => {
58 let src = hir::Source { file_id, ast: it };
59 let def = hir::Trait::from_source(db, src)?;
60 Some(from_module_def(db, def.into(), None))
61 },
62 ast::StaticDef(it) => {
63 let src = hir::Source { file_id, ast: it };
64 let def = hir::Static::from_source(db, src)?;
65 Some(from_module_def(db, def.into(), None))
66 },
67 ast::EnumVariant(it) => {
68 let src = hir::Source { file_id, ast: it };
69 let def = hir::EnumVariant::from_source(db, src)?;
70 Some(from_module_def(db, def.into(), None))
71 },
72 ast::FnDef(it) => {
73 let src = hir::Source { file_id, ast: it };
74 let def = hir::Function::from_source(db, src)?;
75 if parent.parent().and_then(ast::ItemList::cast).is_some() {
76 Some(from_assoc_item(db, def.into()))
77 } else {
78 Some(from_module_def(db, def.into(), None))
79 }
80 },
81 ast::ConstDef(it) => {
82 let src = hir::Source { file_id, ast: it };
83 let def = hir::Const::from_source(db, src)?;
84 if parent.parent().and_then(ast::ItemList::cast).is_some() {
85 Some(from_assoc_item(db, def.into()))
86 } else {
87 Some(from_module_def(db, def.into(), None))
88 }
89 },
90 ast::TypeAliasDef(it) => {
91 let src = hir::Source { file_id, ast: it };
92 let def = hir::TypeAlias::from_source(db, src)?;
93 if parent.parent().and_then(ast::ItemList::cast).is_some() {
94 Some(from_assoc_item(db, def.into()))
95 } else {
96 Some(from_module_def(db, def.into(), None))
97 }
98 },
99 _ => None,
100 }
101 }
102}
103
104pub(crate) fn classify_name_ref(
105 db: &RootDatabase,
106 file_id: FileId,
107 name_ref: &ast::NameRef,
108) -> Option<NameDefinition> {
109 use PathResolution::*;
110
111 let parent = name_ref.syntax().parent()?;
112 let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
113
114 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
115 tested_by!(goto_definition_works_for_methods);
116 if let Some(func) = analyzer.resolve_method_call(&method_call) {
117 return Some(from_assoc_item(db, func.into()));
118 }
119 }
120
121 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
122 tested_by!(goto_definition_works_for_fields);
123 if let Some(field) = analyzer.resolve_field(&field_expr) {
124 return Some(from_struct_field(db, field));
125 }
126 }
127
128 if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
129 tested_by!(goto_definition_works_for_record_fields);
130 if let Some(record_lit) = record_field.syntax().ancestors().find_map(ast::RecordLit::cast) {
131 let variant_def = analyzer.resolve_record_literal(&record_lit)?;
132 let hir_path = Path::from_name_ref(name_ref);
133 let hir_name = hir_path.as_ident()?;
134 let field = variant_def.field(db, hir_name)?;
135 return Some(from_struct_field(db, field));
136 }
137 }
138
139 let ast = ModuleSource::from_child_node(db, file_id, &parent);
140 let file_id = file_id.into();
141 // FIXME: find correct container and visibility for each case
142 let container = Module::from_definition(db, Source { file_id, ast })?;
143 let visibility = None;
144
145 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
146 tested_by!(goto_definition_works_for_macros);
147 if let Some(macro_def) = analyzer.resolve_macro_call(db, &macro_call) {
148 let kind = NameKind::Macro(macro_def);
149 return Some(NameDefinition { kind, container, visibility });
150 }
151 }
152
153 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
154 let resolved = analyzer.resolve_path(db, &path)?;
155 match resolved {
156 Def(def) => Some(from_module_def(db, def, Some(container))),
157 AssocItem(item) => Some(from_assoc_item(db, item)),
158 LocalBinding(Either::A(pat)) => from_pat(db, file_id, pat),
159 LocalBinding(Either::B(par)) => {
160 let kind = NameKind::SelfParam(par);
161 Some(NameDefinition { kind, container, visibility })
162 }
163 GenericParam(par) => {
164 // FIXME: get generic param def
165 let kind = NameKind::GenericParam(par);
166 Some(NameDefinition { kind, container, visibility })
167 }
168 Macro(def) => {
169 let kind = NameKind::Macro(def);
170 Some(NameDefinition { kind, container, visibility })
171 }
172 SelfType(impl_block) => {
173 let ty = impl_block.target_ty(db);
174 let kind = NameKind::SelfType(ty);
175 let container = impl_block.module();
176 Some(NameDefinition { kind, container, visibility })
177 }
178 }
179}
diff --git a/crates/ra_ide_api/src/references/name_definition.rs b/crates/ra_ide_api/src/references/name_definition.rs
new file mode 100644
index 000000000..4580bc789
--- /dev/null
+++ b/crates/ra_ide_api/src/references/name_definition.rs
@@ -0,0 +1,113 @@
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.
5
6use hir::{
7 db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef,
8 Module, ModuleDef, StructField, Ty, VariantDef,
9};
10use ra_syntax::{ast, ast::VisibilityOwner, match_ast, AstNode, AstPtr};
11
12use crate::db::RootDatabase;
13
14#[derive(Debug, PartialEq, Eq)]
15pub enum NameKind {
16 Macro(MacroDef),
17 Field(StructField),
18 AssocItem(AssocItem),
19 Def(ModuleDef),
20 SelfType(Ty),
21 Pat((DefWithBody, AstPtr<ast::BindPat>)),
22 SelfParam(AstPtr<ast::SelfParam>),
23 GenericParam(u32),
24}
25
26#[derive(PartialEq, Eq)]
27pub(crate) struct NameDefinition {
28 pub visibility: Option<ast::Visibility>,
29 pub container: Module,
30 pub kind: NameKind,
31}
32
33pub(super) fn from_pat(
34 db: &RootDatabase,
35 file_id: HirFileId,
36 pat: AstPtr<ast::BindPat>,
37) -> Option<NameDefinition> {
38 let root = db.parse_or_expand(file_id)?;
39 let def = pat.to_node(&root).syntax().ancestors().find_map(|node| {
40 match_ast! {
41 match node {
42 ast::FnDef(it) => {
43 let src = hir::Source { file_id, ast: it };
44 Some(hir::Function::from_source(db, src)?.into())
45 },
46 ast::ConstDef(it) => {
47 let src = hir::Source { file_id, ast: it };
48 Some(hir::Const::from_source(db, src)?.into())
49 },
50 ast::StaticDef(it) => {
51 let src = hir::Source { file_id, ast: it };
52 Some(hir::Static::from_source(db, src)?.into())
53 },
54 _ => None,
55 }
56 }
57 })?;
58 let kind = NameKind::Pat((def, pat));
59 let container = def.module(db);
60 Some(NameDefinition { kind, container, visibility: None })
61}
62
63pub(super) fn from_assoc_item(db: &RootDatabase, item: AssocItem) -> NameDefinition {
64 let container = item.module(db);
65 let visibility = match item {
66 AssocItem::Function(f) => f.source(db).ast.visibility(),
67 AssocItem::Const(c) => c.source(db).ast.visibility(),
68 AssocItem::TypeAlias(a) => a.source(db).ast.visibility(),
69 };
70 let kind = NameKind::AssocItem(item);
71 NameDefinition { kind, container, visibility }
72}
73
74pub(super) fn from_struct_field(db: &RootDatabase, field: StructField) -> NameDefinition {
75 let kind = NameKind::Field(field);
76 let parent = field.parent_def(db);
77 let container = parent.module(db);
78 let visibility = match parent {
79 VariantDef::Struct(s) => s.source(db).ast.visibility(),
80 VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(),
81 };
82 NameDefinition { kind, container, visibility }
83}
84
85pub(super) fn from_module_def(
86 db: &RootDatabase,
87 def: ModuleDef,
88 module: Option<Module>,
89) -> NameDefinition {
90 let kind = NameKind::Def(def);
91 let (container, visibility) = match def {
92 ModuleDef::Module(it) => {
93 let container = it.parent(db).or_else(|| Some(it)).unwrap();
94 let visibility = it.declaration_source(db).and_then(|s| s.ast.visibility());
95 (container, visibility)
96 }
97 ModuleDef::EnumVariant(it) => {
98 let container = it.module(db);
99 let visibility = it.source(db).ast.parent_enum().visibility();
100 (container, visibility)
101 }
102 ModuleDef::Function(it) => (it.module(db), it.source(db).ast.visibility()),
103 ModuleDef::Const(it) => (it.module(db), it.source(db).ast.visibility()),
104 ModuleDef::Static(it) => (it.module(db), it.source(db).ast.visibility()),
105 ModuleDef::Trait(it) => (it.module(db), it.source(db).ast.visibility()),
106 ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).ast.visibility()),
107 ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).ast.visibility()),
108 ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).ast.visibility()),
109 ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).ast.visibility()),
110 ModuleDef::BuiltinType(..) => (module.unwrap(), None),
111 };
112 NameDefinition { kind, container, visibility }
113}
diff --git a/crates/ra_ide_api/src/references/rename.rs b/crates/ra_ide_api/src/references/rename.rs
new file mode 100644
index 000000000..0e2e088e0
--- /dev/null
+++ b/crates/ra_ide_api/src/references/rename.rs
@@ -0,0 +1,469 @@
1//! FIXME: write short doc here
2
3use hir::ModuleSource;
4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
6use relative_path::{RelativePath, RelativePathBuf};
7
8use crate::{
9 db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange,
10 SourceFileEdit, TextRange,
11};
12
13use super::find_all_refs;
14
15pub(crate) fn rename(
16 db: &RootDatabase,
17 position: FilePosition,
18 new_name: &str,
19) -> Option<RangeInfo<SourceChange>> {
20 let parse = db.parse(position.file_id);
21 if let Some((ast_name, ast_module)) =
22 find_name_and_module_at_offset(parse.tree().syntax(), position)
23 {
24 let range = ast_name.syntax().text_range();
25 rename_mod(db, &ast_name, &ast_module, position, new_name)
26 .map(|info| RangeInfo::new(range, info))
27 } else {
28 rename_reference(db, position, new_name)
29 }
30}
31
32fn find_name_and_module_at_offset(
33 syntax: &SyntaxNode,
34 position: FilePosition,
35) -> Option<(ast::Name, ast::Module)> {
36 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
37 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
38 Some((ast_name, ast_module))
39}
40
41fn source_edit_from_file_id_range(
42 file_id: FileId,
43 range: TextRange,
44 new_name: &str,
45) -> SourceFileEdit {
46 SourceFileEdit {
47 file_id,
48 edit: {
49 let mut builder = ra_text_edit::TextEditBuilder::default();
50 builder.replace(range, new_name.into());
51 builder.finish()
52 },
53 }
54}
55
56fn rename_mod(
57 db: &RootDatabase,
58 ast_name: &ast::Name,
59 ast_module: &ast::Module,
60 position: FilePosition,
61 new_name: &str,
62) -> Option<SourceChange> {
63 let mut source_file_edits = Vec::new();
64 let mut file_system_edits = Vec::new();
65 let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() };
66 if let Some(module) = hir::Module::from_declaration(db, module_src) {
67 let src = module.definition_source(db);
68 let file_id = src.file_id.original_file(db);
69 match src.ast {
70 ModuleSource::SourceFile(..) => {
71 let mod_path: RelativePathBuf = db.file_relative_path(file_id);
72 // mod is defined in path/to/dir/mod.rs
73 let dst_path = if mod_path.file_stem() == Some("mod") {
74 mod_path
75 .parent()
76 .and_then(|p| p.parent())
77 .or_else(|| Some(RelativePath::new("")))
78 .map(|p| p.join(new_name).join("mod.rs"))
79 } else {
80 Some(mod_path.with_file_name(new_name).with_extension("rs"))
81 };
82 if let Some(path) = dst_path {
83 let move_file = FileSystemEdit::MoveFile {
84 src: file_id,
85 dst_source_root: db.file_source_root(position.file_id),
86 dst_path: path,
87 };
88 file_system_edits.push(move_file);
89 }
90 }
91 ModuleSource::Module(..) => {}
92 }
93 }
94
95 let edit = SourceFileEdit {
96 file_id: position.file_id,
97 edit: {
98 let mut builder = ra_text_edit::TextEditBuilder::default();
99 builder.replace(ast_name.syntax().text_range(), new_name.into());
100 builder.finish()
101 },
102 };
103 source_file_edits.push(edit);
104
105 Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits))
106}
107
108fn rename_reference(
109 db: &RootDatabase,
110 position: FilePosition,
111 new_name: &str,
112) -> Option<RangeInfo<SourceChange>> {
113 let RangeInfo { range, info: refs } = find_all_refs(db, position)?;
114
115 let edit = refs
116 .into_iter()
117 .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name))
118 .collect::<Vec<_>>();
119
120 if edit.is_empty() {
121 return None;
122 }
123
124 Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit)))
125}
126
127#[cfg(test)]
128mod tests {
129 use crate::{
130 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
131 ReferenceSearchResult,
132 };
133 use insta::assert_debug_snapshot;
134 use test_utils::assert_eq_text;
135
136 #[test]
137 fn test_find_all_refs_for_local() {
138 let code = r#"
139 fn main() {
140 let mut i = 1;
141 let j = 1;
142 i = i<|> + j;
143
144 {
145 i = 0;
146 }
147
148 i = 5;
149 }"#;
150
151 let refs = get_all_refs(code);
152 assert_eq!(refs.len(), 5);
153 }
154
155 #[test]
156 fn test_find_all_refs_for_param_inside() {
157 let code = r#"
158 fn foo(i : u32) -> u32 {
159 i<|>
160 }"#;
161
162 let refs = get_all_refs(code);
163 assert_eq!(refs.len(), 2);
164 }
165
166 #[test]
167 fn test_find_all_refs_for_fn_param() {
168 let code = r#"
169 fn foo(i<|> : u32) -> u32 {
170 i
171 }"#;
172
173 let refs = get_all_refs(code);
174 assert_eq!(refs.len(), 2);
175 }
176
177 #[test]
178 fn test_find_all_refs_field_name() {
179 let code = r#"
180 //- /lib.rs
181 struct Foo {
182 pub spam<|>: u32,
183 }
184
185 fn main(s: Foo) {
186 let f = s.spam;
187 }
188 "#;
189
190 let refs = get_all_refs(code);
191 assert_eq!(refs.len(), 2);
192 }
193
194 #[test]
195 fn test_find_all_refs_impl_item_name() {
196 let code = r#"
197 //- /lib.rs
198 struct Foo;
199 impl Foo {
200 fn f<|>(&self) { }
201 }
202 "#;
203
204 let refs = get_all_refs(code);
205 assert_eq!(refs.len(), 1);
206 }
207
208 #[test]
209 fn test_find_all_refs_enum_var_name() {
210 let code = r#"
211 //- /lib.rs
212 enum Foo {
213 A,
214 B<|>,
215 C,
216 }
217 "#;
218
219 let refs = get_all_refs(code);
220 assert_eq!(refs.len(), 1);
221 }
222
223 #[test]
224 fn test_find_all_refs_modules() {
225 let code = r#"
226 //- /lib.rs
227 pub mod foo;
228 pub mod bar;
229
230 fn f() {
231 let i = foo::Foo { n: 5 };
232 }
233
234 //- /foo.rs
235 use crate::bar;
236
237 pub struct Foo {
238 pub n: u32,
239 }
240
241 fn f() {
242 let i = bar::Bar { n: 5 };
243 }
244
245 //- /bar.rs
246 use crate::foo;
247
248 pub struct Bar {
249 pub n: u32,
250 }
251
252 fn f() {
253 let i = foo::Foo<|> { n: 5 };
254 }
255 "#;
256
257 let (analysis, pos) = analysis_and_position(code);
258 let refs = analysis.find_all_refs(pos).unwrap().unwrap();
259 assert_eq!(refs.len(), 3);
260 }
261
262 fn get_all_refs(text: &str) -> ReferenceSearchResult {
263 let (analysis, position) = single_file_with_position(text);
264 analysis.find_all_refs(position).unwrap().unwrap()
265 }
266
267 #[test]
268 fn test_rename_for_local() {
269 test_rename(
270 r#"
271 fn main() {
272 let mut i = 1;
273 let j = 1;
274 i = i<|> + j;
275
276 {
277 i = 0;
278 }
279
280 i = 5;
281 }"#,
282 "k",
283 r#"
284 fn main() {
285 let mut k = 1;
286 let j = 1;
287 k = k + j;
288
289 {
290 k = 0;
291 }
292
293 k = 5;
294 }"#,
295 );
296 }
297
298 #[test]
299 fn test_rename_for_param_inside() {
300 test_rename(
301 r#"
302 fn foo(i : u32) -> u32 {
303 i<|>
304 }"#,
305 "j",
306 r#"
307 fn foo(j : u32) -> u32 {
308 j
309 }"#,
310 );
311 }
312
313 #[test]
314 fn test_rename_refs_for_fn_param() {
315 test_rename(
316 r#"
317 fn foo(i<|> : u32) -> u32 {
318 i
319 }"#,
320 "new_name",
321 r#"
322 fn foo(new_name : u32) -> u32 {
323 new_name
324 }"#,
325 );
326 }
327
328 #[test]
329 fn test_rename_for_mut_param() {
330 test_rename(
331 r#"
332 fn foo(mut i<|> : u32) -> u32 {
333 i
334 }"#,
335 "new_name",
336 r#"
337 fn foo(mut new_name : u32) -> u32 {
338 new_name
339 }"#,
340 );
341 }
342
343 #[test]
344 fn test_rename_mod() {
345 let (analysis, position) = analysis_and_position(
346 "
347 //- /lib.rs
348 mod bar;
349
350 //- /bar.rs
351 mod foo<|>;
352
353 //- /bar/foo.rs
354 // emtpy
355 ",
356 );
357 let new_name = "foo2";
358 let source_change = analysis.rename(position, new_name).unwrap();
359 assert_debug_snapshot!(&source_change,
360@r###"
361 Some(
362 RangeInfo {
363 range: [4; 7),
364 info: SourceChange {
365 label: "rename",
366 source_file_edits: [
367 SourceFileEdit {
368 file_id: FileId(
369 2,
370 ),
371 edit: TextEdit {
372 atoms: [
373 AtomTextEdit {
374 delete: [4; 7),
375 insert: "foo2",
376 },
377 ],
378 },
379 },
380 ],
381 file_system_edits: [
382 MoveFile {
383 src: FileId(
384 3,
385 ),
386 dst_source_root: SourceRootId(
387 0,
388 ),
389 dst_path: "bar/foo2.rs",
390 },
391 ],
392 cursor_position: None,
393 },
394 },
395 )
396 "###);
397 }
398
399 #[test]
400 fn test_rename_mod_in_dir() {
401 let (analysis, position) = analysis_and_position(
402 "
403 //- /lib.rs
404 mod fo<|>o;
405 //- /foo/mod.rs
406 // emtpy
407 ",
408 );
409 let new_name = "foo2";
410 let source_change = analysis.rename(position, new_name).unwrap();
411 assert_debug_snapshot!(&source_change,
412 @r###"
413 Some(
414 RangeInfo {
415 range: [4; 7),
416 info: SourceChange {
417 label: "rename",
418 source_file_edits: [
419 SourceFileEdit {
420 file_id: FileId(
421 1,
422 ),
423 edit: TextEdit {
424 atoms: [
425 AtomTextEdit {
426 delete: [4; 7),
427 insert: "foo2",
428 },
429 ],
430 },
431 },
432 ],
433 file_system_edits: [
434 MoveFile {
435 src: FileId(
436 2,
437 ),
438 dst_source_root: SourceRootId(
439 0,
440 ),
441 dst_path: "foo2/mod.rs",
442 },
443 ],
444 cursor_position: None,
445 },
446 },
447 )
448 "###
449 );
450 }
451
452 fn test_rename(text: &str, new_name: &str, expected: &str) {
453 let (analysis, position) = single_file_with_position(text);
454 let source_change = analysis.rename(position, new_name).unwrap();
455 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
456 let mut file_id: Option<FileId> = None;
457 if let Some(change) = source_change {
458 for edit in change.info.source_file_edits {
459 file_id = Some(edit.file_id);
460 for atom in edit.edit.as_atoms() {
461 text_edit_builder.replace(atom.delete, atom.insert.clone());
462 }
463 }
464 }
465 let result =
466 text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap());
467 assert_eq_text!(expected, &*result);
468 }
469}
diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs
new file mode 100644
index 000000000..5cb69b8fc
--- /dev/null
+++ b/crates/ra_ide_api/src/references/search_scope.rs
@@ -0,0 +1,92 @@
1//! Generally, `search_scope` returns files that might contain references for the element.
2//! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates.
3//! In some cases, the location of the references is known to within a `TextRange`,
4//! e.g. for things like local variables.
5
6use hir::{DefWithBody, HasSource, ModuleSource};
7use ra_db::{FileId, SourceDatabase, SourceDatabaseExt};
8use ra_syntax::{AstNode, TextRange};
9use rustc_hash::FxHashSet;
10
11use crate::db::RootDatabase;
12
13use super::{NameDefinition, NameKind};
14
15impl NameDefinition {
16 pub(crate) fn search_scope(&self, db: &RootDatabase) -> FxHashSet<(FileId, Option<TextRange>)> {
17 let module_src = self.container.definition_source(db);
18 let file_id = module_src.file_id.original_file(db);
19
20 if let NameKind::Pat((def, _)) = self.kind {
21 let mut res = FxHashSet::default();
22 let range = match def {
23 DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(),
24 DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(),
25 DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(),
26 };
27 res.insert((file_id, Some(range)));
28 return res;
29 }
30
31 let vis =
32 self.visibility.as_ref().map(|v| v.syntax().to_string()).unwrap_or("".to_string());
33
34 if vis.as_str() == "pub(super)" {
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);
39
40 match parent_src.ast {
41 ModuleSource::Module(m) => {
42 let range = Some(m.syntax().text_range());
43 files.insert((file_id, range));
44 }
45 ModuleSource::SourceFile(_) => {
46 files.insert((file_id, None));
47 files.extend(parent_module.children(db).map(|m| {
48 let src = m.definition_source(db);
49 (src.file_id.original_file(db), None)
50 }));
51 }
52 }
53 return files;
54 }
55 }
56
57 if vis.as_str() != "" {
58 let source_root_id = db.file_source_root(file_id);
59 let source_root = db.source_root(source_root_id);
60 let mut files =
61 source_root.walk().map(|id| (id.into(), None)).collect::<FxHashSet<_>>();
62
63 // FIXME: add "pub(in path)"
64
65 if vis.as_str() == "pub(crate)" {
66 return files;
67 }
68 if vis.as_str() == "pub" {
69 let krate = self.container.krate(db).unwrap();
70 let crate_graph = db.crate_graph();
71 for crate_id in crate_graph.iter() {
72 let mut crate_deps = crate_graph.dependencies(crate_id);
73 if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) {
74 let root_file = crate_graph.crate_root(crate_id);
75 let source_root_id = db.file_source_root(root_file);
76 let source_root = db.source_root(source_root_id);
77 files.extend(source_root.walk().map(|id| (id.into(), None)));
78 }
79 }
80 return files;
81 }
82 }
83
84 let mut res = FxHashSet::default();
85 let range = match module_src.ast {
86 ModuleSource::Module(m) => Some(m.syntax().text_range()),
87 ModuleSource::SourceFile(_) => None,
88 };
89 res.insert((file_id, range));
90 res
91 }
92}