aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs2
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs45
-rw-r--r--crates/ra_ide/src/goto_definition.rs1052
-rw-r--r--crates/ra_ide/src/goto_implementation.rs193
-rw-r--r--crates/ra_ide/src/inlay_hints.rs985
-rw-r--r--crates/ra_ide/src/join_lines.rs5
-rw-r--r--crates/ra_ide/src/mock_analysis.rs24
-rw-r--r--crates/ra_ide/src/runnables.rs1036
-rw-r--r--crates/ra_ide/src/ssr.rs2
9 files changed, 1404 insertions, 1940 deletions
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 560fb19e6..ef22ea54d 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -213,7 +213,7 @@ impl<'a> CompletionContext<'a> {
213 } 213 }
214 } 214 }
215 215
216 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { 216 pub(crate) fn scope(&self) -> SemanticsScope<'_> {
217 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
218 } 218 }
219 219
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 bea7fbfa7..4c78fa214 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -103,205 +103,149 @@ pub(crate) fn reference_definition(
103 103
104#[cfg(test)] 104#[cfg(test)]
105mod tests { 105mod tests {
106 use test_utils::assert_eq_text; 106 use ra_db::FileRange;
107 107 use ra_syntax::{TextRange, TextSize};
108 use crate::mock_analysis::analysis_and_position; 108
109 109 use crate::mock_analysis::MockAnalysis;
110 fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { 110
111 let (analysis, pos) = analysis_and_position(ra_fixture); 111 fn check(ra_fixture: &str) {
112 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
113 let (mut expected, data) = mock.annotation();
114 let analysis = mock.analysis();
115 match data.as_str() {
116 "" => (),
117 "file" => {
118 expected.range =
119 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
120 }
121 data => panic!("bad data: {}", data),
122 }
112 123
113 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 124 let mut navs = analysis.goto_definition(position).unwrap().unwrap().info;
114 if navs.len() == 0 { 125 if navs.len() == 0 {
115 panic!("unresolved reference") 126 panic!("unresolved reference")
116 } 127 }
117 assert_eq!(navs.len(), 1); 128 assert_eq!(navs.len(), 1);
118 129
119 let nav = navs.pop().unwrap(); 130 let nav = navs.pop().unwrap();
120 let file_text = analysis.file_text(nav.file_id()).unwrap(); 131 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
121
122 let mut actual = file_text[nav.full_range()].to_string();
123 if let Some(focus) = nav.focus_range() {
124 actual += "|";
125 actual += &file_text[focus];
126 }
127
128 if !expected_range.contains("...") {
129 test_utils::assert_eq_text!(&actual, expected_range);
130 } else {
131 let mut parts = expected_range.split("...");
132 let prefix = parts.next().unwrap();
133 let suffix = parts.next().unwrap();
134 assert!(
135 actual.starts_with(prefix) && actual.ends_with(suffix),
136 "\nExpected: {}\n Actual: {}\n",
137 expected_range,
138 actual
139 );
140 }
141
142 nav.assert_match(expected);
143 } 132 }
144 133
145 #[test] 134 #[test]
146 fn goto_def_in_items() { 135 fn goto_def_in_items() {
147 check_goto( 136 check(
148 " 137 r#"
149 //- /lib.rs 138struct Foo;
150 struct Foo; 139 //^^^
151 enum E { X(Foo<|>) } 140enum E { X(Foo<|>) }
152 ", 141"#,
153 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
154 "struct Foo;|Foo",
155 ); 142 );
156 } 143 }
157 144
158 #[test] 145 #[test]
159 fn goto_def_at_start_of_item() { 146 fn goto_def_at_start_of_item() {
160 check_goto( 147 check(
161 " 148 r#"
162 //- /lib.rs 149struct Foo;
163 struct Foo; 150 //^^^
164 enum E { X(<|>Foo) } 151enum E { X(<|>Foo) }
165 ", 152"#,
166 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
167 "struct Foo;|Foo",
168 ); 153 );
169 } 154 }
170 155
171 #[test] 156 #[test]
172 fn goto_definition_resolves_correct_name() { 157 fn goto_definition_resolves_correct_name() {
173 check_goto( 158 check(
174 " 159 r#"
175 //- /lib.rs 160//- /lib.rs
176 use a::Foo; 161use a::Foo;
177 mod a; 162mod a;
178 mod b; 163mod b;
179 enum E { X(Foo<|>) } 164enum E { X(Foo<|>) }
180 165
181 //- /a.rs 166//- /a.rs
182 struct Foo; 167struct Foo;
183 168 //^^^
184 //- /b.rs 169//- /b.rs
185 struct Foo; 170struct Foo;
186 ", 171"#,
187 "Foo STRUCT_DEF FileId(2) 0..11 7..10",
188 "struct Foo;|Foo",
189 ); 172 );
190 } 173 }
191 174
192 #[test] 175 #[test]
193 fn goto_def_for_module_declaration() { 176 fn goto_def_for_module_declaration() {
194 check_goto( 177 check(
195 r#" 178 r#"
196//- /lib.rs 179//- /lib.rs
197mod <|>foo; 180mod <|>foo;
198 181
199//- /foo.rs 182//- /foo.rs
200// empty 183// empty
184//^ file
201"#, 185"#,
202 "foo SOURCE_FILE FileId(2) 0..9",
203 "// empty\n",
204 ); 186 );
205 187
206 check_goto( 188 check(
207 r#" 189 r#"
208//- /lib.rs 190//- /lib.rs
209mod <|>foo; 191mod <|>foo;
210 192
211//- /foo/mod.rs 193//- /foo/mod.rs
212// empty 194// empty
195//^ file
213"#, 196"#,
214 "foo SOURCE_FILE FileId(2) 0..9",
215 "// empty\n",
216 ); 197 );
217 } 198 }
218 199
219 #[test] 200 #[test]
220 fn goto_def_for_macros() { 201 fn goto_def_for_macros() {
221 check_goto( 202 check(
222 " 203 r#"
223 //- /lib.rs 204macro_rules! foo { () => { () } }
224 macro_rules! foo { () => { () } } 205 //^^^
225 206fn bar() {
226 fn bar() { 207 <|>foo!();
227 <|>foo!(); 208}
228 } 209"#,
229 ",
230 "foo MACRO_CALL FileId(1) 0..33 13..16",
231 "macro_rules! foo { () => { () } }|foo",
232 ); 210 );
233 } 211 }
234 212
235 #[test] 213 #[test]
236 fn goto_def_for_macros_from_other_crates() { 214 fn goto_def_for_macros_from_other_crates() {
237 check_goto( 215 check(
238 "
239 //- /lib.rs
240 use foo::foo;
241 fn bar() {
242 <|>foo!();
243 }
244
245 //- /foo/lib.rs
246 #[macro_export]
247 macro_rules! foo { () => { () } }
248 ",
249 "foo MACRO_CALL FileId(2) 0..49 29..32",
250 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
251 );
252 }
253
254 #[test]
255 fn goto_def_for_use_alias() {
256 check_goto(
257 r#" 216 r#"
258//- /lib.rs 217//- /lib.rs
259use foo as bar<|>; 218use foo::foo;
219fn bar() {
220 <|>foo!();
221}
260 222
261//- /foo/lib.rs 223//- /foo/lib.rs
262#[macro_export] 224#[macro_export]
263macro_rules! foo { () => { () } } 225macro_rules! foo { () => { () } }
226 //^^^
264"#, 227"#,
265 "SOURCE_FILE FileId(2) 0..50",
266 "#[macro_export]\nmacro_rules! foo { () => { () } }\n",
267 );
268 }
269
270 #[test]
271 fn goto_def_for_use_alias_foo_macro() {
272 check_goto(
273 "
274 //- /lib.rs
275 use foo::foo as bar<|>;
276
277 //- /foo/lib.rs
278 #[macro_export]
279 macro_rules! foo { () => { () } }
280 ",
281 "foo MACRO_CALL FileId(2) 0..49 29..32",
282 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
283 ); 228 );
284 } 229 }
285 230
286 #[test] 231 #[test]
287 fn goto_def_for_macros_in_use_tree() { 232 fn goto_def_for_macros_in_use_tree() {
288 check_goto( 233 check(
289 " 234 r#"
290 //- /lib.rs 235//- /lib.rs
291 use foo::foo<|>; 236use foo::foo<|>;
292 237
293 //- /foo/lib.rs 238//- /foo/lib.rs
294 #[macro_export] 239#[macro_export]
295 macro_rules! foo { () => { () } } 240macro_rules! foo { () => { () } }
296 ", 241 //^^^
297 "foo MACRO_CALL FileId(2) 0..49 29..32", 242"#,
298 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
299 ); 243 );
300 } 244 }
301 245
302 #[test] 246 #[test]
303 fn goto_def_for_macro_defined_fn_with_arg() { 247 fn goto_def_for_macro_defined_fn_with_arg() {
304 check_goto( 248 check(
305 r#" 249 r#"
306//- /lib.rs 250//- /lib.rs
307macro_rules! define_fn { 251macro_rules! define_fn {
@@ -309,522 +253,478 @@ macro_rules! define_fn {
309} 253}
310 254
311define_fn!(foo); 255define_fn!(foo);
256 //^^^
312 257
313fn bar() { 258fn bar() {
314 <|>foo(); 259 <|>foo();
315} 260}
316"#, 261"#,
317 "foo FN_DEF FileId(1) 65..81 76..79",
318 "define_fn!(foo);|foo",
319 ); 262 );
320 } 263 }
321 264
322 #[test] 265 #[test]
323 fn goto_def_for_macro_defined_fn_no_arg() { 266 fn goto_def_for_macro_defined_fn_no_arg() {
324 check_goto( 267 check(
325 r#" 268 r#"
326//- /lib.rs 269//- /lib.rs
327macro_rules! define_fn { 270macro_rules! define_fn {
328 () => (fn foo() {}) 271 () => (fn foo() {})
329} 272}
330 273
331define_fn!(); 274 define_fn!();
275//^^^^^^^^^^^^^
332 276
333fn bar() { 277fn bar() {
334 <|>foo(); 278 <|>foo();
335} 279}
336"#, 280"#,
337 "foo FN_DEF FileId(1) 52..65 52..65",
338 "define_fn!();|define_fn!();",
339 ); 281 );
340 } 282 }
341 283
342 #[test] 284 #[test]
343 fn goto_definition_works_for_macro_inside_pattern() { 285 fn goto_definition_works_for_macro_inside_pattern() {
344 check_goto( 286 check(
345 " 287 r#"
346 //- /lib.rs 288//- /lib.rs
347 macro_rules! foo {() => {0}} 289macro_rules! foo {() => {0}}
348 290 //^^^
349 fn bar() { 291
350 match (0,1) { 292fn bar() {
351 (<|>foo!(), _) => {} 293 match (0,1) {
352 } 294 (<|>foo!(), _) => {}
353 } 295 }
354 ", 296}
355 "foo MACRO_CALL FileId(1) 0..28 13..16", 297"#,
356 "macro_rules! foo {() => {0}}|foo",
357 ); 298 );
358 } 299 }
359 300
360 #[test] 301 #[test]
361 fn goto_definition_works_for_macro_inside_match_arm_lhs() { 302 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
362 check_goto( 303 check(
363 " 304 r#"
364 //- /lib.rs 305//- /lib.rs
365 macro_rules! foo {() => {0}} 306macro_rules! foo {() => {0}}
366 307 //^^^
367 fn bar() { 308fn bar() {
368 match 0 { 309 match 0 {
369 <|>foo!() => {} 310 <|>foo!() => {}
370 } 311 }
371 } 312}
372 ", 313"#,
373 "foo MACRO_CALL FileId(1) 0..28 13..16", 314 );
374 "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"#,
375 ); 343 );
376 } 344 }
377 345
378 #[test] 346 #[test]
379 fn goto_def_for_methods() { 347 fn goto_def_for_methods() {
380 check_goto( 348 check(
381 " 349 r#"
382 //- /lib.rs 350//- /lib.rs
383 struct Foo; 351struct Foo;
384 impl Foo { 352impl Foo {
385 fn frobnicate(&self) { } 353 fn frobnicate(&self) { }
386 } 354 //^^^^^^^^^^
355}
387 356
388 fn bar(foo: &Foo) { 357fn bar(foo: &Foo) {
389 foo.frobnicate<|>(); 358 foo.frobnicate<|>();
390 } 359}
391 ", 360"#,
392 "frobnicate FN_DEF FileId(1) 27..51 30..40",
393 "fn frobnicate(&self) { }|frobnicate",
394 ); 361 );
395 } 362 }
396 363
397 #[test] 364 #[test]
398 fn goto_def_for_fields() { 365 fn goto_def_for_fields() {
399 check_goto( 366 check(
400 r" 367 r#"
401 //- /lib.rs 368struct Foo {
402 struct Foo { 369 spam: u32,
403 spam: u32, 370} //^^^^
404 }
405 371
406 fn bar(foo: &Foo) { 372fn bar(foo: &Foo) {
407 foo.spam<|>; 373 foo.spam<|>;
408 } 374}
409 ", 375"#,
410 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
411 "spam: u32|spam",
412 ); 376 );
413 } 377 }
414 378
415 #[test] 379 #[test]
416 fn goto_def_for_record_fields() { 380 fn goto_def_for_record_fields() {
417 check_goto( 381 check(
418 r" 382 r#"
419 //- /lib.rs 383//- /lib.rs
420 struct Foo { 384struct Foo {
421 spam: u32, 385 spam: u32,
422 } 386} //^^^^
423 387
424 fn bar() -> Foo { 388fn bar() -> Foo {
425 Foo { 389 Foo {
426 spam<|>: 0, 390 spam<|>: 0,
427 } 391 }
428 } 392}
429 ", 393"#,
430 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
431 "spam: u32|spam",
432 ); 394 );
433 } 395 }
434 396
435 #[test] 397 #[test]
436 fn goto_def_for_record_pat_fields() { 398 fn goto_def_for_record_pat_fields() {
437 check_goto( 399 check(
438 r" 400 r#"
439 //- /lib.rs 401//- /lib.rs
440 struct Foo { 402struct Foo {
441 spam: u32, 403 spam: u32,
442 } 404} //^^^^
443 405
444 fn bar(foo: Foo) -> Foo { 406fn bar(foo: Foo) -> Foo {
445 let Foo { spam<|>: _, } = foo 407 let Foo { spam<|>: _, } = foo
446 } 408}
447 ", 409"#,
448 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
449 "spam: u32|spam",
450 ); 410 );
451 } 411 }
452 412
453 #[test] 413 #[test]
454 fn goto_def_for_record_fields_macros() { 414 fn goto_def_for_record_fields_macros() {
455 check_goto( 415 check(
456 r" 416 r"
457 //- /lib.rs 417macro_rules! m { () => { 92 };}
458 macro_rules! m { () => { 92 };} 418struct Foo { spam: u32 }
459 struct Foo { spam: u32 } 419 //^^^^
460 420
461 fn bar() -> Foo { 421fn bar() -> Foo {
462 Foo { spam<|>: m!() } 422 Foo { spam<|>: m!() }
463 } 423}
464 ", 424",
465 "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49",
466 "spam: u32|spam",
467 ); 425 );
468 } 426 }
469 427
470 #[test] 428 #[test]
471 fn goto_for_tuple_fields() { 429 fn goto_for_tuple_fields() {
472 check_goto( 430 check(
473 " 431 r#"
474 //- /lib.rs 432struct Foo(u32);
475 struct Foo(u32); 433 //^^^
476 434
477 fn bar() { 435fn bar() {
478 let foo = Foo(0); 436 let foo = Foo(0);
479 foo.<|>0; 437 foo.<|>0;
480 } 438}
481 ", 439"#,
482 "TUPLE_FIELD_DEF FileId(1) 11..14",
483 "u32",
484 ); 440 );
485 } 441 }
486 442
487 #[test] 443 #[test]
488 fn goto_def_for_ufcs_inherent_methods() { 444 fn goto_def_for_ufcs_inherent_methods() {
489 check_goto( 445 check(
490 " 446 r#"
491 //- /lib.rs 447struct Foo;
492 struct Foo; 448impl Foo {
493 impl Foo { 449 fn frobnicate() { }
494 fn frobnicate() { } 450} //^^^^^^^^^^
495 }
496 451
497 fn bar(foo: &Foo) { 452fn bar(foo: &Foo) {
498 Foo::frobnicate<|>(); 453 Foo::frobnicate<|>();
499 } 454}
500 ", 455"#,
501 "frobnicate FN_DEF FileId(1) 27..46 30..40",
502 "fn frobnicate() { }|frobnicate",
503 ); 456 );
504 } 457 }
505 458
506 #[test] 459 #[test]
507 fn goto_def_for_ufcs_trait_methods_through_traits() { 460 fn goto_def_for_ufcs_trait_methods_through_traits() {
508 check_goto( 461 check(
509 " 462 r#"
510 //- /lib.rs 463trait Foo {
511 trait Foo { 464 fn frobnicate();
512 fn frobnicate(); 465} //^^^^^^^^^^
513 }
514 466
515 fn bar() { 467fn bar() {
516 Foo::frobnicate<|>(); 468 Foo::frobnicate<|>();
517 } 469}
518 ", 470"#,
519 "frobnicate FN_DEF FileId(1) 16..32 19..29",
520 "fn frobnicate();|frobnicate",
521 ); 471 );
522 } 472 }
523 473
524 #[test] 474 #[test]
525 fn goto_def_for_ufcs_trait_methods_through_self() { 475 fn goto_def_for_ufcs_trait_methods_through_self() {
526 check_goto( 476 check(
527 " 477 r#"
528 //- /lib.rs 478struct Foo;
529 struct Foo; 479trait Trait {
530 trait Trait { 480 fn frobnicate();
531 fn frobnicate(); 481} //^^^^^^^^^^
532 } 482impl Trait for Foo {}
533 impl Trait for Foo {}
534 483
535 fn bar() { 484fn bar() {
536 Foo::frobnicate<|>(); 485 Foo::frobnicate<|>();
537 } 486}
538 ", 487"#,
539 "frobnicate FN_DEF FileId(1) 30..46 33..43",
540 "fn frobnicate();|frobnicate",
541 ); 488 );
542 } 489 }
543 490
544 #[test] 491 #[test]
545 fn goto_definition_on_self() { 492 fn goto_definition_on_self() {
546 check_goto( 493 check(
547 " 494 r#"
548 //- /lib.rs 495struct Foo;
549 struct Foo; 496impl Foo {
550 impl Foo { 497 //^^^
551 pub fn new() -> Self { 498 pub fn new() -> Self {
552 Self<|> {} 499 Self<|> {}
553 } 500 }
554 } 501}
555 ", 502"#,
556 "impl IMPL_DEF FileId(1) 12..73", 503 );
557 "impl Foo {...}", 504 check(
558 ); 505 r#"
559 506struct Foo;
560 check_goto( 507impl Foo {
561 " 508 //^^^
562 //- /lib.rs 509 pub fn new() -> Self<|> {
563 struct Foo; 510 Self {}
564 impl Foo { 511 }
565 pub fn new() -> Self<|> { 512}
566 Self {} 513"#,
567 } 514 );
568 } 515
569 ", 516 check(
570 "impl IMPL_DEF FileId(1) 12..73", 517 r#"
571 "impl Foo {...}", 518enum Foo { A }
572 ); 519impl Foo {
573 520 //^^^
574 check_goto( 521 pub fn new() -> Self<|> {
575 " 522 Foo::A
576 //- /lib.rs 523 }
577 enum Foo { A } 524}
578 impl Foo { 525"#,
579 pub fn new() -> Self<|> { 526 );
580 Foo::A 527
581 } 528 check(
582 } 529 r#"
583 ", 530enum Foo { A }
584 "impl IMPL_DEF FileId(1) 15..75", 531impl Foo {
585 "impl Foo {...}", 532 //^^^
586 ); 533 pub fn thing(a: &Self<|>) {
587 534 }
588 check_goto( 535}
589 " 536"#,
590 //- /lib.rs
591 enum Foo { A }
592 impl Foo {
593 pub fn thing(a: &Self<|>) {
594 }
595 }
596 ",
597 "impl IMPL_DEF FileId(1) 15..62",
598 "impl Foo {...}",
599 ); 537 );
600 } 538 }
601 539
602 #[test] 540 #[test]
603 fn goto_definition_on_self_in_trait_impl() { 541 fn goto_definition_on_self_in_trait_impl() {
604 check_goto( 542 check(
605 " 543 r#"
606 //- /lib.rs 544struct Foo;
607 struct Foo; 545trait Make {
608 trait Make { 546 fn new() -> Self;
609 fn new() -> Self; 547}
610 } 548impl Make for Foo {
611 impl Make for Foo { 549 //^^^
612 fn new() -> Self { 550 fn new() -> Self {
613 Self<|> {} 551 Self<|> {}
614 } 552 }
615 } 553}
616 ", 554"#,
617 "impl IMPL_DEF FileId(1) 49..115",
618 "impl Make for Foo {...}",
619 ); 555 );
620 556
621 check_goto( 557 check(
622 " 558 r#"
623 //- /lib.rs 559struct Foo;
624 struct Foo; 560trait Make {
625 trait Make { 561 fn new() -> Self;
626 fn new() -> Self; 562}
627 } 563impl Make for Foo {
628 impl Make for Foo { 564 //^^^
629 fn new() -> Self<|> { 565 fn new() -> Self<|> {
630 Self {} 566 Self {}
631 } 567 }
632 } 568}
633 ", 569"#,
634 "impl IMPL_DEF FileId(1) 49..115",
635 "impl Make for Foo {...}",
636 ); 570 );
637 } 571 }
638 572
639 #[test] 573 #[test]
640 fn goto_def_when_used_on_definition_name_itself() { 574 fn goto_def_when_used_on_definition_name_itself() {
641 check_goto( 575 check(
642 " 576 r#"
643 //- /lib.rs 577struct Foo<|> { value: u32 }
644 struct Foo<|> { value: u32 } 578 //^^^
645 ", 579 "#,
646 "Foo STRUCT_DEF FileId(1) 0..25 7..10",
647 "struct Foo { value: u32 }|Foo",
648 ); 580 );
649 581
650 check_goto( 582 check(
651 r#" 583 r#"
652 //- /lib.rs 584struct Foo {
653 struct Foo { 585 field<|>: string,
654 field<|>: string, 586} //^^^^^
655 } 587"#,
656 "#,
657 "field RECORD_FIELD_DEF FileId(1) 17..30 17..22",
658 "field: string|field",
659 ); 588 );
660 589
661 check_goto( 590 check(
662 " 591 r#"
663 //- /lib.rs 592fn foo_test<|>() { }
664 fn foo_test<|>() { } 593 //^^^^^^^^
665 ", 594"#,
666 "foo_test FN_DEF FileId(1) 0..17 3..11",
667 "fn foo_test() { }|foo_test",
668 ); 595 );
669 596
670 check_goto( 597 check(
671 " 598 r#"
672 //- /lib.rs 599enum Foo<|> { Variant }
673 enum Foo<|> { 600 //^^^
674 Variant, 601"#,
675 }
676 ",
677 "Foo ENUM_DEF FileId(1) 0..25 5..8",
678 "enum Foo {...}|Foo",
679 );
680
681 check_goto(
682 "
683 //- /lib.rs
684 enum Foo {
685 Variant1,
686 Variant2<|>,
687 Variant3,
688 }
689 ",
690 "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37",
691 "Variant2|Variant2",
692 ); 602 );
693 603
694 check_goto( 604 check(
695 r#" 605 r#"
696 //- /lib.rs 606enum Foo {
697 static INNER<|>: &str = ""; 607 Variant1,
698 "#, 608 Variant2<|>,
699 "INNER STATIC_DEF FileId(1) 0..24 7..12", 609 //^^^^^^^^
700 "static INNER: &str = \"\";|INNER", 610 Variant3,
611}
612"#,
701 ); 613 );
702 614
703 check_goto( 615 check(
704 r#" 616 r#"
705 //- /lib.rs 617static INNER<|>: &str = "";
706 const INNER<|>: &str = ""; 618 //^^^^^
707 "#, 619"#,
708 "INNER CONST_DEF FileId(1) 0..23 6..11",
709 "const INNER: &str = \"\";|INNER",
710 ); 620 );
711 621
712 check_goto( 622 check(
713 r#" 623 r#"
714 //- /lib.rs 624const INNER<|>: &str = "";
715 type Thing<|> = Option<()>; 625 //^^^^^
716 "#, 626"#,
717 "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10",
718 "type Thing = Option<()>;|Thing",
719 ); 627 );
720 628
721 check_goto( 629 check(
722 r#" 630 r#"
723 //- /lib.rs 631type Thing<|> = Option<()>;
724 trait Foo<|> { } 632 //^^^^^
725 "#, 633"#,
726 "Foo TRAIT_DEF FileId(1) 0..13 6..9",
727 "trait Foo { }|Foo",
728 ); 634 );
729 635
730 check_goto( 636 check(
731 r#" 637 r#"
732 //- /lib.rs 638trait Foo<|> { }
733 mod bar<|> { } 639 //^^^
734 "#, 640"#,
735 "bar MODULE FileId(1) 0..11 4..7", 641 );
736 "mod bar { }|bar", 642
643 check(
644 r#"
645mod bar<|> { }
646 //^^^
647"#,
737 ); 648 );
738 } 649 }
739 650
740 #[test] 651 #[test]
741 fn goto_from_macro() { 652 fn goto_from_macro() {
742 check_goto( 653 check(
743 " 654 r#"
744 //- /lib.rs 655macro_rules! id {
745 macro_rules! id { 656 ($($tt:tt)*) => { $($tt)* }
746 ($($tt:tt)*) => { $($tt)* } 657}
747 } 658fn foo() {}
748 fn foo() {} 659 //^^^
749 id! { 660id! {
750 fn bar() { 661 fn bar() {
751 fo<|>o(); 662 fo<|>o();
752 } 663 }
753 } 664}
754 mod confuse_index { fn foo(); } 665mod confuse_index { fn foo(); }
755 ", 666"#,
756 "foo FN_DEF FileId(1) 52..63 55..58",
757 "fn foo() {}|foo",
758 ); 667 );
759 } 668 }
760 669
761 #[test] 670 #[test]
762 fn goto_through_format() { 671 fn goto_through_format() {
763 check_goto( 672 check(
764 " 673 r#"
765 //- /lib.rs 674#[macro_export]
766 #[macro_export] 675macro_rules! format {
767 macro_rules! format { 676 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
768 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) 677}
769 } 678#[rustc_builtin_macro]
770 #[rustc_builtin_macro] 679#[macro_export]
771 #[macro_export] 680macro_rules! format_args {
772 macro_rules! format_args { 681 ($fmt:expr) => ({ /* compiler built-in */ });
773 ($fmt:expr) => ({ /* compiler built-in */ }); 682 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
774 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) 683}
775 } 684pub mod __export {
776 pub mod __export { 685 pub use crate::format_args;
777 pub use crate::format_args; 686 fn foo() {} // for index confusion
778 fn foo() {} // for index confusion 687}
779 } 688fn foo() -> i8 {}
780 fn foo() -> i8 {} 689 //^^^
781 fn test() { 690fn test() {
782 format!(\"{}\", fo<|>o()) 691 format!("{}", fo<|>o())
783 } 692}
784 ", 693"#,
785 "foo FN_DEF FileId(1) 398..415 401..404",
786 "fn foo() -> i8 {}|foo",
787 ); 694 );
788 } 695 }
789 696
790 #[test] 697 #[test]
791 fn goto_for_type_param() { 698 fn goto_for_type_param() {
792 check_goto( 699 check(
793 r#" 700 r#"
794 //- /lib.rs 701struct Foo<T: Clone> { t: <|>T }
795 struct Foo<T: Clone> { 702 //^
796 t: <|>T, 703"#,
797 }
798 "#,
799 "T TYPE_PARAM FileId(1) 11..19 11..12",
800 "T: Clone|T",
801 ); 704 );
802 } 705 }
803 706
804 #[test] 707 #[test]
805 fn goto_within_macro() { 708 fn goto_within_macro() {
806 check_goto( 709 check(
807 r#" 710 r#"
808//- /lib.rs
809macro_rules! id { 711macro_rules! id {
810 ($($tt:tt)*) => ($($tt)*) 712 ($($tt:tt)*) => ($($tt)*)
811} 713}
812 714
813fn foo() { 715fn foo() {
814 let x = 1; 716 let x = 1;
717 //^
815 id!({ 718 id!({
816 let y = <|>x; 719 let y = <|>x;
817 let z = y; 720 let z = y;
818 }); 721 });
819} 722}
820"#, 723"#,
821 "x BIND_PAT FileId(1) 70..71",
822 "x",
823 ); 724 );
824 725
825 check_goto( 726 check(
826 r#" 727 r#"
827//- /lib.rs
828macro_rules! id { 728macro_rules! id {
829 ($($tt:tt)*) => ($($tt)*) 729 ($($tt:tt)*) => ($($tt)*)
830} 730}
@@ -833,159 +733,125 @@ fn foo() {
833 let x = 1; 733 let x = 1;
834 id!({ 734 id!({
835 let y = x; 735 let y = x;
736 //^
836 let z = <|>y; 737 let z = <|>y;
837 }); 738 });
838} 739}
839"#, 740"#,
840 "y BIND_PAT FileId(1) 99..100",
841 "y",
842 ); 741 );
843 } 742 }
844 743
845 #[test] 744 #[test]
846 fn goto_def_in_local_fn() { 745 fn goto_def_in_local_fn() {
847 check_goto( 746 check(
848 " 747 r#"
849 //- /lib.rs 748fn main() {
850 fn main() { 749 fn foo() {
851 fn foo() { 750 let x = 92;
852 let x = 92; 751 //^
853 <|>x; 752 <|>x;
854 } 753 }
855 } 754}
856 ", 755"#,
857 "x BIND_PAT FileId(1) 39..40",
858 "x",
859 ); 756 );
860 } 757 }
861 758
862 #[test] 759 #[test]
863 fn goto_def_in_local_macro() { 760 fn goto_def_in_local_macro() {
864 check_goto( 761 check(
865 r" 762 r#"
866 //- /lib.rs 763fn bar() {
867 fn bar() { 764 macro_rules! foo { () => { () } }
868 macro_rules! foo { () => { () } } 765 //^^^
869 <|>foo!(); 766 <|>foo!();
870 } 767}
871 ", 768"#,
872 "foo MACRO_CALL FileId(1) 15..48 28..31",
873 "macro_rules! foo { () => { () } }|foo",
874 ); 769 );
875 } 770 }
876 771
877 #[test] 772 #[test]
878 fn goto_def_for_field_init_shorthand() { 773 fn goto_def_for_field_init_shorthand() {
879 check_goto( 774 check(
880 " 775 r#"
881 //- /lib.rs 776struct Foo { x: i32 }
882 struct Foo { x: i32 } 777fn main() {
883 fn main() { 778 let x = 92;
884 let x = 92; 779 //^
885 Foo { x<|> }; 780 Foo { x<|> };
886 } 781}
887 ", 782"#,
888 "x BIND_PAT FileId(1) 42..43",
889 "x",
890 ) 783 )
891 } 784 }
892 785
893 #[test] 786 #[test]
894 fn goto_def_for_enum_variant_field() { 787 fn goto_def_for_enum_variant_field() {
895 check_goto( 788 check(
896 " 789 r#"
897 //- /lib.rs 790enum Foo {
898 enum Foo { 791 Bar { x: i32 }
899 Bar { x: i32 } 792} //^
900 } 793fn baz(foo: Foo) {
901 fn baz(foo: Foo) { 794 match foo {
902 match foo { 795 Foo::Bar { x<|> } => x
903 Foo::Bar { x<|> } => x 796 };
904 }; 797}
905 } 798"#,
906 ",
907 "x RECORD_FIELD_DEF FileId(1) 21..27 21..22",
908 "x: i32|x",
909 ); 799 );
910 } 800 }
911 801
912 #[test] 802 #[test]
913 fn goto_def_for_enum_variant_self_pattern_const() { 803 fn goto_def_for_enum_variant_self_pattern_const() {
914 check_goto( 804 check(
915 " 805 r#"
916 //- /lib.rs 806enum Foo { Bar }
917 enum Foo { 807 //^^^
918 Bar, 808impl Foo {
919 } 809 fn baz(self) {
920 impl Foo { 810 match self { Self::Bar<|> => {} }
921 fn baz(self) { 811 }
922 match self { 812}
923 Self::Bar<|> => {} 813"#,
924 }
925 }
926 }
927 ",
928 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
929 "Bar|Bar",
930 ); 814 );
931 } 815 }
932 816
933 #[test] 817 #[test]
934 fn goto_def_for_enum_variant_self_pattern_record() { 818 fn goto_def_for_enum_variant_self_pattern_record() {
935 check_goto( 819 check(
936 " 820 r#"
937 //- /lib.rs 821enum Foo { Bar { val: i32 } }
938 enum Foo { 822 //^^^
939 Bar { val: i32 }, 823impl Foo {
940 } 824 fn baz(self) -> i32 {
941 impl Foo { 825 match self { Self::Bar<|> { val } => {} }
942 fn baz(self) -> i32 { 826 }
943 match self { 827}
944 Self::Bar<|> { val } => {} 828"#,
945 }
946 }
947 }
948 ",
949 "Bar ENUM_VARIANT FileId(1) 15..31 15..18",
950 "Bar { val: i32 }|Bar",
951 ); 829 );
952 } 830 }
953 831
954 #[test] 832 #[test]
955 fn goto_def_for_enum_variant_self_expr_const() { 833 fn goto_def_for_enum_variant_self_expr_const() {
956 check_goto( 834 check(
957 " 835 r#"
958 //- /lib.rs 836enum Foo { Bar }
959 enum Foo { 837 //^^^
960 Bar, 838impl Foo {
961 } 839 fn baz(self) { Self::Bar<|>; }
962 impl Foo { 840}
963 fn baz(self) { 841"#,
964 Self::Bar<|>;
965 }
966 }
967 ",
968 "Bar ENUM_VARIANT FileId(1) 15..18 15..18",
969 "Bar|Bar",
970 ); 842 );
971 } 843 }
972 844
973 #[test] 845 #[test]
974 fn goto_def_for_enum_variant_self_expr_record() { 846 fn goto_def_for_enum_variant_self_expr_record() {
975 check_goto( 847 check(
976 " 848 r#"
977 //- /lib.rs 849enum Foo { Bar { val: i32 } }
978 enum Foo { 850 //^^^
979 Bar { val: i32 }, 851impl Foo {
980 } 852 fn baz(self) { Self::Bar<|> {val: 4}; }
981 impl Foo { 853}
982 fn baz(self) { 854"#,
983 Self::Bar<|> {val: 4};
984 }
985 }
986 ",
987 "Bar ENUM_VARIANT FileId(1) 15..31 15..18",
988 "Bar { val: i32 }|Bar",
989 ); 855 );
990 } 856 }
991} 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/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 3fd08b1e8..62d364bfa 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{FileId, FunctionSignature}; 9use crate::{FileId, FunctionSignature};
@@ -112,7 +112,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 112 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 113 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 114 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 115 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 116 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 117 if ty.is_unknown() {
118 return None; 118 return None;
@@ -258,6 +258,7 @@ fn should_show_param_name_hint(
258 if param_name.is_empty() 258 if param_name.is_empty()
259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
260 || is_argument_similar_to_param_name(sema, argument, param_name) 260 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture")
261 { 262 {
262 return false; 263 return false;
263 } 264 }
@@ -270,7 +271,7 @@ fn should_show_param_name_hint(
270 271
271 // avoid displaying hints for common functions like map, filter, etc. 272 // avoid displaying hints for common functions like map, filter, etc.
272 // or other obvious words used in std 273 // or other obvious words used in std
273 parameters_len != 1 || !is_obvious_param(param_name) 274 !(parameters_len == 1 && is_obvious_param(param_name))
274} 275}
275 276
276fn is_argument_similar_to_param_name( 277fn is_argument_similar_to_param_name(
@@ -312,10 +313,8 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
312} 313}
313 314
314fn is_obvious_param(param_name: &str) -> bool { 315fn is_obvious_param(param_name: &str) -> bool {
315 let is_obvious_param_name = match param_name { 316 let is_obvious_param_name =
316 "predicate" | "value" | "pat" | "rhs" | "other" => true, 317 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
317 _ => false,
318 };
319 param_name.len() == 1 || is_obvious_param_name 318 param_name.len() == 1 || is_obvious_param_name
320} 319}
321 320
@@ -346,583 +345,251 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
346 345
347#[cfg(test)] 346#[cfg(test)]
348mod tests { 347mod tests {
349 use crate::inlay_hints::InlayHintsConfig; 348 use expect::{expect, Expect};
350 use insta::assert_debug_snapshot; 349 use test_utils::extract_annotations;
350
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
351 352
352 use crate::mock_analysis::single_file; 353 fn check(ra_fixture: &str) {
354 check_with_config(ra_fixture, InlayHintsConfig::default());
355 }
356
357 fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual);
364 }
365
366 fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints)
370 }
353 371
354 #[test] 372 #[test]
355 fn param_hints_only() { 373 fn param_hints_only() {
356 let (analysis, file_id) = single_file( 374 check_with_config(
357 r#" 375 r#"
358 fn foo(a: i32, b: i32) -> i32 { a + b } 376fn foo(a: i32, b: i32) -> i32 { a + b }
359 fn main() { 377fn main() {
360 let _x = foo(4, 4); 378 let _x = foo(
361 }"#, 379 4,
362 ); 380 //^ a
363 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 381 4,
364 [ 382 //^ b
365 InlayHint { 383 );
366 range: 69..70, 384}"#,
367 kind: ParameterHint, 385 InlayHintsConfig {
368 label: "a", 386 parameter_hints: true,
369 }, 387 type_hints: false,
370 InlayHint { 388 chaining_hints: false,
371 range: 72..73, 389 max_length: None,
372 kind: ParameterHint,
373 label: "b",
374 }, 390 },
375 ] 391 );
376 "###);
377 } 392 }
378 393
379 #[test] 394 #[test]
380 fn hints_disabled() { 395 fn hints_disabled() {
381 let (analysis, file_id) = single_file( 396 check_with_config(
382 r#" 397 r#"
383 fn foo(a: i32, b: i32) -> i32 { a + b } 398fn foo(a: i32, b: i32) -> i32 { a + b }
384 fn main() { 399fn main() {
385 let _x = foo(4, 4); 400 let _x = foo(4, 4);
386 }"#, 401}"#,
402 InlayHintsConfig {
403 type_hints: false,
404 parameter_hints: false,
405 chaining_hints: false,
406 max_length: None,
407 },
387 ); 408 );
388 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
389 } 409 }
390 410
391 #[test] 411 #[test]
392 fn type_hints_only() { 412 fn type_hints_only() {
393 let (analysis, file_id) = single_file( 413 check_with_config(
394 r#" 414 r#"
395 fn foo(a: i32, b: i32) -> i32 { a + b } 415fn foo(a: i32, b: i32) -> i32 { a + b }
396 fn main() { 416fn main() {
397 let _x = foo(4, 4); 417 let _x = foo(4, 4);
398 }"#, 418 //^^ i32
399 ); 419}"#,
400 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 420 InlayHintsConfig {
401 [ 421 type_hints: true,
402 InlayHint { 422 parameter_hints: false,
403 range: 60..62, 423 chaining_hints: false,
404 kind: TypeHint, 424 max_length: None,
405 label: "i32",
406 }, 425 },
407 ] 426 );
408 "###);
409 } 427 }
428
410 #[test] 429 #[test]
411 fn default_generic_types_should_not_be_displayed() { 430 fn default_generic_types_should_not_be_displayed() {
412 let (analysis, file_id) = single_file( 431 check(
413 r#" 432 r#"
414struct Test<K, T = u8> { 433struct Test<K, T = u8> { k: K, t: T }
415 k: K,
416 t: T,
417}
418 434
419fn main() { 435fn main() {
420 let zz = Test { t: 23u8, k: 33 }; 436 let zz = Test { t: 23u8, k: 33 };
437 //^^ Test<i32>
421 let zz_ref = &zz; 438 let zz_ref = &zz;
439 //^^^^^^ &Test<i32>
422}"#, 440}"#,
423 ); 441 );
424
425 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
426 [
427 InlayHint {
428 range: 68..70,
429 kind: TypeHint,
430 label: "Test<i32>",
431 },
432 InlayHint {
433 range: 106..112,
434 kind: TypeHint,
435 label: "&Test<i32>",
436 },
437 ]
438 "###
439 );
440 } 442 }
441 443
442 #[test] 444 #[test]
443 fn let_statement() { 445 fn let_statement() {
444 let (analysis, file_id) = single_file( 446 check(
445 r#" 447 r#"
446#[derive(PartialEq)] 448#[derive(PartialEq)]
447enum CustomOption<T> { 449enum Option<T> { None, Some(T) }
448 None,
449 Some(T),
450}
451 450
452#[derive(PartialEq)] 451#[derive(PartialEq)]
453struct Test { 452struct Test { a: Option<u32>, b: u8 }
454 a: CustomOption<u32>,
455 b: u8,
456}
457 453
458fn main() { 454fn main() {
459 struct InnerStruct {} 455 struct InnerStruct {}
460 456
461 let test = 54; 457 let test = 54;
458 //^^^^ i32
462 let test: i32 = 33; 459 let test: i32 = 33;
463 let mut test = 33; 460 let mut test = 33;
461 //^^^^^^^^ i32
464 let _ = 22; 462 let _ = 22;
465 let test = "test"; 463 let test = "test";
464 //^^^^ &str
466 let test = InnerStruct {}; 465 let test = InnerStruct {};
467 466
468 let test = vec![222]; 467 let test = unresolved();
469 let test: Vec<_> = (0..3).collect();
470 let test = (0..3).collect::<Vec<i128>>();
471 let test = (0..3).collect::<Vec<_>>();
472
473 let mut test = Vec::new();
474 test.push(333);
475 468
476 let test = (42, 'a'); 469 let test = (42, 'a');
477 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 470 //^^^^ (i32, char)
471 let (a, (b, (c,)) = (2, (3, (9.2,));
472 //^ i32 ^ i32 ^ f64
478 let &x = &92; 473 let &x = &92;
474 //^ i32
479}"#, 475}"#,
480 ); 476 );
481
482 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
483 [
484 InlayHint {
485 range: 192..196,
486 kind: TypeHint,
487 label: "i32",
488 },
489 InlayHint {
490 range: 235..243,
491 kind: TypeHint,
492 label: "i32",
493 },
494 InlayHint {
495 range: 274..278,
496 kind: TypeHint,
497 label: "&str",
498 },
499 InlayHint {
500 range: 538..542,
501 kind: TypeHint,
502 label: "(i32, char)",
503 },
504 InlayHint {
505 range: 565..566,
506 kind: TypeHint,
507 label: "i32",
508 },
509 InlayHint {
510 range: 569..570,
511 kind: TypeHint,
512 label: "i32",
513 },
514 InlayHint {
515 range: 572..573,
516 kind: TypeHint,
517 label: "i32",
518 },
519 InlayHint {
520 range: 576..577,
521 kind: TypeHint,
522 label: "f64",
523 },
524 InlayHint {
525 range: 579..580,
526 kind: TypeHint,
527 label: "f64",
528 },
529 InlayHint {
530 range: 583..584,
531 kind: TypeHint,
532 label: "i32",
533 },
534 InlayHint {
535 range: 626..627,
536 kind: TypeHint,
537 label: "i32",
538 },
539 ]
540 "###
541 );
542 } 477 }
543 478
544 #[test] 479 #[test]
545 fn closure_parameters() { 480 fn closure_parameters() {
546 let (analysis, file_id) = single_file( 481 check(
547 r#" 482 r#"
548fn main() { 483fn main() {
549 let mut start = 0; 484 let mut start = 0;
550 (0..2).for_each(|increment| { 485 //^^^^^^^^^ i32
551 start += increment; 486 (0..2).for_each(|increment| { start += increment; });
552 }); 487 //^^^^^^^^^ i32
488
489 let multiply =
490 //^^^^^^^^ |…| -> i32
491 | a, b| a * b
492 //^ i32 ^ i32
493 ;
553 494
554 let multiply = |a, b, c, d| a * b * c * d; 495 let _: i32 = multiply(1, 2);
555 let _: i32 = multiply(1, 2, 3, 4);
556 let multiply_ref = &multiply; 496 let multiply_ref = &multiply;
497 //^^^^^^^^^^^^ &|…| -> i32
557 498
558 let return_42 = || 42; 499 let return_42 = || 42;
500 //^^^^^^^^^ || -> i32
559}"#, 501}"#,
560 ); 502 );
561
562 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
563 [
564 InlayHint {
565 range: 20..29,
566 kind: TypeHint,
567 label: "i32",
568 },
569 InlayHint {
570 range: 56..65,
571 kind: TypeHint,
572 label: "i32",
573 },
574 InlayHint {
575 range: 114..122,
576 kind: TypeHint,
577 label: "|…| -> i32",
578 },
579 InlayHint {
580 range: 126..127,
581 kind: TypeHint,
582 label: "i32",
583 },
584 InlayHint {
585 range: 129..130,
586 kind: TypeHint,
587 label: "i32",
588 },
589 InlayHint {
590 range: 132..133,
591 kind: TypeHint,
592 label: "i32",
593 },
594 InlayHint {
595 range: 135..136,
596 kind: TypeHint,
597 label: "i32",
598 },
599 InlayHint {
600 range: 200..212,
601 kind: TypeHint,
602 label: "&|…| -> i32",
603 },
604 InlayHint {
605 range: 235..244,
606 kind: TypeHint,
607 label: "|| -> i32",
608 },
609 ]
610 "###
611 );
612 } 503 }
613 504
614 #[test] 505 #[test]
615 fn for_expression() { 506 fn for_expression() {
616 let (analysis, file_id) = single_file( 507 check(
617 r#" 508 r#"
618fn main() { 509fn main() {
619 let mut start = 0; 510 let mut start = 0;
620 for increment in 0..2 { 511 //^^^^^^^^^ i32
621 start += increment; 512 for increment in 0..2 { start += increment; }
622 } 513 //^^^^^^^^^ i32
623}"#, 514}"#,
624 ); 515 );
625
626 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
627 [
628 InlayHint {
629 range: 20..29,
630 kind: TypeHint,
631 label: "i32",
632 },
633 InlayHint {
634 range: 43..52,
635 kind: TypeHint,
636 label: "i32",
637 },
638 ]
639 "###
640 );
641 } 516 }
642 517
643 #[test] 518 #[test]
644 fn if_expr() { 519 fn if_expr() {
645 let (analysis, file_id) = single_file( 520 check(
646 r#" 521 r#"
647#[derive(PartialEq)] 522enum Option<T> { None, Some(T) }
648enum CustomOption<T> { 523use Option::*;
649 None,
650 Some(T),
651}
652 524
653#[derive(PartialEq)] 525struct Test { a: Option<u32>, b: u8 }
654struct Test {
655 a: CustomOption<u32>,
656 b: u8,
657}
658
659use CustomOption::*;
660 526
661fn main() { 527fn main() {
662 let test = Some(Test { a: Some(3), b: 1 }); 528 let test = Some(Test { a: Some(3), b: 1 });
529 //^^^^ Option<Test>
663 if let None = &test {}; 530 if let None = &test {};
664 if let test = &test {}; 531 if let test = &test {};
532 //^^^^ &Option<Test>
665 if let Some(test) = &test {}; 533 if let Some(test) = &test {};
666 if let Some(Test { a, b }) = &test {}; 534 //^^^^ &Test
667 if let Some(Test { a: x, b: y }) = &test {}; 535 if let Some(Test { a, b }) = &test {};
668 if let Some(Test { a: Some(x), b: y }) = &test {}; 536 //^ &Option<u32> ^ &u8
669 if let Some(Test { a: None, b: y }) = &test {}; 537 if let Some(Test { a: x, b: y }) = &test {};
538 //^ &Option<u32> ^ &u8
539 if let Some(Test { a: Some(x), b: y }) = &test {};
540 //^ &u32 ^ &u8
541 if let Some(Test { a: None, b: y }) = &test {};
542 //^ &u8
670 if let Some(Test { b: y, .. }) = &test {}; 543 if let Some(Test { b: y, .. }) = &test {};
671 544 //^ &u8
672 if test == None {} 545 if test == None {}
673}"#, 546}"#,
674 ); 547 );
675
676 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
677 [
678 InlayHint {
679 range: 187..191,
680 kind: TypeHint,
681 label: "CustomOption<Test>",
682 },
683 InlayHint {
684 range: 266..270,
685 kind: TypeHint,
686 label: "&CustomOption<Test>",
687 },
688 InlayHint {
689 range: 299..303,
690 kind: TypeHint,
691 label: "&Test",
692 },
693 InlayHint {
694 range: 340..341,
695 kind: TypeHint,
696 label: "&CustomOption<u32>",
697 },
698 InlayHint {
699 range: 343..344,
700 kind: TypeHint,
701 label: "&u8",
702 },
703 InlayHint {
704 range: 386..387,
705 kind: TypeHint,
706 label: "&CustomOption<u32>",
707 },
708 InlayHint {
709 range: 392..393,
710 kind: TypeHint,
711 label: "&u8",
712 },
713 InlayHint {
714 range: 440..441,
715 kind: TypeHint,
716 label: "&u32",
717 },
718 InlayHint {
719 range: 447..448,
720 kind: TypeHint,
721 label: "&u8",
722 },
723 InlayHint {
724 range: 499..500,
725 kind: TypeHint,
726 label: "&u8",
727 },
728 InlayHint {
729 range: 542..543,
730 kind: TypeHint,
731 label: "&u8",
732 },
733 ]
734 "###
735 );
736 } 548 }
737 549
738 #[test] 550 #[test]
739 fn while_expr() { 551 fn while_expr() {
740 let (analysis, file_id) = single_file( 552 check(
741 r#" 553 r#"
742#[derive(PartialEq)] 554enum Option<T> { None, Some(T) }
743enum CustomOption<T> { 555use Option::*;
744 None,
745 Some(T),
746}
747 556
748#[derive(PartialEq)] 557struct Test { a: Option<u32>, b: u8 }
749struct Test {
750 a: CustomOption<u32>,
751 b: u8,
752}
753
754use CustomOption::*;
755 558
756fn main() { 559fn main() {
757 let test = Some(Test { a: Some(3), b: 1 }); 560 let test = Some(Test { a: Some(3), b: 1 });
758 while let None = &test {}; 561 //^^^^ Option<Test>
759 while let test = &test {}; 562 while let Some(Test { a: Some(x), b: y }) = &test {};
760 while let Some(test) = &test {}; 563 //^ &u32 ^ &u8
761 while let Some(Test { a, b }) = &test {};
762 while let Some(Test { a: x, b: y }) = &test {};
763 while let Some(Test { a: Some(x), b: y }) = &test {};
764 while let Some(Test { a: None, b: y }) = &test {};
765 while let Some(Test { b: y, .. }) = &test {};
766
767 while test == None {}
768}"#, 564}"#,
769 ); 565 );
770
771 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
772 [
773 InlayHint {
774 range: 187..191,
775 kind: TypeHint,
776 label: "CustomOption<Test>",
777 },
778 InlayHint {
779 range: 272..276,
780 kind: TypeHint,
781 label: "&CustomOption<Test>",
782 },
783 InlayHint {
784 range: 308..312,
785 kind: TypeHint,
786 label: "&Test",
787 },
788 InlayHint {
789 range: 352..353,
790 kind: TypeHint,
791 label: "&CustomOption<u32>",
792 },
793 InlayHint {
794 range: 355..356,
795 kind: TypeHint,
796 label: "&u8",
797 },
798 InlayHint {
799 range: 401..402,
800 kind: TypeHint,
801 label: "&CustomOption<u32>",
802 },
803 InlayHint {
804 range: 407..408,
805 kind: TypeHint,
806 label: "&u8",
807 },
808 InlayHint {
809 range: 458..459,
810 kind: TypeHint,
811 label: "&u32",
812 },
813 InlayHint {
814 range: 465..466,
815 kind: TypeHint,
816 label: "&u8",
817 },
818 InlayHint {
819 range: 520..521,
820 kind: TypeHint,
821 label: "&u8",
822 },
823 InlayHint {
824 range: 566..567,
825 kind: TypeHint,
826 label: "&u8",
827 },
828 ]
829 "###
830 );
831 } 566 }
832 567
833 #[test] 568 #[test]
834 fn match_arm_list() { 569 fn match_arm_list() {
835 let (analysis, file_id) = single_file( 570 check(
836 r#" 571 r#"
837#[derive(PartialEq)] 572enum Option<T> { None, Some(T) }
838enum CustomOption<T> { 573use Option::*;
839 None,
840 Some(T),
841}
842
843#[derive(PartialEq)]
844struct Test {
845 a: CustomOption<u32>,
846 b: u8,
847}
848 574
849use CustomOption::*; 575struct Test { a: Option<u32>, b: u8 }
850 576
851fn main() { 577fn main() {
852 match Some(Test { a: Some(3), b: 1 }) { 578 match Some(Test { a: Some(3), b: 1 }) {
853 None => (), 579 None => (),
854 test => (), 580 test => (),
855 Some(test) => (), 581 //^^^^ Option<Test>
856 Some(Test { a, b }) => (),
857 Some(Test { a: x, b: y }) => (),
858 Some(Test { a: Some(x), b: y }) => (), 582 Some(Test { a: Some(x), b: y }) => (),
859 Some(Test { a: None, b: y }) => (), 583 //^ u32 ^ u8
860 Some(Test { b: y, .. }) => (),
861 _ => {} 584 _ => {}
862 } 585 }
863}"#, 586}"#,
864 ); 587 );
865
866 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
867 [
868 InlayHint {
869 range: 251..255,
870 kind: TypeHint,
871 label: "CustomOption<Test>",
872 },
873 InlayHint {
874 range: 276..280,
875 kind: TypeHint,
876 label: "Test",
877 },
878 InlayHint {
879 range: 309..310,
880 kind: TypeHint,
881 label: "CustomOption<u32>",
882 },
883 InlayHint {
884 range: 312..313,
885 kind: TypeHint,
886 label: "u8",
887 },
888 InlayHint {
889 range: 347..348,
890 kind: TypeHint,
891 label: "CustomOption<u32>",
892 },
893 InlayHint {
894 range: 353..354,
895 kind: TypeHint,
896 label: "u8",
897 },
898 InlayHint {
899 range: 393..394,
900 kind: TypeHint,
901 label: "u32",
902 },
903 InlayHint {
904 range: 400..401,
905 kind: TypeHint,
906 label: "u8",
907 },
908 InlayHint {
909 range: 444..445,
910 kind: TypeHint,
911 label: "u8",
912 },
913 InlayHint {
914 range: 479..480,
915 kind: TypeHint,
916 label: "u8",
917 },
918 ]
919 "###
920 );
921 } 588 }
922 589
923 #[test] 590 #[test]
924 fn hint_truncation() { 591 fn hint_truncation() {
925 let (analysis, file_id) = single_file( 592 check_with_config(
926 r#" 593 r#"
927struct Smol<T>(T); 594struct Smol<T>(T);
928 595
@@ -930,52 +597,26 @@ struct VeryLongOuterName<T>(T);
930 597
931fn main() { 598fn main() {
932 let a = Smol(0u32); 599 let a = Smol(0u32);
600 //^ Smol<u32>
933 let b = VeryLongOuterName(0usize); 601 let b = VeryLongOuterName(0usize);
602 //^ VeryLongOuterName<…>
934 let c = Smol(Smol(0u32)) 603 let c = Smol(Smol(0u32))
604 //^ Smol<Smol<…>>
935}"#, 605}"#,
936 ); 606 InlayHintsConfig { max_length: Some(8), ..Default::default() },
937
938 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
939 [
940 InlayHint {
941 range: 73..74,
942 kind: TypeHint,
943 label: "Smol<u32>",
944 },
945 InlayHint {
946 range: 97..98,
947 kind: TypeHint,
948 label: "VeryLongOuterName<…>",
949 },
950 InlayHint {
951 range: 136..137,
952 kind: TypeHint,
953 label: "Smol<Smol<…>>",
954 },
955 ]
956 "###
957 ); 607 );
958 } 608 }
959 609
960 #[test] 610 #[test]
961 fn function_call_parameter_hint() { 611 fn function_call_parameter_hint() {
962 let (analysis, file_id) = single_file( 612 check(
963 r#" 613 r#"
964enum CustomOption<T> { 614enum Option<T> { None, Some(T) }
965 None, 615use Option::*;
966 Some(T),
967}
968use CustomOption::*;
969 616
970struct FileId {} 617struct FileId {}
971struct SmolStr {} 618struct SmolStr {}
972 619
973impl From<&str> for SmolStr {
974 fn from(_: &str) -> Self {
975 unimplemented!()
976 }
977}
978
979struct TextRange {} 620struct TextRange {}
980struct SyntaxKind {} 621struct SyntaxKind {}
981struct NavigationTarget {} 622struct NavigationTarget {}
@@ -983,18 +624,15 @@ struct NavigationTarget {}
983struct Test {} 624struct Test {}
984 625
985impl Test { 626impl Test {
986 fn method(&self, mut param: i32) -> i32 { 627 fn method(&self, mut param: i32) -> i32 { param * 2 }
987 param * 2
988 }
989 628
990 fn from_syntax( 629 fn from_syntax(
991 file_id: FileId, 630 file_id: FileId,
992 name: SmolStr, 631 name: SmolStr,
993 focus_range: CustomOption<TextRange>, 632 focus_range: Option<TextRange>,
994 full_range: TextRange, 633 full_range: TextRange,
995 kind: SyntaxKind, 634 kind: SyntaxKind,
996 docs: CustomOption<String>, 635 docs: Option<String>,
997 description: CustomOption<String>,
998 ) -> NavigationTarget { 636 ) -> NavigationTarget {
999 NavigationTarget {} 637 NavigationTarget {}
1000 } 638 }
@@ -1006,108 +644,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1006 644
1007fn main() { 645fn main() {
1008 let not_literal = 1; 646 let not_literal = 1;
1009 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 647 //^^^^^^^^^^^ i32
648 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
649 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1010 let t: Test = Test {}; 650 let t: Test = Test {};
1011 t.method(123); 651 t.method(123);
1012 Test::method(&t, 3456); 652 //^^^ param
1013 653 Test::method(&t, 3456);
654 //^^ &self ^^^^ param
1014 Test::from_syntax( 655 Test::from_syntax(
1015 FileId {}, 656 FileId {},
657 //^^^^^^^^^ file_id
1016 "impl".into(), 658 "impl".into(),
659 //^^^^^^^^^^^^^ name
1017 None, 660 None,
661 //^^^^ focus_range
1018 TextRange {}, 662 TextRange {},
663 //^^^^^^^^^^^^ full_range
1019 SyntaxKind {}, 664 SyntaxKind {},
665 //^^^^^^^^^^^^^ kind
1020 None, 666 None,
1021 None, 667 //^^^^ docs
1022 ); 668 );
1023}"#, 669}"#,
1024 ); 670 );
1025
1026 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1027 [
1028 InlayHint {
1029 range: 797..808,
1030 kind: TypeHint,
1031 label: "i32",
1032 },
1033 InlayHint {
1034 range: 841..842,
1035 kind: ParameterHint,
1036 label: "foo",
1037 },
1038 InlayHint {
1039 range: 844..845,
1040 kind: ParameterHint,
1041 label: "bar",
1042 },
1043 InlayHint {
1044 range: 847..854,
1045 kind: ParameterHint,
1046 label: "msg",
1047 },
1048 InlayHint {
1049 range: 859..870,
1050 kind: ParameterHint,
1051 label: "last",
1052 },
1053 InlayHint {
1054 range: 913..916,
1055 kind: ParameterHint,
1056 label: "param",
1057 },
1058 InlayHint {
1059 range: 936..938,
1060 kind: ParameterHint,
1061 label: "&self",
1062 },
1063 InlayHint {
1064 range: 940..944,
1065 kind: ParameterHint,
1066 label: "param",
1067 },
1068 InlayHint {
1069 range: 979..988,
1070 kind: ParameterHint,
1071 label: "file_id",
1072 },
1073 InlayHint {
1074 range: 998..1011,
1075 kind: ParameterHint,
1076 label: "name",
1077 },
1078 InlayHint {
1079 range: 1021..1025,
1080 kind: ParameterHint,
1081 label: "focus_range",
1082 },
1083 InlayHint {
1084 range: 1035..1047,
1085 kind: ParameterHint,
1086 label: "full_range",
1087 },
1088 InlayHint {
1089 range: 1057..1070,
1090 kind: ParameterHint,
1091 label: "kind",
1092 },
1093 InlayHint {
1094 range: 1080..1084,
1095 kind: ParameterHint,
1096 label: "docs",
1097 },
1098 InlayHint {
1099 range: 1094..1098,
1100 kind: ParameterHint,
1101 label: "description",
1102 },
1103 ]
1104 "###
1105 );
1106 } 671 }
1107 672
1108 #[test] 673 #[test]
1109 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
1110 let (analysis, file_id) = single_file( 675 check_with_config(
1111 r#" 676 r#"
1112fn map(f: i32) {} 677fn map(f: i32) {}
1113fn filter(predicate: i32) {} 678fn filter(predicate: i32) {}
@@ -1188,23 +753,16 @@ fn main() {
1188 let _: f64 = a.div_euclid(b); 753 let _: f64 = a.div_euclid(b);
1189 let _: f64 = a.abs_sub(b); 754 let _: f64 = a.abs_sub(b);
1190}"#, 755}"#,
1191 ); 756 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1192
1193 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1194 []
1195 "###
1196 ); 757 );
1197 } 758 }
1198 759
1199 #[test] 760 #[test]
1200 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
1201 let (analysis, file_id) = single_file( 762 check_with_config(
1202 r#" 763 r#"
1203enum CustomResult<T, E> { 764enum Result<T, E> { Ok(T), Err(E) }
1204 Ok(T), 765use Result::*;
1205 Err(E),
1206}
1207use CustomResult::*;
1208 766
1209struct SyntheticSyntax; 767struct SyntheticSyntax;
1210 768
@@ -1214,136 +772,157 @@ fn main() {
1214 Err(SyntheticSyntax) => (), 772 Err(SyntheticSyntax) => (),
1215 } 773 }
1216}"#, 774}"#,
1217 ); 775 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1218
1219 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1220 []
1221 "###
1222 ); 776 );
1223 } 777 }
1224 778
1225 #[test] 779 #[test]
1226 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
1227 let (analysis, file_id) = single_file( 781 check_expect(
1228 r#" 782 r#"
1229 struct A(B); 783struct A(B);
1230 impl A { fn into_b(self) -> B { self.0 } } 784impl A { fn into_b(self) -> B { self.0 } }
1231 struct B(C); 785struct B(C);
1232 impl B { fn into_c(self) -> C { self.0 } } 786impl B { fn into_c(self) -> C { self.0 } }
1233 struct C; 787struct C;
1234 788
1235 fn main() { 789fn main() {
1236 let c = A(B(C)) 790 let c = A(B(C))
1237 .into_b() // This is a comment 791 .into_b() // This is a comment
1238 .into_c(); 792 .into_c();
1239 }"#, 793}
794"#,
795 InlayHintsConfig {
796 parameter_hints: false,
797 type_hints: false,
798 chaining_hints: true,
799 max_length: None,
800 },
801 expect![[r#"
802 [
803 InlayHint {
804 range: 147..172,
805 kind: ChainingHint,
806 label: "B",
807 },
808 InlayHint {
809 range: 147..154,
810 kind: ChainingHint,
811 label: "A",
812 },
813 ]
814 "#]],
1240 ); 815 );
1241 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1242 [
1243 InlayHint {
1244 range: 147..172,
1245 kind: ChainingHint,
1246 label: "B",
1247 },
1248 InlayHint {
1249 range: 147..154,
1250 kind: ChainingHint,
1251 label: "A",
1252 },
1253 ]
1254 "###);
1255 } 816 }
1256 817
1257 #[test] 818 #[test]
1258 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
1259 let (analysis, file_id) = single_file( 820 check_with_config(
1260 r#" 821 r#"
1261 struct A(B); 822struct A(B);
1262 impl A { fn into_b(self) -> B { self.0 } } 823impl A { fn into_b(self) -> B { self.0 } }
1263 struct B(C); 824struct B(C);
1264 impl B { fn into_c(self) -> C { self.0 } } 825impl B { fn into_c(self) -> C { self.0 } }
1265 struct C; 826struct C;
1266 827
1267 fn main() { 828fn main() {
1268 let c = A(B(C)).into_b().into_c(); 829 let c = A(B(C)).into_b().into_c();
1269 }"#, 830}"#,
831 InlayHintsConfig {
832 parameter_hints: false,
833 type_hints: false,
834 chaining_hints: true,
835 max_length: None,
836 },
1270 ); 837 );
1271 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1272 } 838 }
1273 839
1274 #[test] 840 #[test]
1275 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
1276 let (analysis, file_id) = single_file( 842 check_expect(
1277 r#" 843 r#"
1278 struct A { pub b: B } 844struct A { pub b: B }
1279 struct B { pub c: C } 845struct B { pub c: C }
1280 struct C(pub bool); 846struct C(pub bool);
1281 struct D; 847struct D;
1282 848
1283 impl D { 849impl D {
1284 fn foo(&self) -> i32 { 42 } 850 fn foo(&self) -> i32 { 42 }
1285 } 851}
1286 852
1287 fn main() { 853fn main() {
1288 let x = A { b: B { c: C(true) } } 854 let x = A { b: B { c: C(true) } }
1289 .b 855 .b
1290 .c 856 .c
1291 .0; 857 .0;
1292 let x = D 858 let x = D
1293 .foo(); 859 .foo();
1294 }"#, 860}"#,
861 InlayHintsConfig {
862 parameter_hints: false,
863 type_hints: false,
864 chaining_hints: true,
865 max_length: None,
866 },
867 expect![[r#"
868 [
869 InlayHint {
870 range: 143..190,
871 kind: ChainingHint,
872 label: "C",
873 },
874 InlayHint {
875 range: 143..179,
876 kind: ChainingHint,
877 label: "B",
878 },
879 ]
880 "#]],
1295 ); 881 );
1296 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1297 [
1298 InlayHint {
1299 range: 143..190,
1300 kind: ChainingHint,
1301 label: "C",
1302 },
1303 InlayHint {
1304 range: 143..179,
1305 kind: ChainingHint,
1306 label: "B",
1307 },
1308 ]
1309 "###);
1310 } 882 }
1311 883
1312 #[test] 884 #[test]
1313 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
1314 let (analysis, file_id) = single_file( 886 check_expect(
1315 r#" 887 r#"
1316 struct A<T>(T); 888struct A<T>(T);
1317 struct B<T>(T); 889struct B<T>(T);
1318 struct C<T>(T); 890struct C<T>(T);
1319 struct X<T,R>(T, R); 891struct X<T,R>(T, R);
1320 892
1321 impl<T> A<T> { 893impl<T> A<T> {
1322 fn new(t: T) -> Self { A(t) } 894 fn new(t: T) -> Self { A(t) }
1323 fn into_b(self) -> B<T> { B(self.0) } 895 fn into_b(self) -> B<T> { B(self.0) }
1324 } 896}
1325 impl<T> B<T> { 897impl<T> B<T> {
1326 fn into_c(self) -> C<T> { C(self.0) } 898 fn into_c(self) -> C<T> { C(self.0) }
1327 } 899}
1328 fn main() { 900fn main() {
1329 let c = A::new(X(42, true)) 901 let c = A::new(X(42, true))
1330 .into_b() 902 .into_b()
1331 .into_c(); 903 .into_c();
1332 }"#, 904}
905"#,
906 InlayHintsConfig {
907 parameter_hints: false,
908 type_hints: false,
909 chaining_hints: true,
910 max_length: None,
911 },
912 expect![[r#"
913 [
914 InlayHint {
915 range: 246..283,
916 kind: ChainingHint,
917 label: "B<X<i32, bool>>",
918 },
919 InlayHint {
920 range: 246..265,
921 kind: ChainingHint,
922 label: "A<X<i32, bool>>",
923 },
924 ]
925 "#]],
1333 ); 926 );
1334 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1335 [
1336 InlayHint {
1337 range: 246..283,
1338 kind: ChainingHint,
1339 label: "B<X<i32, bool>>",
1340 },
1341 InlayHint {
1342 range: 246..265,
1343 kind: ChainingHint,
1344 label: "A<X<i32, bool>>",
1345 },
1346 ]
1347 "###);
1348 } 927 }
1349} 928}
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index 5036c1fb0..6907c09e8 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -165,10 +165,7 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Opti
165} 165}
166 166
167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { 167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
168 match (left, right) { 168 matches!((left, right), (T![,], T![')']) | (T![,], T![']']))
169 (T![,], T![')']) | (T![,], T![']']) => true,
170 _ => false,
171 }
172} 169}
173 170
174#[cfg(test)] 171#[cfg(test)]
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 889b84c59..a393d3dba 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();
@@ -110,7 +130,7 @@ impl MockAnalysis {
110 let other_crate = crate_graph.add_crate_root( 130 let other_crate = crate_graph.add_crate_root(
111 file_id, 131 file_id,
112 edition, 132 edition,
113 Some(CrateName::new(crate_name).unwrap()), 133 Some(crate_name.to_string()),
114 cfg, 134 cfg,
115 env, 135 env,
116 Default::default(), 136 Default::default(),
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index f569a3f17..ed15d6494 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -251,13 +251,21 @@ fn runnable_mod(
251 251
252#[cfg(test)] 252#[cfg(test)]
253mod tests { 253mod tests {
254 use insta::assert_debug_snapshot; 254 use expect::{expect, Expect};
255 255
256 use crate::mock_analysis::analysis_and_position; 256 use crate::mock_analysis::analysis_and_position;
257 257
258 use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST}; 258 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST};
259 259
260 fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) { 260 fn check(
261 ra_fixture: &str,
262 // FIXME: fold this into `expect` as well
263 actions: &[&RunnableAction],
264 expect: Expect,
265 ) {
266 let (analysis, position) = analysis_and_position(ra_fixture);
267 let runnables = analysis.runnables(position.file_id).unwrap();
268 expect.assert_debug_eq(&runnables);
261 assert_eq!( 269 assert_eq!(
262 actions, 270 actions,
263 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice() 271 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
@@ -266,579 +274,557 @@ mod tests {
266 274
267 #[test] 275 #[test]
268 fn test_runnables() { 276 fn test_runnables() {
269 let (analysis, pos) = analysis_and_position( 277 check(
270 r#" 278 r#"
271 //- /lib.rs 279//- /lib.rs
272 <|> //empty 280<|>
273 fn main() {} 281fn main() {}
274 282
275 #[test] 283#[test]
276 fn test_foo() {} 284fn test_foo() {}
277 285
278 #[test] 286#[test]
279 #[ignore] 287#[ignore]
280 fn test_foo() {} 288fn test_foo() {}
281 289
282 #[bench] 290#[bench]
283 fn bench() {} 291fn bench() {}
284 "#, 292"#,
285 ); 293 &[&BIN, &TEST, &TEST, &BENCH],
286 let runnables = analysis.runnables(pos.file_id).unwrap(); 294 expect![[r#"
287 assert_debug_snapshot!(&runnables, 295 [
288 @r###" 296 Runnable {
289 [ 297 nav: NavigationTarget {
290 Runnable { 298 file_id: FileId(
291 nav: NavigationTarget { 299 1,
292 file_id: FileId( 300 ),
293 1, 301 full_range: 1..13,
294 ), 302 name: "main",
295 full_range: 1..21, 303 kind: FN_DEF,
296 name: "main", 304 focus_range: Some(
297 kind: FN_DEF, 305 4..8,
298 focus_range: Some( 306 ),
299 12..16, 307 container_name: None,
300 ), 308 description: None,
301 container_name: None, 309 docs: None,
302 description: None, 310 },
303 docs: None, 311 kind: Bin,
304 }, 312 cfg_exprs: [],
305 kind: Bin,
306 cfg_exprs: [],
307 },
308 Runnable {
309 nav: NavigationTarget {
310 file_id: FileId(
311 1,
312 ),
313 full_range: 23..47,
314 name: "test_foo",
315 kind: FN_DEF,
316 focus_range: Some(
317 34..42,
318 ),
319 container_name: None,
320 description: None,
321 docs: None,
322 },
323 kind: Test {
324 test_id: Path(
325 "test_foo",
326 ),
327 attr: TestAttr {
328 ignore: false,
329 }, 313 },
330 }, 314 Runnable {
331 cfg_exprs: [], 315 nav: NavigationTarget {
332 }, 316 file_id: FileId(
333 Runnable { 317 1,
334 nav: NavigationTarget { 318 ),
335 file_id: FileId( 319 full_range: 15..39,
336 1, 320 name: "test_foo",
337 ), 321 kind: FN_DEF,
338 full_range: 49..83, 322 focus_range: Some(
339 name: "test_foo", 323 26..34,
340 kind: FN_DEF, 324 ),
341 focus_range: Some( 325 container_name: None,
342 70..78, 326 description: None,
343 ), 327 docs: None,
344 container_name: None, 328 },
345 description: None, 329 kind: Test {
346 docs: None, 330 test_id: Path(
347 }, 331 "test_foo",
348 kind: Test { 332 ),
349 test_id: Path( 333 attr: TestAttr {
350 "test_foo", 334 ignore: false,
351 ), 335 },
352 attr: TestAttr { 336 },
353 ignore: true, 337 cfg_exprs: [],
338 },
339 Runnable {
340 nav: NavigationTarget {
341 file_id: FileId(
342 1,
343 ),
344 full_range: 41..75,
345 name: "test_foo",
346 kind: FN_DEF,
347 focus_range: Some(
348 62..70,
349 ),
350 container_name: None,
351 description: None,
352 docs: None,
353 },
354 kind: Test {
355 test_id: Path(
356 "test_foo",
357 ),
358 attr: TestAttr {
359 ignore: true,
360 },
361 },
362 cfg_exprs: [],
354 }, 363 },
355 }, 364 Runnable {
356 cfg_exprs: [], 365 nav: NavigationTarget {
357 }, 366 file_id: FileId(
358 Runnable { 367 1,
359 nav: NavigationTarget { 368 ),
360 file_id: FileId( 369 full_range: 77..99,
361 1, 370 name: "bench",
362 ), 371 kind: FN_DEF,
363 full_range: 85..107, 372 focus_range: Some(
364 name: "bench", 373 89..94,
365 kind: FN_DEF, 374 ),
366 focus_range: Some( 375 container_name: None,
367 97..102, 376 description: None,
368 ), 377 docs: None,
369 container_name: None, 378 },
370 description: None, 379 kind: Bench {
371 docs: None, 380 test_id: Path(
372 }, 381 "bench",
373 kind: Bench { 382 ),
374 test_id: Path( 383 },
375 "bench", 384 cfg_exprs: [],
376 ), 385 },
377 }, 386 ]
378 cfg_exprs: [], 387 "#]],
379 }, 388 );
380 ]
381 "###
382 );
383 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
384 } 389 }
385 390
386 #[test] 391 #[test]
387 fn test_runnables_doc_test() { 392 fn test_runnables_doc_test() {
388 let (analysis, pos) = analysis_and_position( 393 check(
389 r#" 394 r#"
390 //- /lib.rs 395//- /lib.rs
391 <|> //empty 396<|>
392 fn main() {} 397fn main() {}
393 398
394 /// ``` 399/// ```
395 /// let x = 5; 400/// let x = 5;
396 /// ``` 401/// ```
397 fn foo() {} 402fn foo() {}
398 "#, 403"#,
404 &[&BIN, &DOCTEST],
405 expect![[r#"
406 [
407 Runnable {
408 nav: NavigationTarget {
409 file_id: FileId(
410 1,
411 ),
412 full_range: 1..13,
413 name: "main",
414 kind: FN_DEF,
415 focus_range: Some(
416 4..8,
417 ),
418 container_name: None,
419 description: None,
420 docs: None,
421 },
422 kind: Bin,
423 cfg_exprs: [],
424 },
425 Runnable {
426 nav: NavigationTarget {
427 file_id: FileId(
428 1,
429 ),
430 full_range: 15..57,
431 name: "foo",
432 kind: FN_DEF,
433 focus_range: None,
434 container_name: None,
435 description: None,
436 docs: None,
437 },
438 kind: DocTest {
439 test_id: Path(
440 "foo",
441 ),
442 },
443 cfg_exprs: [],
444 },
445 ]
446 "#]],
399 ); 447 );
400 let runnables = analysis.runnables(pos.file_id).unwrap();
401 assert_debug_snapshot!(&runnables,
402 @r###"
403 [
404 Runnable {
405 nav: NavigationTarget {
406 file_id: FileId(
407 1,
408 ),
409 full_range: 1..21,
410 name: "main",
411 kind: FN_DEF,
412 focus_range: Some(
413 12..16,
414 ),
415 container_name: None,
416 description: None,
417 docs: None,
418 },
419 kind: Bin,
420 cfg_exprs: [],
421 },
422 Runnable {
423 nav: NavigationTarget {
424 file_id: FileId(
425 1,
426 ),
427 full_range: 23..65,
428 name: "foo",
429 kind: FN_DEF,
430 focus_range: None,
431 container_name: None,
432 description: None,
433 docs: None,
434 },
435 kind: DocTest {
436 test_id: Path(
437 "foo",
438 ),
439 },
440 cfg_exprs: [],
441 },
442 ]
443 "###
444 );
445 assert_actions(&runnables, &[&BIN, &DOCTEST]);
446 } 448 }
447 449
448 #[test] 450 #[test]
449 fn test_runnables_doc_test_in_impl() { 451 fn test_runnables_doc_test_in_impl() {
450 let (analysis, pos) = analysis_and_position( 452 check(
451 r#" 453 r#"
452 //- /lib.rs 454//- /lib.rs
453 <|> //empty 455<|>
454 fn main() {} 456fn main() {}
455 457
456 struct Data; 458struct Data;
457 impl Data { 459impl Data {
458 /// ``` 460 /// ```
459 /// let x = 5; 461 /// let x = 5;
460 /// ``` 462 /// ```
461 fn foo() {} 463 fn foo() {}
462 } 464}
463 "#, 465"#,
466 &[&BIN, &DOCTEST],
467 expect![[r#"
468 [
469 Runnable {
470 nav: NavigationTarget {
471 file_id: FileId(
472 1,
473 ),
474 full_range: 1..13,
475 name: "main",
476 kind: FN_DEF,
477 focus_range: Some(
478 4..8,
479 ),
480 container_name: None,
481 description: None,
482 docs: None,
483 },
484 kind: Bin,
485 cfg_exprs: [],
486 },
487 Runnable {
488 nav: NavigationTarget {
489 file_id: FileId(
490 1,
491 ),
492 full_range: 44..98,
493 name: "foo",
494 kind: FN_DEF,
495 focus_range: None,
496 container_name: None,
497 description: None,
498 docs: None,
499 },
500 kind: DocTest {
501 test_id: Path(
502 "Data::foo",
503 ),
504 },
505 cfg_exprs: [],
506 },
507 ]
508 "#]],
464 ); 509 );
465 let runnables = analysis.runnables(pos.file_id).unwrap();
466 assert_debug_snapshot!(&runnables,
467 @r###"
468 [
469 Runnable {
470 nav: NavigationTarget {
471 file_id: FileId(
472 1,
473 ),
474 full_range: 1..21,
475 name: "main",
476 kind: FN_DEF,
477 focus_range: Some(
478 12..16,
479 ),
480 container_name: None,
481 description: None,
482 docs: None,
483 },
484 kind: Bin,
485 cfg_exprs: [],
486 },
487 Runnable {
488 nav: NavigationTarget {
489 file_id: FileId(
490 1,
491 ),
492 full_range: 52..106,
493 name: "foo",
494 kind: FN_DEF,
495 focus_range: None,
496 container_name: None,
497 description: None,
498 docs: None,
499 },
500 kind: DocTest {
501 test_id: Path(
502 "Data::foo",
503 ),
504 },
505 cfg_exprs: [],
506 },
507 ]
508 "###
509 );
510 assert_actions(&runnables, &[&BIN, &DOCTEST]);
511 } 510 }
512 511
513 #[test] 512 #[test]
514 fn test_runnables_module() { 513 fn test_runnables_module() {
515 let (analysis, pos) = analysis_and_position( 514 check(
516 r#" 515 r#"
517 //- /lib.rs 516//- /lib.rs
518 <|> //empty 517<|>
519 mod test_mod { 518mod test_mod {
520 #[test] 519 #[test]
521 fn test_foo1() {} 520 fn test_foo1() {}
522 } 521}
523 "#, 522"#,
524 ); 523 &[&TEST, &TEST],
525 let runnables = analysis.runnables(pos.file_id).unwrap(); 524 expect![[r#"
526 assert_debug_snapshot!(&runnables, 525 [
527 @r###" 526 Runnable {
528 [ 527 nav: NavigationTarget {
529 Runnable { 528 file_id: FileId(
530 nav: NavigationTarget { 529 1,
531 file_id: FileId( 530 ),
532 1, 531 full_range: 1..51,
533 ), 532 name: "test_mod",
534 full_range: 1..59, 533 kind: MODULE,
535 name: "test_mod", 534 focus_range: Some(
536 kind: MODULE, 535 5..13,
537 focus_range: Some( 536 ),
538 13..21, 537 container_name: None,
539 ), 538 description: None,
540 container_name: None, 539 docs: None,
541 description: None, 540 },
542 docs: None, 541 kind: TestMod {
543 }, 542 path: "test_mod",
544 kind: TestMod { 543 },
545 path: "test_mod", 544 cfg_exprs: [],
546 }, 545 },
547 cfg_exprs: [], 546 Runnable {
548 }, 547 nav: NavigationTarget {
549 Runnable { 548 file_id: FileId(
550 nav: NavigationTarget { 549 1,
551 file_id: FileId( 550 ),
552 1, 551 full_range: 20..49,
553 ), 552 name: "test_foo1",
554 full_range: 28..57, 553 kind: FN_DEF,
555 name: "test_foo1", 554 focus_range: Some(
556 kind: FN_DEF, 555 35..44,
557 focus_range: Some( 556 ),
558 43..52, 557 container_name: None,
559 ), 558 description: None,
560 container_name: None, 559 docs: None,
561 description: None, 560 },
562 docs: None, 561 kind: Test {
563 }, 562 test_id: Path(
564 kind: Test { 563 "test_mod::test_foo1",
565 test_id: Path( 564 ),
566 "test_mod::test_foo1", 565 attr: TestAttr {
567 ), 566 ignore: false,
568 attr: TestAttr { 567 },
569 ignore: false, 568 },
569 cfg_exprs: [],
570 }, 570 },
571 }, 571 ]
572 cfg_exprs: [], 572 "#]],
573 }, 573 );
574 ]
575 "###
576 );
577 assert_actions(&runnables, &[&TEST, &TEST]);
578 } 574 }
579 575
580 #[test] 576 #[test]
581 fn test_runnables_one_depth_layer_module() { 577 fn test_runnables_one_depth_layer_module() {
582 let (analysis, pos) = analysis_and_position( 578 check(
583 r#" 579 r#"
584 //- /lib.rs 580//- /lib.rs
585 <|> //empty 581<|>
586 mod foo { 582mod foo {
587 mod test_mod { 583 mod test_mod {
588 #[test] 584 #[test]
589 fn test_foo1() {} 585 fn test_foo1() {}
590 } 586 }
591 } 587}
592 "#, 588"#,
593 ); 589 &[&TEST, &TEST],
594 let runnables = analysis.runnables(pos.file_id).unwrap(); 590 expect![[r#"
595 assert_debug_snapshot!(&runnables, 591 [
596 @r###" 592 Runnable {
597 [ 593 nav: NavigationTarget {
598 Runnable { 594 file_id: FileId(
599 nav: NavigationTarget { 595 1,
600 file_id: FileId( 596 ),
601 1, 597 full_range: 15..77,
602 ), 598 name: "test_mod",
603 full_range: 23..85, 599 kind: MODULE,
604 name: "test_mod", 600 focus_range: Some(
605 kind: MODULE, 601 19..27,
606 focus_range: Some( 602 ),
607 27..35, 603 container_name: None,
608 ), 604 description: None,
609 container_name: None, 605 docs: None,
610 description: None, 606 },
611 docs: None, 607 kind: TestMod {
612 }, 608 path: "foo::test_mod",
613 kind: TestMod { 609 },
614 path: "foo::test_mod", 610 cfg_exprs: [],
615 }, 611 },
616 cfg_exprs: [], 612 Runnable {
617 }, 613 nav: NavigationTarget {
618 Runnable { 614 file_id: FileId(
619 nav: NavigationTarget { 615 1,
620 file_id: FileId( 616 ),
621 1, 617 full_range: 38..71,
622 ), 618 name: "test_foo1",
623 full_range: 46..79, 619 kind: FN_DEF,
624 name: "test_foo1", 620 focus_range: Some(
625 kind: FN_DEF, 621 57..66,
626 focus_range: Some( 622 ),
627 65..74, 623 container_name: None,
628 ), 624 description: None,
629 container_name: None, 625 docs: None,
630 description: None, 626 },
631 docs: None, 627 kind: Test {
632 }, 628 test_id: Path(
633 kind: Test { 629 "foo::test_mod::test_foo1",
634 test_id: Path( 630 ),
635 "foo::test_mod::test_foo1", 631 attr: TestAttr {
636 ), 632 ignore: false,
637 attr: TestAttr { 633 },
638 ignore: false, 634 },
635 cfg_exprs: [],
639 }, 636 },
640 }, 637 ]
641 cfg_exprs: [], 638 "#]],
642 }, 639 );
643 ]
644 "###
645 );
646 assert_actions(&runnables, &[&TEST, &TEST]);
647 } 640 }
648 641
649 #[test] 642 #[test]
650 fn test_runnables_multiple_depth_module() { 643 fn test_runnables_multiple_depth_module() {
651 let (analysis, pos) = analysis_and_position( 644 check(
652 r#" 645 r#"
653 //- /lib.rs 646//- /lib.rs
654 <|> //empty 647<|>
655 mod foo { 648mod foo {
656 mod bar { 649 mod bar {
657 mod test_mod { 650 mod test_mod {
658 #[test] 651 #[test]
659 fn test_foo1() {} 652 fn test_foo1() {}
660 }
661 }
662 } 653 }
663 "#, 654 }
664 ); 655}
665 let runnables = analysis.runnables(pos.file_id).unwrap(); 656"#,
666 assert_debug_snapshot!(&runnables, 657 &[&TEST, &TEST],
667 @r###" 658 expect![[r#"
668 [ 659 [
669 Runnable { 660 Runnable {
670 nav: NavigationTarget { 661 nav: NavigationTarget {
671 file_id: FileId( 662 file_id: FileId(
672 1, 663 1,
673 ), 664 ),
674 full_range: 41..115, 665 full_range: 33..107,
675 name: "test_mod", 666 name: "test_mod",
676 kind: MODULE, 667 kind: MODULE,
677 focus_range: Some( 668 focus_range: Some(
678 45..53, 669 37..45,
679 ), 670 ),
680 container_name: None, 671 container_name: None,
681 description: None, 672 description: None,
682 docs: None, 673 docs: None,
683 }, 674 },
684 kind: TestMod { 675 kind: TestMod {
685 path: "foo::bar::test_mod", 676 path: "foo::bar::test_mod",
686 }, 677 },
687 cfg_exprs: [], 678 cfg_exprs: [],
688 }, 679 },
689 Runnable { 680 Runnable {
690 nav: NavigationTarget { 681 nav: NavigationTarget {
691 file_id: FileId( 682 file_id: FileId(
692 1, 683 1,
693 ), 684 ),
694 full_range: 68..105, 685 full_range: 60..97,
695 name: "test_foo1", 686 name: "test_foo1",
696 kind: FN_DEF, 687 kind: FN_DEF,
697 focus_range: Some( 688 focus_range: Some(
698 91..100, 689 83..92,
699 ), 690 ),
700 container_name: None, 691 container_name: None,
701 description: None, 692 description: None,
702 docs: None, 693 docs: None,
703 }, 694 },
704 kind: Test { 695 kind: Test {
705 test_id: Path( 696 test_id: Path(
706 "foo::bar::test_mod::test_foo1", 697 "foo::bar::test_mod::test_foo1",
707 ), 698 ),
708 attr: TestAttr { 699 attr: TestAttr {
709 ignore: false, 700 ignore: false,
701 },
702 },
703 cfg_exprs: [],
710 }, 704 },
711 }, 705 ]
712 cfg_exprs: [], 706 "#]],
713 }, 707 );
714 ]
715 "###
716 );
717 assert_actions(&runnables, &[&TEST, &TEST]);
718 } 708 }
719 709
720 #[test] 710 #[test]
721 fn test_runnables_with_feature() { 711 fn test_runnables_with_feature() {
722 let (analysis, pos) = analysis_and_position( 712 check(
723 r#" 713 r#"
724 //- /lib.rs crate:foo cfg:feature=foo 714//- /lib.rs crate:foo cfg:feature=foo
725 <|> //empty 715<|>
726 #[test] 716#[test]
727 #[cfg(feature = "foo")] 717#[cfg(feature = "foo")]
728 fn test_foo1() {} 718fn test_foo1() {}
729 "#, 719"#,
730 ); 720 &[&TEST],
731 let runnables = analysis.runnables(pos.file_id).unwrap(); 721 expect![[r#"
732 assert_debug_snapshot!(&runnables, 722 [
733 @r###" 723 Runnable {
734 [ 724 nav: NavigationTarget {
735 Runnable { 725 file_id: FileId(
736 nav: NavigationTarget { 726 1,
737 file_id: FileId( 727 ),
738 1, 728 full_range: 1..50,
739 ), 729 name: "test_foo1",
740 full_range: 1..58, 730 kind: FN_DEF,
741 name: "test_foo1", 731 focus_range: Some(
742 kind: FN_DEF, 732 36..45,
743 focus_range: Some( 733 ),
744 44..53, 734 container_name: None,
745 ), 735 description: None,
746 container_name: None, 736 docs: None,
747 description: None, 737 },
748 docs: None, 738 kind: Test {
749 }, 739 test_id: Path(
750 kind: Test { 740 "test_foo1",
751 test_id: Path( 741 ),
752 "test_foo1", 742 attr: TestAttr {
753 ), 743 ignore: false,
754 attr: TestAttr { 744 },
755 ignore: false, 745 },
756 }, 746 cfg_exprs: [
757 }, 747 KeyValue {
758 cfg_exprs: [ 748 key: "feature",
759 KeyValue { 749 value: "foo",
760 key: "feature", 750 },
761 value: "foo", 751 ],
762 }, 752 },
763 ], 753 ]
764 }, 754 "#]],
765 ] 755 );
766 "###
767 );
768 assert_actions(&runnables, &[&TEST]);
769 } 756 }
770 757
771 #[test] 758 #[test]
772 fn test_runnables_with_features() { 759 fn test_runnables_with_features() {
773 let (analysis, pos) = analysis_and_position( 760 check(
774 r#" 761 r#"
775 //- /lib.rs crate:foo cfg:feature=foo,feature=bar 762//- /lib.rs crate:foo cfg:feature=foo,feature=bar
776 <|> //empty 763<|>
777 #[test] 764#[test]
778 #[cfg(all(feature = "foo", feature = "bar"))] 765#[cfg(all(feature = "foo", feature = "bar"))]
779 fn test_foo1() {} 766fn test_foo1() {}
780 "#, 767"#,
781 ); 768 &[&TEST],
782 let runnables = analysis.runnables(pos.file_id).unwrap(); 769 expect![[r#"
783 assert_debug_snapshot!(&runnables, 770 [
784 @r###" 771 Runnable {
785 [ 772 nav: NavigationTarget {
786 Runnable { 773 file_id: FileId(
787 nav: NavigationTarget { 774 1,
788 file_id: FileId( 775 ),
789 1, 776 full_range: 1..72,
790 ), 777 name: "test_foo1",
791 full_range: 1..80, 778 kind: FN_DEF,
792 name: "test_foo1", 779 focus_range: Some(
793 kind: FN_DEF, 780 58..67,
794 focus_range: Some( 781 ),
795 66..75, 782 container_name: None,
796 ), 783 description: None,
797 container_name: None, 784 docs: None,
798 description: None, 785 },
799 docs: None, 786 kind: Test {
800 }, 787 test_id: Path(
801 kind: Test { 788 "test_foo1",
802 test_id: Path( 789 ),
803 "test_foo1", 790 attr: TestAttr {
804 ), 791 ignore: false,
805 attr: TestAttr {
806 ignore: false,
807 },
808 },
809 cfg_exprs: [
810 All(
811 [
812 KeyValue {
813 key: "feature",
814 value: "foo",
815 },
816 KeyValue {
817 key: "feature",
818 value: "bar",
819 }, 792 },
793 },
794 cfg_exprs: [
795 All(
796 [
797 KeyValue {
798 key: "feature",
799 value: "foo",
800 },
801 KeyValue {
802 key: "feature",
803 value: "bar",
804 },
805 ],
806 ),
820 ], 807 ],
821 ), 808 },
822 ], 809 ]
823 }, 810 "#]],
824 ] 811 );
825 "###
826 );
827 assert_actions(&runnables, &[&TEST]);
828 } 812 }
829 813
830 #[test] 814 #[test]
831 fn test_runnables_no_test_function_in_module() { 815 fn test_runnables_no_test_function_in_module() {
832 let (analysis, pos) = analysis_and_position( 816 check(
833 r#" 817 r#"
834 //- /lib.rs 818//- /lib.rs
835 <|> //empty 819<|>
836 mod test_mod { 820mod test_mod {
837 fn foo1() {} 821 fn foo1() {}
838 } 822}
839 "#, 823"#,
824 &[],
825 expect![[r#"
826 []
827 "#]],
840 ); 828 );
841 let runnables = analysis.runnables(pos.file_id).unwrap();
842 assert!(runnables.is_empty())
843 } 829 }
844} 830}
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 6cb96608b..9f8e540c0 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -4,7 +4,7 @@ use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
4use crate::SourceFileEdit; 4use crate::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrError, SsrRule}; 5use ra_ssr::{MatchFinder, SsrError, SsrRule};
6 6
7// Feature: Structural Seach and Replace 7// Feature: Structural Search and Replace
8// 8//
9// Search and replace with named wildcards that will match any expression, type, path, pattern or item. 9// Search and replace with named wildcards that will match any expression, type, path, pattern or item.
10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.