aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/goto_defenition.rs177
-rw-r--r--crates/ra_analysis/src/hover.rs17
-rw-r--r--crates/ra_analysis/src/imp.rs64
-rw-r--r--crates/ra_analysis/src/lib.rs28
-rw-r--r--crates/ra_analysis/tests/test/main.rs59
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs5
6 files changed, 138 insertions, 212 deletions
diff --git a/crates/ra_analysis/src/goto_defenition.rs b/crates/ra_analysis/src/goto_defenition.rs
index 607a25115..e37421f8d 100644
--- a/crates/ra_analysis/src/goto_defenition.rs
+++ b/crates/ra_analysis/src/goto_defenition.rs
@@ -1,73 +1,138 @@
1use ra_db::FileId; 1use ra_db::{FileId, Cancelable, SyntaxDatabase};
2use ra_syntax::ast; 2use ra_syntax::{TextRange, AstNode, ast, SyntaxKind::{NAME, MODULE}};
3 3
4use crate::db::RootDatabase; 4use ra_editor::find_node_at_offset;
5 5
6pub fn goto_defenition(db: &RootDatabase, position: FilePosition, 6use crate::{FilePosition, NavigationTarget, db::RootDatabase};
7
8pub(crate) fn goto_defenition(
9 db: &RootDatabase,
10 position: FilePosition,
7) -> Cancelable<Option<Vec<NavigationTarget>>> { 11) -> Cancelable<Option<Vec<NavigationTarget>>> {
8 let file = db.source_file(position.file_id); 12 let file = db.source_file(position.file_id);
9 let syntax = file.syntax(); 13 let syntax = file.syntax();
10 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 14 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
11 return Ok(Some(reference_defenition(db, position.file_id, name_ref))); 15 return Ok(Some(reference_defenition(db, position.file_id, name_ref)?));
12 } 16 }
13 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 17 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
14 return Ok(Some(name_defenition(db, position.file_idname))); 18 return name_defenition(db, position.file_id, name);
15 } 19 }
16 Ok(None) 20 Ok(None)
17} 21}
18 22
19fn reference_defenition(db: &RootDatabase, file_id: FileId, name_ref: ast::NameRef) -> Cancelable<Vec<Nav>> { 23pub(crate) fn reference_defenition(
20 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { 24 db: &RootDatabase,
21 let mut rr = ReferenceResolution::new(name_ref.syntax().range()); 25 file_id: FileId,
22 if let Some(fn_descr) = 26 name_ref: ast::NameRef,
23 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())? 27) -> Cancelable<Vec<NavigationTarget>> {
24 { 28 if let Some(fn_descr) =
25 let scope = fn_descr.scopes(self); 29 hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
26 // First try to resolve the symbol locally 30 {
27 if let Some(entry) = scope.resolve_local_name(name_ref) { 31 let scope = fn_descr.scopes(db);
28 rr.resolves_to.push(NavigationTarget { 32 // First try to resolve the symbol locally
29 file_id: position.file_id, 33 if let Some(entry) = scope.resolve_local_name(name_ref) {
30 name: entry.name().to_string().into(), 34 let nav = NavigationTarget {
31 range: entry.ptr().range(), 35 file_id,
32 kind: NAME, 36 name: entry.name().to_string().into(),
33 ptr: None, 37 range: entry.ptr().range(),
34 }); 38 kind: NAME,
35 return Ok(Some(rr)); 39 ptr: None,
36 }; 40 };
37 } 41 return Ok(vec![nav]);
38 // If that fails try the index based approach. 42 };
39 rr.resolves_to.extend(
40 self.index_resolve(name_ref)?
41 .into_iter()
42 .map(NavigationTarget::from_symbol),
43 );
44 return Ok(Some(rr));
45 } 43 }
46 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { 44 // If that fails try the index based approach.
47 let mut rr = ReferenceResolution::new(name.syntax().range()); 45 let navs = db
48 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 46 .index_resolve(name_ref)?
49 if module.has_semi() { 47 .into_iter()
50 if let Some(child_module) = 48 .map(NavigationTarget::from_symbol)
51 source_binder::module_from_declaration(self, position.file_id, module)? 49 .collect();
52 { 50 Ok(navs)
53 let file_id = child_module.file_id(); 51}
54 let name = match child_module.name() { 52
55 Some(name) => name.to_string().into(), 53fn name_defenition(
56 None => "".into(), 54 db: &RootDatabase,
57 }; 55 file_id: FileId,
58 let symbol = NavigationTarget { 56 name: ast::Name,
59 file_id, 57) -> Cancelable<Option<Vec<NavigationTarget>>> {
60 name, 58 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
61 range: TextRange::offset_len(0.into(), 0.into()), 59 if module.has_semi() {
62 kind: MODULE, 60 if let Some(child_module) =
63 ptr: None, 61 hir::source_binder::module_from_declaration(db, file_id, module)?
64 }; 62 {
65 rr.resolves_to.push(symbol); 63 let file_id = child_module.file_id();
66 return Ok(Some(rr)); 64 let name = match child_module.name() {
67 } 65 Some(name) => name.to_string().into(),
68 } 66 None => "".into(),
67 };
68 let nav = NavigationTarget {
69 file_id,
70 name,
71 range: TextRange::offset_len(0.into(), 0.into()),
72 kind: MODULE,
73 ptr: None,
74 };
75 return Ok(Some(vec![nav]));
69 } 76 }
70 } 77 }
71 Ok(None) 78 }
79 Ok(None)
80}
81
82#[cfg(test)]
83mod tests {
84 use test_utils::assert_eq_dbg;
85 use crate::mock_analysis::analysis_and_position;
72 86
87 #[test]
88 fn goto_defenition_works_in_items() {
89 let (analysis, pos) = analysis_and_position(
90 "
91 //- /lib.rs
92 struct Foo;
93 enum E { X(Foo<|>) }
94 ",
95 );
96
97 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
98 assert_eq_dbg(
99 r#"[NavigationTarget { file_id: FileId(1), name: "Foo",
100 kind: STRUCT_DEF, range: [0; 11),
101 ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#,
102 &symbols,
103 );
104 }
105
106 #[test]
107 fn goto_defenition_works_for_module_declaration() {
108 let (analysis, pos) = analysis_and_position(
109 "
110 //- /lib.rs
111 mod <|>foo;
112 //- /foo.rs
113 // empty
114 ",
115 );
116
117 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
118 assert_eq_dbg(
119 r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#,
120 &symbols,
121 );
122
123 let (analysis, pos) = analysis_and_position(
124 "
125 //- /lib.rs
126 mod <|>foo;
127 //- /foo/mod.rs
128 // empty
129 ",
130 );
131
132 let symbols = analysis.goto_defenition(pos).unwrap().unwrap();
133 assert_eq_dbg(
134 r#"[NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]"#,
135 &symbols,
136 );
137 }
73} 138}
diff --git a/crates/ra_analysis/src/hover.rs b/crates/ra_analysis/src/hover.rs
index 766fa0547..758de376e 100644
--- a/crates/ra_analysis/src/hover.rs
+++ b/crates/ra_analysis/src/hover.rs
@@ -1,4 +1,5 @@
1use ra_db::{Cancelable, SyntaxDatabase}; 1use ra_db::{Cancelable, SyntaxDatabase};
2use ra_editor::find_node_at_offset;
2use ra_syntax::{ 3use ra_syntax::{
3 AstNode, SyntaxNode, 4 AstNode, SyntaxNode,
4 ast::{self, NameOwner}, 5 ast::{self, NameOwner},
@@ -11,18 +12,18 @@ pub(crate) fn hover(
11 db: &RootDatabase, 12 db: &RootDatabase,
12 position: FilePosition, 13 position: FilePosition,
13) -> Cancelable<Option<RangeInfo<String>>> { 14) -> Cancelable<Option<RangeInfo<String>>> {
15 let file = db.source_file(position.file_id);
14 let mut res = Vec::new(); 16 let mut res = Vec::new();
15 let range = if let Some(rr) = db.approximately_resolve_symbol(position)? { 17 let range = if let Some(name_ref) =
16 for nav in rr.resolves_to { 18 find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)
19 {
20 let navs = crate::goto_defenition::reference_defenition(db, position.file_id, name_ref)?;
21 for nav in navs {
17 res.extend(doc_text_for(db, nav)?) 22 res.extend(doc_text_for(db, nav)?)
18 } 23 }
19 rr.reference_range 24 name_ref.syntax().range()
20 } else { 25 } else {
21 let file = db.source_file(position.file_id); 26 let expr: ast::Expr = ctry!(find_node_at_offset(file.syntax(), position.offset));
22 let expr: ast::Expr = ctry!(ra_editor::find_node_at_offset(
23 file.syntax(),
24 position.offset
25 ));
26 let frange = FileRange { 27 let frange = FileRange {
27 file_id: position.file_id, 28 file_id: position.file_id,
28 range: expr.syntax().range(), 29 range: expr.syntax().range(),
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index e2871451c..6ab3c5476 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -18,7 +18,7 @@ use crate::{
18 AnalysisChange, 18 AnalysisChange,
19 Cancelable, NavigationTarget, 19 Cancelable, NavigationTarget,
20 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, 20 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
21 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 21 Query, RootChange, SourceChange, SourceFileEdit,
22 symbol_index::{LibrarySymbolsQuery, FileSymbol}, 22 symbol_index::{LibrarySymbolsQuery, FileSymbol},
23}; 23};
24 24
@@ -139,66 +139,6 @@ impl db::RootDatabase {
139 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId { 139 pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId {
140 self.crate_graph().crate_root(crate_id) 140 self.crate_graph().crate_root(crate_id)
141 } 141 }
142 pub(crate) fn approximately_resolve_symbol(
143 &self,
144 position: FilePosition,
145 ) -> Cancelable<Option<ReferenceResolution>> {
146 let file = self.source_file(position.file_id);
147 let syntax = file.syntax();
148 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
149 let mut rr = ReferenceResolution::new(name_ref.syntax().range());
150 if let Some(fn_descr) =
151 source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())?
152 {
153 let scope = fn_descr.scopes(self);
154 // First try to resolve the symbol locally
155 if let Some(entry) = scope.resolve_local_name(name_ref) {
156 rr.resolves_to.push(NavigationTarget {
157 file_id: position.file_id,
158 name: entry.name().to_string().into(),
159 range: entry.ptr().range(),
160 kind: NAME,
161 ptr: None,
162 });
163 return Ok(Some(rr));
164 };
165 }
166 // If that fails try the index based approach.
167 rr.resolves_to.extend(
168 self.index_resolve(name_ref)?
169 .into_iter()
170 .map(NavigationTarget::from_symbol),
171 );
172 return Ok(Some(rr));
173 }
174 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
175 let mut rr = ReferenceResolution::new(name.syntax().range());
176 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
177 if module.has_semi() {
178 if let Some(child_module) =
179 source_binder::module_from_declaration(self, position.file_id, module)?
180 {
181 let file_id = child_module.file_id();
182 let name = match child_module.name() {
183 Some(name) => name.to_string().into(),
184 None => "".into(),
185 };
186 let symbol = NavigationTarget {
187 file_id,
188 name,
189 range: TextRange::offset_len(0.into(), 0.into()),
190 kind: MODULE,
191 ptr: None,
192 };
193 rr.resolves_to.push(symbol);
194 return Ok(Some(rr));
195 }
196 }
197 }
198 }
199 Ok(None)
200 }
201
202 pub(crate) fn find_all_refs( 142 pub(crate) fn find_all_refs(
203 &self, 143 &self,
204 position: FilePosition, 144 position: FilePosition,
@@ -416,7 +356,7 @@ impl db::RootDatabase {
416 .collect::<Vec<_>>(); 356 .collect::<Vec<_>>();
417 Ok(res) 357 Ok(res)
418 } 358 }
419 fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<FileSymbol>> { 359 pub(crate) fn index_resolve(&self, name_ref: ast::NameRef) -> Cancelable<Vec<FileSymbol>> {
420 let name = name_ref.text(); 360 let name = name_ref.text();
421 let mut query = Query::new(name.to_string()); 361 let mut query = Query::new(name.to_string());
422 query.exact(); 362 query.exact();
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index b068119d2..4d895b004 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -15,6 +15,7 @@ macro_rules! ctry {
15mod db; 15mod db;
16mod imp; 16mod imp;
17mod completion; 17mod completion;
18mod goto_defenition;
18mod symbol_index; 19mod symbol_index;
19pub mod mock_analysis; 20pub mod mock_analysis;
20mod runnables; 21mod runnables;
@@ -273,26 +274,6 @@ impl<T> RangeInfo<T> {
273 } 274 }
274} 275}
275 276
276/// Result of "goto def" query.
277#[derive(Debug)]
278pub struct ReferenceResolution {
279 /// The range of the reference itself. Client does not know what constitutes
280 /// a reference, it handles us only the offset. It's helpful to tell the
281 /// client where the reference was.
282 pub reference_range: TextRange,
283 /// What this reference resolves to.
284 pub resolves_to: Vec<NavigationTarget>,
285}
286
287impl ReferenceResolution {
288 fn new(reference_range: TextRange) -> ReferenceResolution {
289 ReferenceResolution {
290 reference_range,
291 resolves_to: Vec::new(),
292 }
293 }
294}
295
296/// `AnalysisHost` stores the current state of the world. 277/// `AnalysisHost` stores the current state of the world.
297#[derive(Debug, Default)] 278#[derive(Debug, Default)]
298pub struct AnalysisHost { 279pub struct AnalysisHost {
@@ -392,12 +373,11 @@ impl Analysis {
392 .collect(); 373 .collect();
393 Ok(res) 374 Ok(res)
394 } 375 }
395 /// Resolves reference to definition, but does not gurantee correctness. 376 pub fn goto_defenition(
396 pub fn approximately_resolve_symbol(
397 &self, 377 &self,
398 position: FilePosition, 378 position: FilePosition,
399 ) -> Cancelable<Option<ReferenceResolution>> { 379 ) -> Cancelable<Option<Vec<NavigationTarget>>> {
400 self.db.approximately_resolve_symbol(position) 380 goto_defenition::goto_defenition(&*self.db, position)
401 } 381 }
402 /// Finds all usages of the reference at point. 382 /// Finds all usages of the reference at point.
403 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 383 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
diff --git a/crates/ra_analysis/tests/test/main.rs b/crates/ra_analysis/tests/test/main.rs
index beeae1e19..e15035304 100644
--- a/crates/ra_analysis/tests/test/main.rs
+++ b/crates/ra_analysis/tests/test/main.rs
@@ -15,65 +15,6 @@ fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) {
15} 15}
16 16
17#[test] 17#[test]
18fn approximate_resolve_works_in_items() {
19 let (analysis, pos) = analysis_and_position(
20 "
21 //- /lib.rs
22 struct Foo;
23 enum E { X(Foo<|>) }
24 ",
25 );
26
27 let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap();
28 assert_eq_dbg(
29 r#"ReferenceResolution {
30 reference_range: [23; 26),
31 resolves_to: [NavigationTarget { file_id: FileId(1), name: "Foo", kind: STRUCT_DEF, range: [0; 11), ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]
32 }"#,
33 &symbols,
34 );
35}
36
37#[test]
38fn test_resolve_module() {
39 let (analysis, pos) = analysis_and_position(
40 "
41 //- /lib.rs
42 mod <|>foo;
43 //- /foo.rs
44 // empty
45 ",
46 );
47
48 let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap();
49 assert_eq_dbg(
50 r#"ReferenceResolution {
51 reference_range: [4; 7),
52 resolves_to: [NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]
53 }"#,
54 &symbols,
55 );
56
57 let (analysis, pos) = analysis_and_position(
58 "
59 //- /lib.rs
60 mod <|>foo;
61 //- /foo/mod.rs
62 // empty
63 ",
64 );
65
66 let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap();
67 assert_eq_dbg(
68 r#"ReferenceResolution {
69 reference_range: [4; 7),
70 resolves_to: [NavigationTarget { file_id: FileId(2), name: "foo", kind: MODULE, range: [0; 0), ptr: None }]
71 }"#,
72 &symbols,
73 );
74}
75
76#[test]
77fn test_unresolved_module_diagnostic() { 18fn test_unresolved_module_diagnostic() {
78 let (analysis, file_id) = single_file("mod foo;"); 19 let (analysis, file_id) = single_file("mod foo;");
79 let diagnostics = analysis.diagnostics(file_id).unwrap(); 20 let diagnostics = analysis.diagnostics(file_id).unwrap();
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index ffca3f51c..1baed73ad 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -207,12 +207,11 @@ pub fn handle_goto_definition(
207 params: req::TextDocumentPositionParams, 207 params: req::TextDocumentPositionParams,
208) -> Result<Option<req::GotoDefinitionResponse>> { 208) -> Result<Option<req::GotoDefinitionResponse>> {
209 let position = params.try_conv_with(&world)?; 209 let position = params.try_conv_with(&world)?;
210 let rr = match world.analysis().approximately_resolve_symbol(position)? { 210 let navs = match world.analysis().goto_defenition(position)? {
211 None => return Ok(None), 211 None => return Ok(None),
212 Some(it) => it, 212 Some(it) => it,
213 }; 213 };
214 let res = rr 214 let res = navs
215 .resolves_to
216 .into_iter() 215 .into_iter()
217 .map(|nav| nav.try_conv_with(&world)) 216 .map(|nav| nav.try_conv_with(&world))
218 .collect::<Result<Vec<_>>>()?; 217 .collect::<Result<Vec<_>>>()?;