aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-30 12:30:51 +0100
committerGitHub <[email protected]>2020-06-30 12:30:51 +0100
commit6a73d544f4117b942b300afb8bda98216fc92356 (patch)
tree18b444a0d447465a7de75ec5a0153c90b8566a7f /crates
parent5a0fb3caff745d20face2d7ab02fa4da63faef9c (diff)
parentaf7e300041b1af68e671446fe22d2b9e5d30f83d (diff)
Merge #5137
5137: Make gotodef tests more data-driven r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_db/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/src/test_db.rs22
-rw-r--r--crates/ra_hir_ty/src/tests.rs5
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs45
-rw-r--r--crates/ra_ide/src/goto_definition.rs1063
-rw-r--r--crates/ra_ide/src/goto_implementation.rs193
-rw-r--r--crates/ra_ide/src/mock_analysis.rs22
-rw-r--r--crates/rust-analyzer/src/handlers.rs9
-rw-r--r--crates/rust-analyzer/src/to_proto.rs12
9 files changed, 636 insertions, 737 deletions
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 4a3ba57da..1ddacc1f6 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -80,7 +80,7 @@ pub struct FilePosition {
80 pub offset: TextSize, 80 pub offset: TextSize,
81} 81}
82 82
83#[derive(Clone, Copy, Debug)] 83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub struct FileRange { 84pub struct FileRange {
85 pub file_id: FileId, 85 pub file_id: FileId,
86 pub range: TextRange, 86 pub range: TextRange,
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 0481a7b12..fddf0604d 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -8,8 +8,10 @@ use std::{
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11use rustc_hash::FxHashSet; 11use ra_syntax::TextRange;
12use rustc_hash::{FxHashMap, FxHashSet};
12use stdx::format_to; 13use stdx::format_to;
14use test_utils::extract_annotations;
13 15
14use crate::{ 16use crate::{
15 db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, 17 db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator,
@@ -155,17 +157,27 @@ impl TestDB {
155 (buf, count) 157 (buf, count)
156 } 158 }
157 159
158 pub fn all_files(&self) -> Vec<FileId> { 160 pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
159 let mut res = Vec::new(); 161 let mut files = Vec::new();
160 let crate_graph = self.crate_graph(); 162 let crate_graph = self.crate_graph();
161 for krate in crate_graph.iter() { 163 for krate in crate_graph.iter() {
162 let crate_def_map = self.crate_def_map(krate); 164 let crate_def_map = self.crate_def_map(krate);
163 for (module_id, _) in crate_def_map.modules.iter() { 165 for (module_id, _) in crate_def_map.modules.iter() {
164 let file_id = crate_def_map[module_id].origin.file_id(); 166 let file_id = crate_def_map[module_id].origin.file_id();
165 res.extend(file_id) 167 files.extend(file_id)
166 } 168 }
167 } 169 }
168 res 170 files
171 .into_iter()
172 .filter_map(|file_id| {
173 let text = self.file_text(file_id);
174 let annotations = extract_annotations(&text);
175 if annotations.is_empty() {
176 return None;
177 }
178 Some((file_id, annotations))
179 })
180 .collect()
169 } 181 }
170} 182}
171 183
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 5424e6bb1..9084c3bed 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -28,7 +28,6 @@ use ra_syntax::{
28 SyntaxNode, 28 SyntaxNode,
29}; 29};
30use stdx::format_to; 30use stdx::format_to;
31use test_utils::extract_annotations;
32 31
33use crate::{ 32use crate::{
34 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, 33 db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
@@ -49,9 +48,7 @@ fn check_types_source_code(ra_fixture: &str) {
49fn check_types_impl(ra_fixture: &str, display_source: bool) { 48fn check_types_impl(ra_fixture: &str, display_source: bool) {
50 let db = TestDB::with_files(ra_fixture); 49 let db = TestDB::with_files(ra_fixture);
51 let mut checked_one = false; 50 let mut checked_one = false;
52 for file_id in db.all_files() { 51 for (file_id, annotations) in db.extract_annotations() {
53 let text = db.parse(file_id).syntax_node().to_string();
54 let annotations = extract_annotations(&text);
55 for (range, expected) in annotations { 52 for (range, expected) in annotations {
56 let ty = type_at_range(&db, FileRange { file_id, range }); 53 let ty = type_at_range(&db, FileRange { file_id, range });
57 let actual = if display_source { 54 let actual = if display_source {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 0b52b01ab..8bf2428ed 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 TextRange, 11 TextRange,
12}; 12};
13 13
14use crate::{FileRange, FileSymbol}; 14use crate::FileSymbol;
15 15
16use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
17 17
@@ -47,6 +47,19 @@ impl NavigationTarget {
47 pub fn range(&self) -> TextRange { 47 pub fn range(&self) -> TextRange {
48 self.focus_range.unwrap_or(self.full_range) 48 self.focus_range.unwrap_or(self.full_range)
49 } 49 }
50 /// A "most interesting" range withing the `full_range`.
51 ///
52 /// Typically, `full_range` is the whole syntax node,
53 /// including doc comments, and `focus_range` is the range of the identifier.
54 pub fn focus_range(&self) -> Option<TextRange> {
55 self.focus_range
56 }
57 pub fn full_range(&self) -> TextRange {
58 self.full_range
59 }
60 pub fn file_id(&self) -> FileId {
61 self.file_id
62 }
50 63
51 pub fn name(&self) -> &SmolStr { 64 pub fn name(&self) -> &SmolStr {
52 &self.name 65 &self.name
@@ -60,18 +73,6 @@ impl NavigationTarget {
60 self.kind 73 self.kind
61 } 74 }
62 75
63 pub fn file_id(&self) -> FileId {
64 self.file_id
65 }
66
67 pub fn file_range(&self) -> FileRange {
68 FileRange { file_id: self.file_id, range: self.full_range }
69 }
70
71 pub fn full_range(&self) -> TextRange {
72 self.full_range
73 }
74
75 pub fn docs(&self) -> Option<&str> { 76 pub fn docs(&self) -> Option<&str> {
76 self.docs.as_deref() 77 self.docs.as_deref()
77 } 78 }
@@ -80,14 +81,6 @@ impl NavigationTarget {
80 self.description.as_deref() 81 self.description.as_deref()
81 } 82 }
82 83
83 /// A "most interesting" range withing the `full_range`.
84 ///
85 /// Typically, `full_range` is the whole syntax node,
86 /// including doc comments, and `focus_range` is the range of the identifier.
87 pub fn focus_range(&self) -> Option<TextRange> {
88 self.focus_range
89 }
90
91 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 84 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 85 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 86 if let Some(src) = module.declaration_source(db) {
@@ -278,16 +271,22 @@ impl ToNav for hir::Module {
278impl ToNav for hir::ImplDef { 271impl ToNav for hir::ImplDef {
279 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 272 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
280 let src = self.source(db); 273 let src = self.source(db);
281 let frange = if let Some(item) = self.is_builtin_derive(db) { 274 let derive_attr = self.is_builtin_derive(db);
275 let frange = if let Some(item) = &derive_attr {
282 original_range(db, item.syntax()) 276 original_range(db, item.syntax())
283 } else { 277 } else {
284 original_range(db, src.as_ref().map(|it| it.syntax())) 278 original_range(db, src.as_ref().map(|it| it.syntax()))
285 }; 279 };
280 let focus_range = if derive_attr.is_some() {
281 None
282 } else {
283 src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
284 };
286 285
287 NavigationTarget::from_syntax( 286 NavigationTarget::from_syntax(
288 frange.file_id, 287 frange.file_id,
289 "impl".into(), 288 "impl".into(),
290 None, 289 focus_range,
291 frange.range, 290 frange.range,
292 src.value.syntax().kind(), 291 src.value.syntax().kind(),
293 ) 292 )
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 969d5e0ff..4c78fa214 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -103,67 +103,32 @@ pub(crate) fn reference_definition(
103 103
104#[cfg(test)] 104#[cfg(test)]
105mod tests { 105mod tests {
106 use expect::{expect, Expect}; 106 use ra_db::FileRange;
107 use test_utils::assert_eq_text; 107 use ra_syntax::{TextRange, TextSize};
108 108
109 use crate::mock_analysis::analysis_and_position; 109 use crate::mock_analysis::MockAnalysis;
110 110
111 fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { 111 fn check(ra_fixture: &str) {
112 let (analysis, pos) = analysis_and_position(ra_fixture); 112 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
113 113 let (mut expected, data) = mock.annotation();
114 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 114 let analysis = mock.analysis();
115 if navs.len() == 0 { 115 match data.as_str() {
116 panic!("unresolved reference") 116 "" => (),
117 } 117 "file" => {
118 assert_eq!(navs.len(), 1); 118 expected.range =
119 119 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
120 let nav = navs.pop().unwrap(); 120 }
121 let file_text = analysis.file_text(nav.file_id()).unwrap(); 121 data => panic!("bad data: {}", data),
122
123 let mut actual = file_text[nav.full_range()].to_string();
124 if let Some(focus) = nav.focus_range() {
125 actual += "|";
126 actual += &file_text[focus];
127 }
128
129 if !expected_range.contains("...") {
130 test_utils::assert_eq_text!(&actual, expected_range);
131 } else {
132 let mut parts = expected_range.split("...");
133 let prefix = parts.next().unwrap();
134 let suffix = parts.next().unwrap();
135 assert!(
136 actual.starts_with(prefix) && actual.ends_with(suffix),
137 "\nExpected: {}\n Actual: {}\n",
138 expected_range,
139 actual
140 );
141 } 122 }
142 123
143 nav.assert_match(expected); 124 let mut navs = analysis.goto_definition(position).unwrap().unwrap().info;
144 }
145
146 fn check(ra_fixture: &str, expect: Expect) {
147 let (analysis, pos) = analysis_and_position(ra_fixture);
148
149 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
150 if navs.len() == 0 { 125 if navs.len() == 0 {
151 panic!("unresolved reference") 126 panic!("unresolved reference")
152 } 127 }
153 assert_eq!(navs.len(), 1); 128 assert_eq!(navs.len(), 1);
154 129
155 let nav = navs.pop().unwrap(); 130 let nav = navs.pop().unwrap();
156 let file_text = analysis.file_text(nav.file_id()).unwrap(); 131 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
157
158 let mut actual = nav.debug_render();
159 actual += "\n";
160 actual += &file_text[nav.full_range()].to_string();
161 if let Some(focus) = nav.focus_range() {
162 actual += "|";
163 actual += &file_text[focus];
164 actual += "\n";
165 }
166 expect.assert_eq(&actual);
167 } 132 }
168 133
169 #[test] 134 #[test]
@@ -171,162 +136,116 @@ mod tests {
171 check( 136 check(
172 r#" 137 r#"
173struct Foo; 138struct Foo;
139 //^^^
174enum E { X(Foo<|>) } 140enum E { X(Foo<|>) }
175"#, 141"#,
176 expect![[r#"
177 Foo STRUCT_DEF FileId(1) 0..11 7..10
178 struct Foo;|Foo
179 "#]],
180 ); 142 );
181 } 143 }
182 144
183 #[test] 145 #[test]
184 fn goto_def_at_start_of_item() { 146 fn goto_def_at_start_of_item() {
185 check_goto( 147 check(
186 " 148 r#"
187 //- /lib.rs 149struct Foo;
188 struct Foo; 150 //^^^
189 enum E { X(<|>Foo) } 151enum E { X(<|>Foo) }
190 ", 152"#,
191 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
192 "struct Foo;|Foo",
193 ); 153 );
194 } 154 }
195 155
196 #[test] 156 #[test]
197 fn goto_definition_resolves_correct_name() { 157 fn goto_definition_resolves_correct_name() {
198 check_goto( 158 check(
199 " 159 r#"
200 //- /lib.rs 160//- /lib.rs
201 use a::Foo; 161use a::Foo;
202 mod a; 162mod a;
203 mod b; 163mod b;
204 enum E { X(Foo<|>) } 164enum E { X(Foo<|>) }
205
206 //- /a.rs
207 struct Foo;
208 165
209 //- /b.rs 166//- /a.rs
210 struct Foo; 167struct Foo;
211 ", 168 //^^^
212 "Foo STRUCT_DEF FileId(2) 0..11 7..10", 169//- /b.rs
213 "struct Foo;|Foo", 170struct Foo;
171"#,
214 ); 172 );
215 } 173 }
216 174
217 #[test] 175 #[test]
218 fn goto_def_for_module_declaration() { 176 fn goto_def_for_module_declaration() {
219 check_goto( 177 check(
220 r#" 178 r#"
221//- /lib.rs 179//- /lib.rs
222mod <|>foo; 180mod <|>foo;
223 181
224//- /foo.rs 182//- /foo.rs
225// empty 183// empty
184//^ file
226"#, 185"#,
227 "foo SOURCE_FILE FileId(2) 0..9",
228 "// empty\n",
229 ); 186 );
230 187
231 check_goto( 188 check(
232 r#" 189 r#"
233//- /lib.rs 190//- /lib.rs
234mod <|>foo; 191mod <|>foo;
235 192
236//- /foo/mod.rs 193//- /foo/mod.rs
237// empty 194// empty
195//^ file
238"#, 196"#,
239 "foo SOURCE_FILE FileId(2) 0..9",
240 "// empty\n",
241 ); 197 );
242 } 198 }
243 199
244 #[test] 200 #[test]
245 fn goto_def_for_macros() { 201 fn goto_def_for_macros() {
246 check_goto( 202 check(
247 " 203 r#"
248 //- /lib.rs 204macro_rules! foo { () => { () } }
249 macro_rules! foo { () => { () } } 205 //^^^
250 206fn bar() {
251 fn bar() { 207 <|>foo!();
252 <|>foo!(); 208}
253 } 209"#,
254 ",
255 "foo MACRO_CALL FileId(1) 0..33 13..16",
256 "macro_rules! foo { () => { () } }|foo",
257 ); 210 );
258 } 211 }
259 212
260 #[test] 213 #[test]
261 fn goto_def_for_macros_from_other_crates() { 214 fn goto_def_for_macros_from_other_crates() {
262 check_goto( 215 check(
263 "
264 //- /lib.rs
265 use foo::foo;
266 fn bar() {
267 <|>foo!();
268 }
269
270 //- /foo/lib.rs
271 #[macro_export]
272 macro_rules! foo { () => { () } }
273 ",
274 "foo MACRO_CALL FileId(2) 0..49 29..32",
275 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
276 );
277 }
278
279 #[test]
280 fn goto_def_for_use_alias() {
281 check_goto(
282 r#" 216 r#"
283//- /lib.rs 217//- /lib.rs
284use foo as bar<|>; 218use foo::foo;
219fn bar() {
220 <|>foo!();
221}
285 222
286//- /foo/lib.rs 223//- /foo/lib.rs
287#[macro_export] 224#[macro_export]
288macro_rules! foo { () => { () } } 225macro_rules! foo { () => { () } }
226 //^^^
289"#, 227"#,
290 "SOURCE_FILE FileId(2) 0..50",
291 "#[macro_export]\nmacro_rules! foo { () => { () } }\n",
292 );
293 }
294
295 #[test]
296 fn goto_def_for_use_alias_foo_macro() {
297 check_goto(
298 "
299 //- /lib.rs
300 use foo::foo as bar<|>;
301
302 //- /foo/lib.rs
303 #[macro_export]
304 macro_rules! foo { () => { () } }
305 ",
306 "foo MACRO_CALL FileId(2) 0..49 29..32",
307 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
308 ); 228 );
309 } 229 }
310 230
311 #[test] 231 #[test]
312 fn goto_def_for_macros_in_use_tree() { 232 fn goto_def_for_macros_in_use_tree() {
313 check_goto( 233 check(
314 " 234 r#"
315 //- /lib.rs 235//- /lib.rs
316 use foo::foo<|>; 236use foo::foo<|>;
317 237
318 //- /foo/lib.rs 238//- /foo/lib.rs
319 #[macro_export] 239#[macro_export]
320 macro_rules! foo { () => { () } } 240macro_rules! foo { () => { () } }
321 ", 241 //^^^
322 "foo MACRO_CALL FileId(2) 0..49 29..32", 242"#,
323 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
324 ); 243 );
325 } 244 }
326 245
327 #[test] 246 #[test]
328 fn goto_def_for_macro_defined_fn_with_arg() { 247 fn goto_def_for_macro_defined_fn_with_arg() {
329 check_goto( 248 check(
330 r#" 249 r#"
331//- /lib.rs 250//- /lib.rs
332macro_rules! define_fn { 251macro_rules! define_fn {
@@ -334,522 +253,478 @@ macro_rules! define_fn {
334} 253}
335 254
336define_fn!(foo); 255define_fn!(foo);
256 //^^^
337 257
338fn bar() { 258fn bar() {
339 <|>foo(); 259 <|>foo();
340} 260}
341"#, 261"#,
342 "foo FN_DEF FileId(1) 65..81 76..79",
343 "define_fn!(foo);|foo",
344 ); 262 );
345 } 263 }
346 264
347 #[test] 265 #[test]
348 fn goto_def_for_macro_defined_fn_no_arg() { 266 fn goto_def_for_macro_defined_fn_no_arg() {
349 check_goto( 267 check(
350 r#" 268 r#"
351//- /lib.rs 269//- /lib.rs
352macro_rules! define_fn { 270macro_rules! define_fn {
353 () => (fn foo() {}) 271 () => (fn foo() {})
354} 272}
355 273
356define_fn!(); 274 define_fn!();
275//^^^^^^^^^^^^^
357 276
358fn bar() { 277fn bar() {
359 <|>foo(); 278 <|>foo();
360} 279}
361"#, 280"#,
362 "foo FN_DEF FileId(1) 52..65 52..65",
363 "define_fn!();|define_fn!();",
364 ); 281 );
365 } 282 }
366 283
367 #[test] 284 #[test]
368 fn goto_definition_works_for_macro_inside_pattern() { 285 fn goto_definition_works_for_macro_inside_pattern() {
369 check_goto( 286 check(
370 " 287 r#"
371 //- /lib.rs 288//- /lib.rs
372 macro_rules! foo {() => {0}} 289macro_rules! foo {() => {0}}
373 290 //^^^
374 fn bar() { 291
375 match (0,1) { 292fn bar() {
376 (<|>foo!(), _) => {} 293 match (0,1) {
377 } 294 (<|>foo!(), _) => {}
378 } 295 }
379 ", 296}
380 "foo MACRO_CALL FileId(1) 0..28 13..16", 297"#,
381 "macro_rules! foo {() => {0}}|foo",
382 ); 298 );
383 } 299 }
384 300
385 #[test] 301 #[test]
386 fn goto_definition_works_for_macro_inside_match_arm_lhs() { 302 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
387 check_goto( 303 check(
388 " 304 r#"
389 //- /lib.rs 305//- /lib.rs
390 macro_rules! foo {() => {0}} 306macro_rules! foo {() => {0}}
391 307 //^^^
392 fn bar() { 308fn bar() {
393 match 0 { 309 match 0 {
394 <|>foo!() => {} 310 <|>foo!() => {}
395 } 311 }
396 } 312}
397 ", 313"#,
398 "foo MACRO_CALL FileId(1) 0..28 13..16", 314 );
399 "macro_rules! foo {() => {0}}|foo", 315 }
316
317 #[test]
318 fn goto_def_for_use_alias() {
319 check(
320 r#"
321//- /lib.rs
322use foo as bar<|>;
323
324//- /foo/lib.rs
325// empty
326//^ file
327"#,
328 );
329 }
330
331 #[test]
332 fn goto_def_for_use_alias_foo_macro() {
333 check(
334 r#"
335//- /lib.rs
336use foo::foo as bar<|>;
337
338//- /foo/lib.rs
339#[macro_export]
340macro_rules! foo { () => { () } }
341 //^^^
342"#,
400 ); 343 );
401 } 344 }
402 345
403 #[test] 346 #[test]
404 fn goto_def_for_methods() { 347 fn goto_def_for_methods() {
405 check_goto( 348 check(
406 " 349 r#"
407 //- /lib.rs 350//- /lib.rs
408 struct Foo; 351struct Foo;
409 impl Foo { 352impl Foo {
410 fn frobnicate(&self) { } 353 fn frobnicate(&self) { }
411 } 354 //^^^^^^^^^^
355}
412 356
413 fn bar(foo: &Foo) { 357fn bar(foo: &Foo) {
414 foo.frobnicate<|>(); 358 foo.frobnicate<|>();
415 } 359}
416 ", 360"#,
417 "frobnicate FN_DEF FileId(1) 27..51 30..40",
418 "fn frobnicate(&self) { }|frobnicate",
419 ); 361 );
420 } 362 }
421 363
422 #[test] 364 #[test]
423 fn goto_def_for_fields() { 365 fn goto_def_for_fields() {
424 check_goto( 366 check(
425 r" 367 r#"
426 //- /lib.rs 368struct Foo {
427 struct Foo { 369 spam: u32,
428 spam: u32, 370} //^^^^
429 }
430 371
431 fn bar(foo: &Foo) { 372fn bar(foo: &Foo) {
432 foo.spam<|>; 373 foo.spam<|>;
433 } 374}
434 ", 375"#,
435 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
436 "spam: u32|spam",
437 ); 376 );
438 } 377 }
439 378
440 #[test] 379 #[test]
441 fn goto_def_for_record_fields() { 380 fn goto_def_for_record_fields() {
442 check_goto( 381 check(
443 r" 382 r#"
444 //- /lib.rs 383//- /lib.rs
445 struct Foo { 384struct Foo {
446 spam: u32, 385 spam: u32,
447 } 386} //^^^^
448 387
449 fn bar() -> Foo { 388fn bar() -> Foo {
450 Foo { 389 Foo {
451 spam<|>: 0, 390 spam<|>: 0,
452 } 391 }
453 } 392}
454 ", 393"#,
455 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
456 "spam: u32|spam",
457 ); 394 );
458 } 395 }
459 396
460 #[test] 397 #[test]
461 fn goto_def_for_record_pat_fields() { 398 fn goto_def_for_record_pat_fields() {
462 check_goto( 399 check(
463 r" 400 r#"
464 //- /lib.rs 401//- /lib.rs
465 struct Foo { 402struct Foo {
466 spam: u32, 403 spam: u32,
467 } 404} //^^^^
468 405
469 fn bar(foo: Foo) -> Foo { 406fn bar(foo: Foo) -> Foo {
470 let Foo { spam<|>: _, } = foo 407 let Foo { spam<|>: _, } = foo
471 } 408}
472 ", 409"#,
473 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
474 "spam: u32|spam",
475 ); 410 );
476 } 411 }
477 412
478 #[test] 413 #[test]
479 fn goto_def_for_record_fields_macros() { 414 fn goto_def_for_record_fields_macros() {
480 check_goto( 415 check(
481 r" 416 r"
482 //- /lib.rs 417macro_rules! m { () => { 92 };}
483 macro_rules! m { () => { 92 };} 418struct Foo { spam: u32 }
484 struct Foo { spam: u32 } 419 //^^^^
485 420
486 fn bar() -> Foo { 421fn bar() -> Foo {
487 Foo { spam<|>: m!() } 422 Foo { spam<|>: m!() }
488 } 423}
489 ", 424",
490 "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49",
491 "spam: u32|spam",
492 ); 425 );
493 } 426 }
494 427
495 #[test] 428 #[test]
496 fn goto_for_tuple_fields() { 429 fn goto_for_tuple_fields() {
497 check_goto( 430 check(
498 " 431 r#"
499 //- /lib.rs 432struct Foo(u32);
500 struct Foo(u32); 433 //^^^
501 434
502 fn bar() { 435fn bar() {
503 let foo = Foo(0); 436 let foo = Foo(0);
504 foo.<|>0; 437 foo.<|>0;
505 } 438}
506 ", 439"#,
507 "TUPLE_FIELD_DEF FileId(1) 11..14",
508 "u32",
509 ); 440 );
510 } 441 }
511 442
512 #[test] 443 #[test]
513 fn goto_def_for_ufcs_inherent_methods() { 444 fn goto_def_for_ufcs_inherent_methods() {
514 check_goto( 445 check(
515 " 446 r#"
516 //- /lib.rs 447struct Foo;
517 struct Foo; 448impl Foo {
518 impl Foo { 449 fn frobnicate() { }
519 fn frobnicate() { } 450} //^^^^^^^^^^
520 }
521 451
522 fn bar(foo: &Foo) { 452fn bar(foo: &Foo) {
523 Foo::frobnicate<|>(); 453 Foo::frobnicate<|>();
524 } 454}
525 ", 455"#,
526 "frobnicate FN_DEF FileId(1) 27..46 30..40",
527 "fn frobnicate() { }|frobnicate",
528 ); 456 );
529 } 457 }
530 458
531 #[test] 459 #[test]
532 fn goto_def_for_ufcs_trait_methods_through_traits() { 460 fn goto_def_for_ufcs_trait_methods_through_traits() {
533 check_goto( 461 check(
534 " 462 r#"
535 //- /lib.rs 463trait Foo {
536 trait Foo { 464 fn frobnicate();
537 fn frobnicate(); 465} //^^^^^^^^^^
538 }
539 466
540 fn bar() { 467fn bar() {
541 Foo::frobnicate<|>(); 468 Foo::frobnicate<|>();
542 } 469}
543 ", 470"#,
544 "frobnicate FN_DEF FileId(1) 16..32 19..29",
545 "fn frobnicate();|frobnicate",
546 ); 471 );
547 } 472 }
548 473
549 #[test] 474 #[test]
550 fn goto_def_for_ufcs_trait_methods_through_self() { 475 fn goto_def_for_ufcs_trait_methods_through_self() {
551 check_goto( 476 check(
552 " 477 r#"
553 //- /lib.rs 478struct Foo;
554 struct Foo; 479trait Trait {
555 trait Trait { 480 fn frobnicate();
556 fn frobnicate(); 481} //^^^^^^^^^^
557 } 482impl Trait for Foo {}
558 impl Trait for Foo {}
559 483
560 fn bar() { 484fn bar() {
561 Foo::frobnicate<|>(); 485 Foo::frobnicate<|>();
562 } 486}
563 ", 487"#,
564 "frobnicate FN_DEF FileId(1) 30..46 33..43",
565 "fn frobnicate();|frobnicate",
566 ); 488 );
567 } 489 }
568 490
569 #[test] 491 #[test]
570 fn goto_definition_on_self() { 492 fn goto_definition_on_self() {
571 check_goto( 493 check(
572 " 494 r#"
573 //- /lib.rs 495struct Foo;
574 struct Foo; 496impl Foo {
575 impl Foo { 497 //^^^
576 pub fn new() -> Self { 498 pub fn new() -> Self {
577 Self<|> {} 499 Self<|> {}
578 } 500 }
579 } 501}
580 ", 502"#,
581 "impl IMPL_DEF FileId(1) 12..73", 503 );
582 "impl Foo {...}", 504 check(
583 ); 505 r#"
584 506struct Foo;
585 check_goto( 507impl Foo {
586 " 508 //^^^
587 //- /lib.rs 509 pub fn new() -> Self<|> {
588 struct Foo; 510 Self {}
589 impl Foo { 511 }
590 pub fn new() -> Self<|> { 512}
591 Self {} 513"#,
592 } 514 );
593 } 515
594 ", 516 check(
595 "impl IMPL_DEF FileId(1) 12..73", 517 r#"
596 "impl Foo {...}", 518enum Foo { A }
597 ); 519impl Foo {
598 520 //^^^
599 check_goto( 521 pub fn new() -> Self<|> {
600 " 522 Foo::A
601 //- /lib.rs 523 }
602 enum Foo { A } 524}
603 impl Foo { 525"#,
604 pub fn new() -> Self<|> { 526 );
605 Foo::A 527
606 } 528 check(
607 } 529 r#"
608 ", 530enum Foo { A }
609 "impl IMPL_DEF FileId(1) 15..75", 531impl Foo {
610 "impl Foo {...}", 532 //^^^
611 ); 533 pub fn thing(a: &Self<|>) {
612 534 }
613 check_goto( 535}
614 " 536"#,
615 //- /lib.rs
616 enum Foo { A }
617 impl Foo {
618 pub fn thing(a: &Self<|>) {
619 }
620 }
621 ",
622 "impl IMPL_DEF FileId(1) 15..62",
623 "impl Foo {...}",
624 ); 537 );
625 } 538 }
626 539
627 #[test] 540 #[test]
628 fn goto_definition_on_self_in_trait_impl() { 541 fn goto_definition_on_self_in_trait_impl() {
629 check_goto( 542 check(
630 " 543 r#"
631 //- /lib.rs 544struct Foo;
632 struct Foo; 545trait Make {
633 trait Make { 546 fn new() -> Self;
634 fn new() -> Self; 547}
635 } 548impl Make for Foo {
636 impl Make for Foo { 549 //^^^
637 fn new() -> Self { 550 fn new() -> Self {
638 Self<|> {} 551 Self<|> {}
639 } 552 }
640 } 553}
641 ", 554"#,
642 "impl IMPL_DEF FileId(1) 49..115",
643 "impl Make for Foo {...}",
644 ); 555 );
645 556
646 check_goto( 557 check(
647 " 558 r#"
648 //- /lib.rs 559struct Foo;
649 struct Foo; 560trait Make {
650 trait Make { 561 fn new() -> Self;
651 fn new() -> Self; 562}
652 } 563impl Make for Foo {
653 impl Make for Foo { 564 //^^^
654 fn new() -> Self<|> { 565 fn new() -> Self<|> {
655 Self {} 566 Self {}
656 } 567 }
657 } 568}
658 ", 569"#,
659 "impl IMPL_DEF FileId(1) 49..115",
660 "impl Make for Foo {...}",
661 ); 570 );
662 } 571 }
663 572
664 #[test] 573 #[test]
665 fn goto_def_when_used_on_definition_name_itself() { 574 fn goto_def_when_used_on_definition_name_itself() {
666 check_goto( 575 check(
667 " 576 r#"
668 //- /lib.rs 577struct Foo<|> { value: u32 }
669 struct Foo<|> { value: u32 } 578 //^^^
670 ", 579 "#,
671 "Foo STRUCT_DEF FileId(1) 0..25 7..10",
672 "struct Foo { value: u32 }|Foo",
673 ); 580 );
674 581
675 check_goto( 582 check(
676 r#" 583 r#"
677 //- /lib.rs 584struct Foo {
678 struct Foo { 585 field<|>: string,
679 field<|>: string, 586} //^^^^^
680 } 587"#,
681 "#,
682 "field RECORD_FIELD_DEF FileId(1) 17..30 17..22",
683 "field: string|field",
684 ); 588 );
685 589
686 check_goto( 590 check(
687 " 591 r#"
688 //- /lib.rs 592fn foo_test<|>() { }
689 fn foo_test<|>() { } 593 //^^^^^^^^
690 ", 594"#,
691 "foo_test FN_DEF FileId(1) 0..17 3..11",
692 "fn foo_test() { }|foo_test",
693 ); 595 );
694 596
695 check_goto( 597 check(
696 " 598 r#"
697 //- /lib.rs 599enum Foo<|> { Variant }
698 enum Foo<|> { 600 //^^^
699 Variant, 601"#,
700 }
701 ",
702 "Foo ENUM_DEF FileId(1) 0..25 5..8",
703 "enum Foo {...}|Foo",
704 );
705
706 check_goto(
707 "
708 //- /lib.rs
709 enum Foo {
710 Variant1,
711 Variant2<|>,
712 Variant3,
713 }
714 ",
715 "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37",
716 "Variant2|Variant2",
717 ); 602 );
718 603
719 check_goto( 604 check(
720 r#" 605 r#"
721 //- /lib.rs 606enum Foo {
722 static INNER<|>: &str = ""; 607 Variant1,
723 "#, 608 Variant2<|>,
724 "INNER STATIC_DEF FileId(1) 0..24 7..12", 609 //^^^^^^^^
725 "static INNER: &str = \"\";|INNER", 610 Variant3,
611}
612"#,
726 ); 613 );
727 614
728 check_goto( 615 check(
729 r#" 616 r#"
730 //- /lib.rs 617static INNER<|>: &str = "";
731 const INNER<|>: &str = ""; 618 //^^^^^
732 "#, 619"#,
733 "INNER CONST_DEF FileId(1) 0..23 6..11",
734 "const INNER: &str = \"\";|INNER",
735 ); 620 );
736 621
737 check_goto( 622 check(
738 r#" 623 r#"
739 //- /lib.rs 624const INNER<|>: &str = "";
740 type Thing<|> = Option<()>; 625 //^^^^^
741 "#, 626"#,
742 "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10",
743 "type Thing = Option<()>;|Thing",
744 ); 627 );
745 628
746 check_goto( 629 check(
747 r#" 630 r#"
748 //- /lib.rs 631type Thing<|> = Option<()>;
749 trait Foo<|> { } 632 //^^^^^
750 "#, 633"#,
751 "Foo TRAIT_DEF FileId(1) 0..13 6..9",
752 "trait Foo { }|Foo",
753 ); 634 );
754 635
755 check_goto( 636 check(
756 r#" 637 r#"
757 //- /lib.rs 638trait Foo<|> { }
758 mod bar<|> { } 639 //^^^
759 "#, 640"#,
760 "bar MODULE FileId(1) 0..11 4..7", 641 );
761 "mod bar { }|bar", 642
643 check(
644 r#"
645mod bar<|> { }
646 //^^^
647"#,
762 ); 648 );
763 } 649 }
764 650
765 #[test] 651 #[test]
766 fn goto_from_macro() { 652 fn goto_from_macro() {
767 check_goto( 653 check(
768 " 654 r#"
769 //- /lib.rs 655macro_rules! id {
770 macro_rules! id { 656 ($($tt:tt)*) => { $($tt)* }
771 ($($tt:tt)*) => { $($tt)* } 657}
772 } 658fn foo() {}
773 fn foo() {} 659 //^^^
774 id! { 660id! {
775 fn bar() { 661 fn bar() {
776 fo<|>o(); 662 fo<|>o();
777 } 663 }
778 } 664}
779 mod confuse_index { fn foo(); } 665mod confuse_index { fn foo(); }
780 ", 666"#,
781 "foo FN_DEF FileId(1) 52..63 55..58",
782 "fn foo() {}|foo",
783 ); 667 );
784 } 668 }
785 669
786 #[test] 670 #[test]
787 fn goto_through_format() { 671 fn goto_through_format() {
788 check_goto( 672 check(
789 " 673 r#"
790 //- /lib.rs 674#[macro_export]
791 #[macro_export] 675macro_rules! format {
792 macro_rules! format { 676 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
793 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) 677}
794 } 678#[rustc_builtin_macro]
795 #[rustc_builtin_macro] 679#[macro_export]
796 #[macro_export] 680macro_rules! format_args {
797 macro_rules! format_args { 681 ($fmt:expr) => ({ /* compiler built-in */ });
798 ($fmt:expr) => ({ /* compiler built-in */ }); 682 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
799 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) 683}
800 } 684pub mod __export {
801 pub mod __export { 685 pub use crate::format_args;
802 pub use crate::format_args; 686 fn foo() {} // for index confusion
803 fn foo() {} // for index confusion 687}
804 } 688fn foo() -> i8 {}
805 fn foo() -> i8 {} 689 //^^^
806 fn test() { 690fn test() {
807 format!(\"{}\", fo<|>o()) 691 format!("{}", fo<|>o())
808 } 692}
809 ", 693"#,
810 "foo FN_DEF FileId(1) 398..415 401..404",
811 "fn foo() -> i8 {}|foo",
812 ); 694 );
813 } 695 }
814 696
815 #[test] 697 #[test]
816 fn goto_for_type_param() { 698 fn goto_for_type_param() {
817 check_goto( 699 check(
818 r#" 700 r#"
819 //- /lib.rs 701struct Foo<T: Clone> { t: <|>T }
820 struct Foo<T: Clone> { 702 //^
821 t: <|>T, 703"#,
822 }
823 "#,
824 "T TYPE_PARAM FileId(1) 11..19 11..12",
825 "T: Clone|T",
826 ); 704 );
827 } 705 }
828 706
829 #[test] 707 #[test]
830 fn goto_within_macro() { 708 fn goto_within_macro() {
831 check_goto( 709 check(
832 r#" 710 r#"
833//- /lib.rs
834macro_rules! id { 711macro_rules! id {
835 ($($tt:tt)*) => ($($tt)*) 712 ($($tt:tt)*) => ($($tt)*)
836} 713}
837 714
838fn foo() { 715fn foo() {
839 let x = 1; 716 let x = 1;
717 //^
840 id!({ 718 id!({
841 let y = <|>x; 719 let y = <|>x;
842 let z = y; 720 let z = y;
843 }); 721 });
844} 722}
845"#, 723"#,
846 "x BIND_PAT FileId(1) 70..71",
847 "x",
848 ); 724 );
849 725
850 check_goto( 726 check(
851 r#" 727 r#"
852//- /lib.rs
853macro_rules! id { 728macro_rules! id {
854 ($($tt:tt)*) => ($($tt)*) 729 ($($tt:tt)*) => ($($tt)*)
855} 730}
@@ -858,159 +733,125 @@ fn foo() {
858 let x = 1; 733 let x = 1;
859 id!({ 734 id!({
860 let y = x; 735 let y = x;
736 //^
861 let z = <|>y; 737 let z = <|>y;
862 }); 738 });
863} 739}
864"#, 740"#,
865 "y BIND_PAT FileId(1) 99..100",
866 "y",
867 ); 741 );
868 } 742 }
869 743
870 #[test] 744 #[test]
871 fn goto_def_in_local_fn() { 745 fn goto_def_in_local_fn() {
872 check_goto( 746 check(
873 " 747 r#"
874 //- /lib.rs 748fn main() {
875 fn main() { 749 fn foo() {
876 fn foo() { 750 let x = 92;
877 let x = 92; 751 //^
878 <|>x; 752 <|>x;
879 } 753 }
880 } 754}
881 ", 755"#,
882 "x BIND_PAT FileId(1) 39..40",
883 "x",
884 ); 756 );
885 } 757 }
886 758
887 #[test] 759 #[test]
888 fn goto_def_in_local_macro() { 760 fn goto_def_in_local_macro() {
889 check_goto( 761 check(
890 r" 762 r#"
891 //- /lib.rs 763fn bar() {
892 fn bar() { 764 macro_rules! foo { () => { () } }
893 macro_rules! foo { () => { () } } 765 //^^^
894 <|>foo!(); 766 <|>foo!();
895 } 767}
896 ", 768"#,
897 "foo MACRO_CALL FileId(1) 15..48 28..31",
898 "macro_rules! foo { () => { () } }|foo",
899 ); 769 );
900 } 770 }
901 771
902 #[test] 772 #[test]
903 fn goto_def_for_field_init_shorthand() { 773 fn goto_def_for_field_init_shorthand() {
904 check_goto( 774 check(
905 " 775 r#"
906 //- /lib.rs 776struct Foo { x: i32 }
907 struct Foo { x: i32 } 777fn main() {
908 fn main() { 778 let x = 92;
909 let x = 92; 779 //^
910 Foo { x<|> }; 780 Foo { x<|> };
911 } 781}
912 ", 782"#,
913 "x BIND_PAT FileId(1) 42..43",
914 "x",
915 ) 783 )
916 } 784 }
917 785
918 #[test] 786 #[test]
919 fn goto_def_for_enum_variant_field() { 787 fn goto_def_for_enum_variant_field() {
920 check_goto( 788 check(
921 " 789 r#"
922 //- /lib.rs 790enum Foo {
923 enum Foo { 791 Bar { x: i32 }
924 Bar { x: i32 } 792} //^
925 } 793fn baz(foo: Foo) {
926 fn baz(foo: Foo) { 794 match foo {
927 match foo { 795 Foo::Bar { x<|> } => x
928 Foo::Bar { x<|> } => x 796 };
929 }; 797}
930 } 798"#,
931 ",
932 "x RECORD_FIELD_DEF FileId(1) 21..27 21..22",
933 "x: i32|x",
934 ); 799 );
935 } 800 }
936 801
937 #[test] 802 #[test]
938 fn goto_def_for_enum_variant_self_pattern_const() { 803 fn goto_def_for_enum_variant_self_pattern_const() {
939 check_goto( 804 check(
940 " 805 r#"
941 //- /lib.rs 806enum Foo { Bar }
942 enum Foo { 807 //^^^
943 Bar, 808impl Foo {
944 } 809 fn baz(self) {
945 impl Foo { 810 match self { Self::Bar<|> => {} }
946 fn baz(self) { 811 }
947 match self { 812}
948 Self::Bar<|> => {} 813"#,
949 }
950 }
951 }
952 ",
953 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
954 "Bar|Bar",
955 ); 814 );
956 } 815 }
957 816
958 #[test] 817 #[test]
959 fn goto_def_for_enum_variant_self_pattern_record() { 818 fn goto_def_for_enum_variant_self_pattern_record() {
960 check_goto( 819 check(
961 " 820 r#"
962 //- /lib.rs 821enum Foo { Bar { val: i32 } }
963 enum Foo { 822 //^^^
964 Bar { val: i32 }, 823impl Foo {
965 } 824 fn baz(self) -> i32 {
966 impl Foo { 825 match self { Self::Bar<|> { val } => {} }
967 fn baz(self) -> i32 { 826 }
968 match self { 827}
969 Self::Bar<|> { val } => {} 828"#,
970 }
971 }
972 }
973 ",
974 "Bar ENUM_VARIANT FileId(1) 15..31 15..18",
975 "Bar { val: i32 }|Bar",
976 ); 829 );
977 } 830 }
978 831
979 #[test] 832 #[test]
980 fn goto_def_for_enum_variant_self_expr_const() { 833 fn goto_def_for_enum_variant_self_expr_const() {
981 check_goto( 834 check(
982 " 835 r#"
983 //- /lib.rs 836enum Foo { Bar }
984 enum Foo { 837 //^^^
985 Bar, 838impl Foo {
986 } 839 fn baz(self) { Self::Bar<|>; }
987 impl Foo { 840}
988 fn baz(self) { 841"#,
989 Self::Bar<|>;
990 }
991 }
992 ",
993 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
994 "Bar|Bar",
995 ); 842 );
996 } 843 }
997 844
998 #[test] 845 #[test]
999 fn goto_def_for_enum_variant_self_expr_record() { 846 fn goto_def_for_enum_variant_self_expr_record() {
1000 check_goto( 847 check(
1001 " 848 r#"
1002 //- /lib.rs 849enum Foo { Bar { val: i32 } }
1003 enum Foo { 850 //^^^
1004 Bar { val: i32 }, 851impl Foo {
1005 } 852 fn baz(self) { Self::Bar<|> {val: 4}; }
1006 impl Foo { 853}
1007 fn baz(self) { 854"#,
1008 Self::Bar<|> {val: 4};
1009 }
1010 }
1011 ",
1012 "Bar ENUM_VARIANT FileId(1) 15..31 15..18",
1013 "Bar { val: i32 }|Bar",
1014 ); 855 );
1015 } 856 }
1016} 857}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 0cec0657e..99a7022a4 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -74,135 +74,152 @@ fn impls_for_trait(
74 74
75#[cfg(test)] 75#[cfg(test)]
76mod tests { 76mod tests {
77 use crate::mock_analysis::analysis_and_position; 77 use ra_db::FileRange;
78 78
79 fn check_goto(fixture: &str, expected: &[&str]) { 79 use crate::mock_analysis::MockAnalysis;
80 let (analysis, pos) = analysis_and_position(fixture);
81 80
82 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; 81 fn check(ra_fixture: &str) {
83 assert_eq!(navs.len(), expected.len()); 82 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
84 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); 83 let annotations = mock.annotations();
85 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); 84 let analysis = mock.analysis();
85
86 let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
87
88 let key = |frange: &FileRange| (frange.file_id, frange.range.start());
89
90 let mut expected = annotations
91 .into_iter()
92 .map(|(range, data)| {
93 assert!(data.is_empty());
94 range
95 })
96 .collect::<Vec<_>>();
97 expected.sort_by_key(key);
98
99 let mut actual = navs
100 .into_iter()
101 .map(|nav| FileRange { file_id: nav.file_id(), range: nav.range() })
102 .collect::<Vec<_>>();
103 actual.sort_by_key(key);
104
105 assert_eq!(expected, actual);
86 } 106 }
87 107
88 #[test] 108 #[test]
89 fn goto_implementation_works() { 109 fn goto_implementation_works() {
90 check_goto( 110 check(
91 " 111 r#"
92 //- /lib.rs 112struct Foo<|>;
93 struct Foo<|>; 113impl Foo {}
94 impl Foo {} 114 //^^^
95 ", 115"#,
96 &["impl IMPL_DEF FileId(1) 12..23"],
97 ); 116 );
98 } 117 }
99 118
100 #[test] 119 #[test]
101 fn goto_implementation_works_multiple_blocks() { 120 fn goto_implementation_works_multiple_blocks() {
102 check_goto( 121 check(
103 " 122 r#"
104 //- /lib.rs 123struct Foo<|>;
105 struct Foo<|>; 124impl Foo {}
106 impl Foo {} 125 //^^^
107 impl Foo {} 126impl Foo {}
108 ", 127 //^^^
109 &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], 128"#,
110 ); 129 );
111 } 130 }
112 131
113 #[test] 132 #[test]
114 fn goto_implementation_works_multiple_mods() { 133 fn goto_implementation_works_multiple_mods() {
115 check_goto( 134 check(
116 " 135 r#"
117 //- /lib.rs 136struct Foo<|>;
118 struct Foo<|>; 137mod a {
119 mod a { 138 impl super::Foo {}
120 impl super::Foo {} 139 //^^^^^^^^^^
121 } 140}
122 mod b { 141mod b {
123 impl super::Foo {} 142 impl super::Foo {}
124 } 143 //^^^^^^^^^^
125 ", 144}
126 &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], 145"#,
127 ); 146 );
128 } 147 }
129 148
130 #[test] 149 #[test]
131 fn goto_implementation_works_multiple_files() { 150 fn goto_implementation_works_multiple_files() {
132 check_goto( 151 check(
133 " 152 r#"
134 //- /lib.rs 153//- /lib.rs
135 struct Foo<|>; 154struct Foo<|>;
136 mod a; 155mod a;
137 mod b; 156mod b;
138 //- /a.rs 157//- /a.rs
139 impl crate::Foo {} 158impl crate::Foo {}
140 //- /b.rs 159 //^^^^^^^^^^
141 impl crate::Foo {} 160//- /b.rs
142 ", 161impl crate::Foo {}
143 &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], 162 //^^^^^^^^^^
163"#,
144 ); 164 );
145 } 165 }
146 166
147 #[test] 167 #[test]
148 fn goto_implementation_for_trait() { 168 fn goto_implementation_for_trait() {
149 check_goto( 169 check(
150 " 170 r#"
151 //- /lib.rs 171trait T<|> {}
152 trait T<|> {} 172struct Foo;
153 struct Foo; 173impl T for Foo {}
154 impl T for Foo {} 174 //^^^
155 ", 175"#,
156 &["impl IMPL_DEF FileId(1) 23..40"],
157 ); 176 );
158 } 177 }
159 178
160 #[test] 179 #[test]
161 fn goto_implementation_for_trait_multiple_files() { 180 fn goto_implementation_for_trait_multiple_files() {
162 check_goto( 181 check(
163 " 182 r#"
164 //- /lib.rs 183//- /lib.rs
165 trait T<|> {}; 184trait T<|> {};
166 struct Foo; 185struct Foo;
167 mod a; 186mod a;
168 mod b; 187mod b;
169 //- /a.rs 188//- /a.rs
170 impl crate::T for crate::Foo {} 189impl crate::T for crate::Foo {}
171 //- /b.rs 190 //^^^^^^^^^^
172 impl crate::T for crate::Foo {} 191//- /b.rs
173 ", 192impl crate::T for crate::Foo {}
174 &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], 193 //^^^^^^^^^^
194 "#,
175 ); 195 );
176 } 196 }
177 197
178 #[test] 198 #[test]
179 fn goto_implementation_all_impls() { 199 fn goto_implementation_all_impls() {
180 check_goto( 200 check(
181 " 201 r#"
182 //- /lib.rs 202//- /lib.rs
183 trait T {} 203trait T {}
184 struct Foo<|>; 204struct Foo<|>;
185 impl Foo {} 205impl Foo {}
186 impl T for Foo {} 206 //^^^
187 impl T for &Foo {} 207impl T for Foo {}
188 ", 208 //^^^
189 &[ 209impl T for &Foo {}
190 "impl IMPL_DEF FileId(1) 23..34", 210 //^^^^
191 "impl IMPL_DEF FileId(1) 35..52", 211"#,
192 "impl IMPL_DEF FileId(1) 53..71",
193 ],
194 ); 212 );
195 } 213 }
196 214
197 #[test] 215 #[test]
198 fn goto_implementation_to_builtin_derive() { 216 fn goto_implementation_to_builtin_derive() {
199 check_goto( 217 check(
200 " 218 r#"
201 //- /lib.rs 219 #[derive(Copy)]
202 #[derive(Copy)] 220//^^^^^^^^^^^^^^^
203 struct Foo<|>; 221struct Foo<|>;
204 ", 222"#,
205 &["impl IMPL_DEF FileId(1) 0..15"],
206 ); 223 );
207 } 224 }
208} 225}
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 889b84c59..db6d50694 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -3,7 +3,9 @@ use std::sync::Arc;
3 3
4use ra_cfg::CfgOptions; 4use ra_cfg::CfgOptions;
5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath}; 5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath};
6use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; 6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8};
7 9
8use crate::{ 10use crate::{
9 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
@@ -77,6 +79,24 @@ impl MockAnalysis {
77 .expect("no file in this mock"); 79 .expect("no file in this mock");
78 FileId(idx as u32 + 1) 80 FileId(idx as u32 + 1)
79 } 81 }
82 pub fn annotations(&self) -> Vec<(FileRange, String)> {
83 self.files
84 .iter()
85 .enumerate()
86 .flat_map(|(idx, fixture)| {
87 let file_id = FileId(idx as u32 + 1);
88 let annotations = extract_annotations(&fixture.text);
89 annotations
90 .into_iter()
91 .map(move |(range, data)| (FileRange { file_id, range }, data))
92 })
93 .collect()
94 }
95 pub fn annotation(&self) -> (FileRange, String) {
96 let mut all = self.annotations();
97 assert_eq!(all.len(), 1);
98 all.pop().unwrap()
99 }
80 pub fn analysis_host(self) -> AnalysisHost { 100 pub fn analysis_host(self) -> AnalysisHost {
81 let mut host = AnalysisHost::default(); 101 let mut host = AnalysisHost::default();
82 let mut change = AnalysisChange::new(); 102 let mut change = AnalysisChange::new();
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 0940fcc28..25bcd80af 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -330,11 +330,12 @@ pub(crate) fn handle_workspace_symbol(
330 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { 330 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
331 let mut res = Vec::new(); 331 let mut res = Vec::new();
332 for nav in snap.analysis.symbol_search(query)? { 332 for nav in snap.analysis.symbol_search(query)? {
333 let container_name = nav.container_name().map(|v| v.to_string());
333 let info = SymbolInformation { 334 let info = SymbolInformation {
334 name: nav.name().to_string(), 335 name: nav.name().to_string(),
335 kind: to_proto::symbol_kind(nav.kind()), 336 kind: to_proto::symbol_kind(nav.kind()),
336 location: to_proto::location(snap, nav.file_range())?, 337 location: to_proto::location_from_nav(snap, nav)?,
337 container_name: nav.container_name().map(|v| v.to_string()), 338 container_name,
338 deprecated: None, 339 deprecated: None,
339 }; 340 };
340 res.push(info); 341 res.push(info);
@@ -1213,8 +1214,8 @@ fn show_impl_command_link(
1213 let position = to_proto::position(&line_index, position.offset); 1214 let position = to_proto::position(&line_index, position.offset);
1214 let locations: Vec<_> = nav_data 1215 let locations: Vec<_> = nav_data
1215 .info 1216 .info
1216 .iter() 1217 .into_iter()
1217 .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) 1218 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1218 .collect(); 1219 .collect();
1219 let title = implementation_title(locations.len()); 1220 let title = implementation_title(locations.len());
1220 let command = show_references_command(title, &uri, position, locations); 1221 let command = show_references_command(title, &uri, position, locations);
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a03222ae9..a0a58f689 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -446,6 +446,18 @@ pub(crate) fn location(
446 Ok(loc) 446 Ok(loc)
447} 447}
448 448
449/// Perefer using `location_link`, if the client has the cap.
450pub(crate) fn location_from_nav(
451 snap: &GlobalStateSnapshot,
452 nav: NavigationTarget,
453) -> Result<lsp_types::Location> {
454 let url = url(snap, nav.file_id());
455 let line_index = snap.analysis.file_line_index(nav.file_id())?;
456 let range = range(&line_index, nav.full_range());
457 let loc = lsp_types::Location::new(url, range);
458 Ok(loc)
459}
460
449pub(crate) fn location_link( 461pub(crate) fn location_link(
450 snap: &GlobalStateSnapshot, 462 snap: &GlobalStateSnapshot,
451 src: Option<FileRange>, 463 src: Option<FileRange>,