aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/from_source.rs60
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs2
-rw-r--r--crates/ra_ide_api/src/hover.rs2
-rw-r--r--crates/ra_ide_api/src/name_ref_kind.rs6
-rw-r--r--crates/ra_ide_api/src/references.rs154
-rw-r--r--crates/ra_ide_api/src/search_scope.rs186
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs2
7 files changed, 376 insertions, 36 deletions
diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs
index f80d8eb5f..e09414ca3 100644
--- a/crates/ra_hir/src/from_source.rs
+++ b/crates/ra_hir/src/from_source.rs
@@ -11,8 +11,9 @@ use crate::{
11 db::{AstDatabase, DefDatabase, HirDatabase}, 11 db::{AstDatabase, DefDatabase, HirDatabase},
12 ids::{AstItemDef, LocationCtx}, 12 ids::{AstItemDef, LocationCtx},
13 name::AsName, 13 name::AsName,
14 Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, 14 AssocItem, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock,
15 ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, 15 Module, ModuleDef, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union,
16 VariantDef,
16}; 17};
17 18
18pub trait FromSource: Sized { 19pub trait FromSource: Sized {
@@ -129,6 +130,61 @@ impl FromSource for StructField {
129 } 130 }
130} 131}
131 132
133impl FromSource for AssocItem {
134 type Ast = ast::ImplItem;
135 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
136 macro_rules! def {
137 ($kind:ident, $ast:ident) => {
138 $kind::from_source(db, Source { file_id: src.file_id, ast: $ast })
139 .and_then(|it| Some(AssocItem::from(it)))
140 };
141 }
142
143 match src.ast {
144 ast::ImplItem::FnDef(f) => def!(Function, f),
145 ast::ImplItem::ConstDef(c) => def!(Const, c),
146 ast::ImplItem::TypeAliasDef(a) => def!(TypeAlias, a),
147 }
148 }
149}
150
151// not fully matched
152impl FromSource for ModuleDef {
153 type Ast = ast::ModuleItem;
154 fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source<Self::Ast>) -> Option<Self> {
155 macro_rules! def {
156 ($kind:ident, $ast:ident) => {
157 $kind::from_source(db, Source { file_id: src.file_id, ast: $ast })
158 .and_then(|it| Some(ModuleDef::from(it)))
159 };
160 }
161
162 match src.ast {
163 ast::ModuleItem::FnDef(f) => def!(Function, f),
164 ast::ModuleItem::ConstDef(c) => def!(Const, c),
165 ast::ModuleItem::TypeAliasDef(a) => def!(TypeAlias, a),
166 ast::ModuleItem::TraitDef(t) => def!(Trait, t),
167 ast::ModuleItem::StaticDef(s) => def!(Static, s),
168 ast::ModuleItem::StructDef(s) => {
169 let src = Source { file_id: src.file_id, ast: s };
170 let s = Struct::from_source(db, src)?;
171 Some(ModuleDef::Adt(s.into()))
172 }
173 ast::ModuleItem::EnumDef(e) => {
174 let src = Source { file_id: src.file_id, ast: e };
175 let e = Enum::from_source(db, src)?;
176 Some(ModuleDef::Adt(e.into()))
177 }
178 ast::ModuleItem::Module(ref m) if !m.has_semi() => {
179 let src = Source { file_id: src.file_id, ast: ModuleSource::Module(m.clone()) };
180 let module = Module::from_definition(db, src)?;
181 Some(ModuleDef::Module(module))
182 }
183 _ => None,
184 }
185 }
186}
187
132// FIXME: simplify it 188// FIXME: simplify it
133impl ModuleSource { 189impl ModuleSource {
134 pub fn from_position( 190 pub fn from_position(
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 41a88314f..13e42bb35 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -10,7 +10,7 @@ use ra_syntax::{
10use crate::{ 10use crate::{
11 db::RootDatabase, 11 db::RootDatabase,
12 display::ShortLabel, 12 display::ShortLabel,
13 name_ref_kind::{classify_name_ref, NameRefKind::*}, 13 name_ref_kind::{classify_name_ref, NameKind::*},
14 FilePosition, NavigationTarget, RangeInfo, 14 FilePosition, NavigationTarget, RangeInfo,
15}; 15};
16 16
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 24b161c5c..5c2549dc3 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -14,7 +14,7 @@ use crate::{
14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, 14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup,
15 rust_code_markup_with_doc, ShortLabel, 15 rust_code_markup_with_doc, ShortLabel,
16 }, 16 },
17 name_ref_kind::{classify_name_ref, NameRefKind::*}, 17 name_ref_kind::{classify_name_ref, NameKind::*},
18 FilePosition, FileRange, RangeInfo, 18 FilePosition, FileRange, RangeInfo,
19}; 19};
20 20
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs
index 149585971..eb8caabfe 100644
--- a/crates/ra_ide_api/src/name_ref_kind.rs
+++ b/crates/ra_ide_api/src/name_ref_kind.rs
@@ -6,7 +6,7 @@ use test_utils::tested_by;
6 6
7use crate::db::RootDatabase; 7use crate::db::RootDatabase;
8 8
9pub enum NameRefKind { 9pub enum NameKind {
10 Method(hir::Function), 10 Method(hir::Function),
11 Macro(hir::MacroDef), 11 Macro(hir::MacroDef),
12 FieldAccess(hir::StructField), 12 FieldAccess(hir::StructField),
@@ -22,8 +22,8 @@ pub(crate) fn classify_name_ref(
22 db: &RootDatabase, 22 db: &RootDatabase,
23 analyzer: &hir::SourceAnalyzer, 23 analyzer: &hir::SourceAnalyzer,
24 name_ref: &ast::NameRef, 24 name_ref: &ast::NameRef,
25) -> Option<NameRefKind> { 25) -> Option<NameKind> {
26 use NameRefKind::*; 26 use NameKind::*;
27 27
28 // Check if it is a method 28 // Check if it is a method
29 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { 29 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 4247c6d90..9335bc8ca 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -1,13 +1,18 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Either, ModuleSource}; 3use hir::{FromSource, ModuleSource};
4use ra_db::{SourceDatabase, SourceDatabaseExt}; 4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}; 5use ra_syntax::{algo::find_node_at_offset, ast, AstNode, AstPtr, SyntaxNode};
6use relative_path::{RelativePath, RelativePathBuf}; 6use relative_path::{RelativePath, RelativePathBuf};
7 7
8use crate::{ 8use crate::{
9 db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, 9 db::RootDatabase,
10 SourceChange, SourceFileEdit, TextRange, 10 name_ref_kind::{
11 classify_name_ref,
12 NameKind::{self, *},
13 },
14 FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange,
15 SourceFileEdit, TextRange,
11}; 16};
12 17
13#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
@@ -52,41 +57,92 @@ pub(crate) fn find_all_refs(
52 position: FilePosition, 57 position: FilePosition,
53) -> Option<RangeInfo<ReferenceSearchResult>> { 58) -> Option<RangeInfo<ReferenceSearchResult>> {
54 let parse = db.parse(position.file_id); 59 let parse = db.parse(position.file_id);
55 let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?; 60 let syntax = parse.tree().syntax().clone();
56 let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); 61 let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?;
62
63 let declaration = match name_kind {
64 Macro(mac) => NavigationTarget::from_macro_def(db, mac),
65 FieldAccess(field) => NavigationTarget::from_field(db, field),
66 AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc),
67 Method(func) => NavigationTarget::from_def_source(db, func),
68 Def(def) => NavigationTarget::from_def(db, def)?,
69 SelfType(ref ty) => match ty.as_adt() {
70 Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id),
71 None => return None,
72 },
73 Pat(pat) => NavigationTarget::from_pat(db, position.file_id, pat),
74 SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par),
75 GenericParam(_) => return None,
76 };
57 77
58 let references = analyzer 78 let references = match name_kind {
59 .find_all_refs(&binding) 79 Pat(pat) => analyzer
60 .into_iter() 80 .find_all_refs(&pat.to_node(&syntax))
61 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) 81 .into_iter()
62 .collect::<Vec<_>>(); 82 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
83 .collect::<Vec<_>>(),
84 _ => vec![],
85 };
63 86
64 return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); 87 return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }));
65 88
66 fn find_binding<'a>( 89 fn find_name<'a>(
67 db: &RootDatabase, 90 db: &RootDatabase,
68 source_file: &SourceFile, 91 syntax: &SyntaxNode,
69 position: FilePosition, 92 position: FilePosition,
70 ) -> Option<RangeInfo<(ast::BindPat, hir::SourceAnalyzer)>> { 93 ) -> Option<RangeInfo<(hir::SourceAnalyzer, NameKind)>> {
71 let syntax = source_file.syntax(); 94 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
72 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 95 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name.syntax(), None);
73 let range = binding.syntax().text_range(); 96 let name_kind = classify_name(db, position.file_id, &name)?;
74 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); 97 let range = name.syntax().text_range();
75 return Some(RangeInfo::new(range, (binding, analyzer))); 98 return Some(RangeInfo::new(range, (analyzer, name_kind)));
76 }; 99 }
77 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; 100 let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?;
78 let range = name_ref.syntax().text_range(); 101 let range = name_ref.syntax().text_range();
79 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); 102 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
80 let resolved = analyzer.resolve_local_name(&name_ref)?; 103 let name_kind = classify_name_ref(db, &analyzer, &name_ref)?;
81 if let Either::A(ptr) = resolved.ptr() { 104 Some(RangeInfo::new(range, (analyzer, name_kind)))
82 if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) {
83 return Some(RangeInfo::new(range, (binding, analyzer)));
84 }
85 }
86 None
87 } 105 }
88} 106}
89 107
108fn classify_name(db: &RootDatabase, file_id: FileId, name: &ast::Name) -> Option<NameKind> {
109 let parent = name.syntax().parent()?;
110 let file_id = file_id.into();
111
112 if let Some(pat) = ast::BindPat::cast(parent.clone()) {
113 return Some(Pat(AstPtr::new(&pat)));
114 }
115 if let Some(var) = ast::EnumVariant::cast(parent.clone()) {
116 let src = hir::Source { file_id, ast: var };
117 let var = hir::EnumVariant::from_source(db, src)?;
118 return Some(Def(var.into()));
119 }
120 if let Some(field) = ast::RecordFieldDef::cast(parent.clone()) {
121 let src = hir::Source { file_id, ast: hir::FieldSource::Named(field) };
122 let field = hir::StructField::from_source(db, src)?;
123 return Some(FieldAccess(field));
124 }
125 if let Some(field) = ast::TupleFieldDef::cast(parent.clone()) {
126 let src = hir::Source { file_id, ast: hir::FieldSource::Pos(field) };
127 let field = hir::StructField::from_source(db, src)?;
128 return Some(FieldAccess(field));
129 }
130 if let Some(_) = parent.parent().and_then(ast::ItemList::cast) {
131 let ast = ast::ImplItem::cast(parent.clone())?;
132 let src = hir::Source { file_id, ast };
133 let item = hir::AssocItem::from_source(db, src)?;
134 return Some(AssocItem(item));
135 }
136 if let Some(item) = ast::ModuleItem::cast(parent.clone()) {
137 let src = hir::Source { file_id, ast: item };
138 let def = hir::ModuleDef::from_source(db, src)?;
139 return Some(Def(def));
140 }
141 // FIXME: TYPE_PARAM, ALIAS, MACRO_CALL; Union
142
143 None
144}
145
90pub(crate) fn rename( 146pub(crate) fn rename(
91 db: &RootDatabase, 147 db: &RootDatabase,
92 position: FilePosition, 148 position: FilePosition,
@@ -249,6 +305,48 @@ mod tests {
249 assert_eq!(refs.len(), 2); 305 assert_eq!(refs.len(), 2);
250 } 306 }
251 307
308 #[test]
309 fn test_find_all_refs_field_name() {
310 let code = r#"
311 //- /lib.rs
312 struct Foo {
313 spam<|>: u32,
314 }
315 "#;
316
317 let refs = get_all_refs(code);
318 assert_eq!(refs.len(), 1);
319 }
320
321 #[test]
322 fn test_find_all_refs_impl_item_name() {
323 let code = r#"
324 //- /lib.rs
325 struct Foo;
326 impl Foo {
327 fn f<|>(&self) { }
328 }
329 "#;
330
331 let refs = get_all_refs(code);
332 assert_eq!(refs.len(), 1);
333 }
334
335 #[test]
336 fn test_find_all_refs_enum_var_name() {
337 let code = r#"
338 //- /lib.rs
339 enum Foo {
340 A,
341 B<|>,
342 C,
343 }
344 "#;
345
346 let refs = get_all_refs(code);
347 assert_eq!(refs.len(), 1);
348 }
349
252 fn get_all_refs(text: &str) -> ReferenceSearchResult { 350 fn get_all_refs(text: &str) -> ReferenceSearchResult {
253 let (analysis, position) = single_file_with_position(text); 351 let (analysis, position) = single_file_with_position(text);
254 analysis.find_all_refs(position).unwrap().unwrap() 352 analysis.find_all_refs(position).unwrap().unwrap()
diff --git a/crates/ra_ide_api/src/search_scope.rs b/crates/ra_ide_api/src/search_scope.rs
new file mode 100644
index 000000000..ca1ac2b03
--- /dev/null
+++ b/crates/ra_ide_api/src/search_scope.rs
@@ -0,0 +1,186 @@
1pub enum SearchScope {
2 Function(hir::Function),
3 Module(hir::Module),
4 Crate(hir::Crate),
5 Crates(Vec<hir::Crate>),
6}
7
8pub struct SearchScope{
9 pub scope: Vec<SyntaxNode>
10}
11
12pub fn find_all_refs(db: &RootDatabase, decl: NameKind) -> Vec<ReferenceDescriptor> {
13 let (module, visibility) = match decl {
14 FieldAccess(field) => {
15 let parent = field.parent_def(db);
16 let module = parent.module(db);
17 let visibility = match parent {
18 VariantDef::Struct(s) => s.source(db).ast.visibility(),
19 VariantDef::EnumVariant(v) => v.parent_enum(db).source(db).ast.visibility(),
20 };
21 (module, visibility)
22 }
23 AssocItem(item) => {
24 let parent = item.parent_trait(db)?;
25 let module = parent.module(db);
26 let visibility = parent.source(db).ast.visibility();
27 (module, visibility)
28 }
29 Def(def) => {
30 let (module, visibility) = match def {
31 ModuleDef::Module(m) => (m, ),
32 ModuleDef::Function(f) => (f.module(db), f.source(db).ast.visibility()),
33 ModuleDef::Adt::Struct(s) => (s.module(db), s.source(db).ast.visibility()),
34 ModuleDef::Adt::Union(u) => (u.module(db), u.source(db).ast.visibility()),
35 ModuleDef::Adt::Enum(e) => (e.module(db), e.source(db).ast.visibility()),
36 ModuleDef::EnumVariant(v) => (v.module(db), v.source(db).ast.visibility()),
37 ModuleDef::Const(c) => (c.module(db), c.source(db).ast.visibility()),
38 ModuleDef::Static(s) => (s.module(db), s.source(db).ast.visibility()),
39 ModuleDef::Trait(t) => (t.module(db), t.source(db).ast.visibility()),
40 ModuleDef::TypeAlias(a) => (a.module(db), a.source(db).ast.visibility()),
41 ModuleDef::BuiltinType(_) => return vec![];
42 };
43 (module, visibility)
44 }
45 // FIXME: add missing kinds
46 _ => return vec![];
47 };
48 let scope = scope(db, module, visibility);
49}
50
51fn scope(db: &RootDatabase, module: hir::Module, item_vis: Option<ast::Visibility>) -> SearchScope {
52 if let Some(v) = item_vis {
53 let krate = module.krate(db)?;
54
55 if v.syntax().text() == "pub" {
56 SearchScope::Crate(krate)
57 }
58 if v.syntax().text() == "pub(crate)" {
59 let crate_graph = db.crate_graph();
60 let crates = crate_graph.iter().filter(|id| {
61 crate_graph.dependencies(id).any(|d| d.crate_id() == krate.crate_id())
62 }).map(|id| Crate { id }).collect::<Vec<_>>();
63 crates.insert(0, krate);
64 SearchScope::Crates(crates)
65 }
66 // FIXME: "pub(super)", "pub(in path)"
67 SearchScope::Module(module)
68 }
69 SearchScope::Module(module)
70}
71
72fn process_one(db, scope: SearchScope, pat) {
73 match scope {
74 SearchScope::Crate(krate) => {
75 let text = db.file_text(position.file_id).as_str();
76 let parse = SourceFile::parse(text);
77 for (offset, name) in text.match_indices(pat) {
78 if let Some() = find_node_at_offset<ast::NameRef>(parse, offset) {
79
80 }
81 }
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::{
89 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
90 ReferenceSearchResult,
91 };
92 use insta::assert_debug_snapshot;
93 use test_utils::assert_eq_text;
94
95 #[test]
96 fn test_find_all_refs_for_local() {
97 let code = r#"
98 fn main() {
99 let mut i = 1;
100 let j = 1;
101 i = i<|> + j;
102
103 {
104 i = 0;
105 }
106
107 i = 5;
108 }
109 "#;
110
111 let refs = get_all_refs(code);
112 assert_eq!(refs.len(), 5);
113 }
114
115 #[test]
116 fn test_find_all_refs_for_param_inside() {
117 let code = r#"
118 fn foo(i : u32) -> u32 {
119 i<|>
120 }"#;
121
122 let refs = get_all_refs(code);
123 assert_eq!(refs.len(), 2);
124 }
125
126 #[test]
127 fn test_find_all_refs_for_fn_param() {
128 let code = r#"
129 fn foo(i<|> : u32) -> u32 {
130 i
131 }"#;
132
133 let refs = get_all_refs(code);
134 assert_eq!(refs.len(), 2);
135 }
136
137 #[test]
138 fn test_find_all_refs_field_name() {
139 let code = r#"
140 //- /lib.rs
141 struct Foo {
142 spam<|>: u32,
143 }
144 "#;
145
146 let refs = get_all_refs(code);
147 assert_eq!(refs.len(), 1);
148 }
149
150 #[test]
151 fn test_find_all_refs_methods() {
152 let code = r#"
153 //- /lib.rs
154 struct Foo;
155 impl Foo {
156 pub fn a() {
157 self.b()
158 }
159 fn b(&self) {}
160 }
161 "#;
162
163 let refs = get_all_refs(code);
164 assert_eq!(refs.len(), 1);
165 }
166
167 #[test]
168 fn test_find_all_refs_pub_enum() {
169 let code = r#"
170 //- /lib.rs
171 pub enum Foo {
172 A,
173 B<|>,
174 C,
175 }
176 "#;
177
178 let refs = get_all_refs(code);
179 assert_eq!(refs.len(), 1);
180 }
181
182 fn get_all_refs(text: &str) -> ReferenceSearchResult {
183 let (analysis, position) = single_file_with_position(text);
184 analysis.find_all_refs(position).unwrap().unwrap()
185 }
186} \ No newline at end of file
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 1d290387c..03104e348 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -14,7 +14,7 @@ use ra_syntax::{
14 14
15use crate::{ 15use crate::{
16 db::RootDatabase, 16 db::RootDatabase,
17 name_ref_kind::{classify_name_ref, NameRefKind::*}, 17 name_ref_kind::{classify_name_ref, NameKind::*},
18 FileId, 18 FileId,
19}; 19};
20 20