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/complete_unqualified_path.rs17
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs2
-rw-r--r--crates/ra_ide/src/completion/presentation.rs4
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs22
-rw-r--r--crates/ra_ide/src/diagnostics.rs684
-rw-r--r--crates/ra_ide/src/inlay_hints.rs94
-rw-r--r--crates/ra_ide/src/mock_analysis.rs20
7 files changed, 360 insertions, 483 deletions
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 72ff82e66..18f4488b7 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -638,4 +638,21 @@ fn f() {}
638 expect![[""]], 638 expect![[""]],
639 ) 639 )
640 } 640 }
641
642 #[test]
643 fn completes_type_or_trait_in_impl_block() {
644 check(
645 r#"
646trait MyTrait {}
647struct MyStruct {}
648
649impl My<|>
650"#,
651 expect![[r#"
652 st MyStruct
653 tt MyTrait
654 tp Self
655 "#]],
656 )
657 }
641} 658}
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index d7011c9cf..7bdda316c 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -58,7 +58,7 @@ pub struct CompletionItem {
58 score: Option<CompletionScore>, 58 score: Option<CompletionScore>,
59} 59}
60 60
61// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem { 62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index e4c57e41a..48afee5fb 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -491,7 +491,7 @@ mod tests {
491 } 491 }
492 } 492 }
493 493
494 let mut completions = get_all_completion_items(ra_fixture, &CompletionConfig::default()); 494 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
495 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 495 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
496 let actual = completions 496 let actual = completions
497 .into_iter() 497 .into_iter()
@@ -835,6 +835,7 @@ fn bar(s: &S) {
835 fn suppress_arg_snippets() { 835 fn suppress_arg_snippets() {
836 mark::check!(suppress_arg_snippets); 836 mark::check!(suppress_arg_snippets);
837 check_edit_with_config( 837 check_edit_with_config(
838 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
838 "with_args", 839 "with_args",
839 r#" 840 r#"
840fn with_args(x: i32, y: String) {} 841fn with_args(x: i32, y: String) {}
@@ -844,7 +845,6 @@ fn main() { with_<|> }
844fn with_args(x: i32, y: String) {} 845fn with_args(x: i32, y: String) {}
845fn main() { with_args($0) } 846fn main() { with_args($0) }
846"#, 847"#,
847 &CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
848 ); 848 );
849 } 849 }
850 850
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index c2be23697..919177745 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -13,15 +13,15 @@ use crate::{
13}; 13};
14 14
15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
16 do_completion_with_config(code, kind, &CompletionConfig::default()) 16 do_completion_with_config(CompletionConfig::default(), code, kind)
17} 17}
18 18
19pub(crate) fn do_completion_with_config( 19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
20 code: &str, 21 code: &str,
21 kind: CompletionKind, 22 kind: CompletionKind,
22 config: &CompletionConfig,
23) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, config) 24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
25 .into_iter() 25 .into_iter()
26 .filter(|c| c.completion_kind == kind) 26 .filter(|c| c.completion_kind == kind)
27 .collect(); 27 .collect();
@@ -30,15 +30,15 @@ pub(crate) fn do_completion_with_config(
30} 30}
31 31
32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
33 completion_list_with_config(code, kind, &CompletionConfig::default()) 33 completion_list_with_config(CompletionConfig::default(), code, kind)
34} 34}
35 35
36pub(crate) fn completion_list_with_config( 36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
37 code: &str, 38 code: &str,
38 kind: CompletionKind, 39 kind: CompletionKind,
39 config: &CompletionConfig,
40) -> String { 40) -> String {
41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, config) 41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
42 .into_iter() 42 .into_iter()
43 .filter(|c| c.completion_kind == kind) 43 .filter(|c| c.completion_kind == kind)
44 .collect(); 44 .collect();
@@ -70,19 +70,19 @@ fn monospace_width(s: &str) -> usize {
70} 70}
71 71
72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
73 check_edit_with_config(what, ra_fixture_before, ra_fixture_after, &CompletionConfig::default()) 73 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
74} 74}
75 75
76pub(crate) fn check_edit_with_config( 76pub(crate) fn check_edit_with_config(
77 config: CompletionConfig,
77 what: &str, 78 what: &str,
78 ra_fixture_before: &str, 79 ra_fixture_before: &str,
79 ra_fixture_after: &str, 80 ra_fixture_after: &str,
80 config: &CompletionConfig,
81) { 81) {
82 let ra_fixture_after = trim_indent(ra_fixture_after); 82 let ra_fixture_after = trim_indent(ra_fixture_after);
83 let (analysis, position) = analysis_and_position(ra_fixture_before); 83 let (analysis, position) = analysis_and_position(ra_fixture_before);
84 let completions: Vec<CompletionItem> = 84 let completions: Vec<CompletionItem> =
85 analysis.completions(config, position).unwrap().unwrap().into(); 85 analysis.completions(&config, position).unwrap().unwrap().into();
86 let (completion,) = completions 86 let (completion,) = completions
87 .iter() 87 .iter()
88 .filter(|it| it.lookup() == what) 88 .filter(|it| it.lookup() == what)
@@ -106,9 +106,9 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -
106} 106}
107 107
108pub(crate) fn get_all_completion_items( 108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
109 code: &str, 110 code: &str,
110 options: &CompletionConfig,
111) -> Vec<CompletionItem> { 111) -> Vec<CompletionItem> {
112 let (analysis, position) = analysis_and_position(code); 112 let (analysis, position) = analysis_and_position(code);
113 analysis.completions(options, position).unwrap().unwrap().into() 113 analysis.completions(&config, position).unwrap().unwrap().into()
114} 114}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index f4bc0d619..e69e9b4ec 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -273,53 +273,21 @@ fn check_struct_shorthand_initialization(
273 273
274#[cfg(test)] 274#[cfg(test)]
275mod tests { 275mod tests {
276 use insta::assert_debug_snapshot;
277 use ra_syntax::SourceFile;
278 use stdx::trim_indent; 276 use stdx::trim_indent;
279 use test_utils::assert_eq_text; 277 use test_utils::assert_eq_text;
280 278
281 use crate::mock_analysis::{analysis_and_position, single_file}; 279 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
282 280 use expect::{expect, Expect};
283 use super::*;
284
285 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
286
287 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
288 let parse = SourceFile::parse(code);
289 let mut diagnostics = Vec::new();
290 for node in parse.tree().syntax().descendants() {
291 func(&mut diagnostics, FileId(0), &node);
292 }
293 assert!(diagnostics.is_empty());
294 }
295
296 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
297 let parse = SourceFile::parse(before);
298 let mut diagnostics = Vec::new();
299 for node in parse.tree().syntax().descendants() {
300 func(&mut diagnostics, FileId(0), &node);
301 }
302 let diagnostic =
303 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
304 let mut fix = diagnostic.fix.unwrap();
305 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
306 let actual = {
307 let mut actual = before.to_string();
308 edit.apply(&mut actual);
309 actual
310 };
311 assert_eq_text!(after, &actual);
312 }
313 281
314 /// Takes a multi-file input fixture with annotated cursor positions, 282 /// Takes a multi-file input fixture with annotated cursor positions,
315 /// and checks that: 283 /// and checks that:
316 /// * a diagnostic is produced 284 /// * a diagnostic is produced
317 /// * this diagnostic touches the input cursor position 285 /// * this diagnostic touches the input cursor position
318 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 286 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
319 fn check_apply_diagnostic_fix_from_position(ra_fixture: &str, after: &str) { 287 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
320 let after = trim_indent(after); 288 let after = trim_indent(ra_fixture_after);
321 289
322 let (analysis, file_position) = analysis_and_position(ra_fixture); 290 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
323 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 291 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
324 let mut fix = diagnostic.fix.unwrap(); 292 let mut fix = diagnostic.fix.unwrap();
325 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 293 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -340,284 +308,251 @@ mod tests {
340 ); 308 );
341 } 309 }
342 310
343 fn check_apply_diagnostic_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
344 let ra_fixture_after = &trim_indent(ra_fixture_after);
345 let (analysis, file_id) = single_file(ra_fixture_before);
346 let before = analysis.file_text(file_id).unwrap();
347 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
348 let mut fix = diagnostic.fix.unwrap();
349 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
350 let actual = {
351 let mut actual = before.to_string();
352 edit.apply(&mut actual);
353 actual
354 };
355 assert_eq_text!(ra_fixture_after, &actual);
356 }
357
358 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 311 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
359 /// apply to the file containing the cursor. 312 /// apply to the file containing the cursor.
360 fn check_no_diagnostic_for_target_file(ra_fixture: &str) { 313 fn check_no_diagnostics(ra_fixture: &str) {
361 let (analysis, file_position) = analysis_and_position(ra_fixture); 314 let mock = MockAnalysis::with_files(ra_fixture);
362 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 315 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
363 assert_eq!(diagnostics.len(), 0); 316 let analysis = mock.analysis();
364 } 317 let diagnostics = files
365 318 .into_iter()
366 fn check_no_diagnostic(ra_fixture: &str) { 319 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
320 .collect::<Vec<_>>();
321 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
322 }
323
324 fn check_expect(ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture); 325 let (analysis, file_id) = single_file(ra_fixture);
368 let diagnostics = analysis.diagnostics(file_id).unwrap(); 326 let diagnostics = analysis.diagnostics(file_id).unwrap();
369 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 327 expect.assert_debug_eq(&diagnostics)
370 } 328 }
371 329
372 #[test] 330 #[test]
373 fn test_wrap_return_type() { 331 fn test_wrap_return_type() {
374 let before = r#" 332 check_fix(
375 //- /main.rs 333 r#"
376 use core::result::Result::{self, Ok, Err}; 334//- /main.rs
377 335use core::result::Result::{self, Ok, Err};
378 fn div(x: i32, y: i32) -> Result<i32, ()> { 336
379 if y == 0 { 337fn div(x: i32, y: i32) -> Result<i32, ()> {
380 return Err(()); 338 if y == 0 {
381 } 339 return Err(());
382 x / y<|> 340 }
383 } 341 x / y<|>
384 //- /core/lib.rs 342}
385 pub mod result { 343//- /core/lib.rs
386 pub enum Result<T, E> { Ok(T), Err(E) } 344pub mod result {
387 } 345 pub enum Result<T, E> { Ok(T), Err(E) }
388 "#; 346}
389 let after = r#" 347"#,
390 use core::result::Result::{self, Ok, Err}; 348 r#"
391 349use core::result::Result::{self, Ok, Err};
392 fn div(x: i32, y: i32) -> Result<i32, ()> { 350
393 if y == 0 { 351fn div(x: i32, y: i32) -> Result<i32, ()> {
394 return Err(()); 352 if y == 0 {
395 } 353 return Err(());
396 Ok(x / y) 354 }
397 } 355 Ok(x / y)
398 "#; 356}
399 check_apply_diagnostic_fix_from_position(before, after); 357"#,
358 );
400 } 359 }
401 360
402 #[test] 361 #[test]
403 fn test_wrap_return_type_handles_generic_functions() { 362 fn test_wrap_return_type_handles_generic_functions() {
404 let before = r#" 363 check_fix(
405 //- /main.rs 364 r#"
406 use core::result::Result::{self, Ok, Err}; 365//- /main.rs
407 366use core::result::Result::{self, Ok, Err};
408 fn div<T>(x: T) -> Result<T, i32> { 367
409 if x == 0 { 368fn div<T>(x: T) -> Result<T, i32> {
410 return Err(7); 369 if x == 0 {
411 } 370 return Err(7);
412 <|>x 371 }
413 } 372 <|>x
414 //- /core/lib.rs 373}
415 pub mod result { 374//- /core/lib.rs
416 pub enum Result<T, E> { Ok(T), Err(E) } 375pub mod result {
417 } 376 pub enum Result<T, E> { Ok(T), Err(E) }
418 "#; 377}
419 let after = r#" 378"#,
420 use core::result::Result::{self, Ok, Err}; 379 r#"
421 380use core::result::Result::{self, Ok, Err};
422 fn div<T>(x: T) -> Result<T, i32> { 381
423 if x == 0 { 382fn div<T>(x: T) -> Result<T, i32> {
424 return Err(7); 383 if x == 0 {
425 } 384 return Err(7);
426 Ok(x) 385 }
427 } 386 Ok(x)
428 "#; 387}
429 check_apply_diagnostic_fix_from_position(before, after); 388"#,
389 );
430 } 390 }
431 391
432 #[test] 392 #[test]
433 fn test_wrap_return_type_handles_type_aliases() { 393 fn test_wrap_return_type_handles_type_aliases() {
434 let before = r#" 394 check_fix(
435 //- /main.rs 395 r#"
436 use core::result::Result::{self, Ok, Err}; 396//- /main.rs
397use core::result::Result::{self, Ok, Err};
437 398
438 type MyResult<T> = Result<T, ()>; 399type MyResult<T> = Result<T, ()>;
439 400
440 fn div(x: i32, y: i32) -> MyResult<i32> { 401fn div(x: i32, y: i32) -> MyResult<i32> {
441 if y == 0 { 402 if y == 0 {
442 return Err(()); 403 return Err(());
443 } 404 }
444 x <|>/ y 405 x <|>/ y
445 } 406}
446 //- /core/lib.rs 407//- /core/lib.rs
447 pub mod result { 408pub mod result {
448 pub enum Result<T, E> { Ok(T), Err(E) } 409 pub enum Result<T, E> { Ok(T), Err(E) }
449 } 410}
450 "#; 411"#,
451 let after = r#" 412 r#"
452 use core::result::Result::{self, Ok, Err}; 413use core::result::Result::{self, Ok, Err};
453 414
454 type MyResult<T> = Result<T, ()>; 415type MyResult<T> = Result<T, ()>;
455 416
456 fn div(x: i32, y: i32) -> MyResult<i32> { 417fn div(x: i32, y: i32) -> MyResult<i32> {
457 if y == 0 { 418 if y == 0 {
458 return Err(()); 419 return Err(());
459 } 420 }
460 Ok(x / y) 421 Ok(x / y)
461 } 422}
462 "#; 423"#,
463 check_apply_diagnostic_fix_from_position(before, after); 424 );
464 } 425 }
465 426
466 #[test] 427 #[test]
467 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 428 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
468 check_no_diagnostic_for_target_file( 429 check_no_diagnostics(
469 r" 430 r#"
470 //- /main.rs 431//- /main.rs
471 use core::result::Result::{self, Ok, Err}; 432use core::result::Result::{self, Ok, Err};
472 433
473 fn foo() -> Result<(), i32> { 434fn foo() -> Result<(), i32> { 0 }
474 0<|>
475 }
476 435
477 //- /core/lib.rs 436//- /core/lib.rs
478 pub mod result { 437pub mod result {
479 pub enum Result<T, E> { Ok(T), Err(E) } 438 pub enum Result<T, E> { Ok(T), Err(E) }
480 } 439}
481 ", 440"#,
482 ); 441 );
483 } 442 }
484 443
485 #[test] 444 #[test]
486 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 445 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
487 check_no_diagnostic_for_target_file( 446 check_no_diagnostics(
488 r" 447 r#"
489 //- /main.rs 448//- /main.rs
490 use core::result::Result::{self, Ok, Err}; 449use core::result::Result::{self, Ok, Err};
491 450
492 enum SomeOtherEnum { 451enum SomeOtherEnum { Ok(i32), Err(String) }
493 Ok(i32),
494 Err(String),
495 }
496 452
497 fn foo() -> SomeOtherEnum { 453fn foo() -> SomeOtherEnum { 0 }
498 0<|>
499 }
500 454
501 //- /core/lib.rs 455//- /core/lib.rs
502 pub mod result { 456pub mod result {
503 pub enum Result<T, E> { Ok(T), Err(E) } 457 pub enum Result<T, E> { Ok(T), Err(E) }
504 } 458}
505 ", 459"#,
506 ); 460 );
507 } 461 }
508 462
509 #[test] 463 #[test]
510 fn test_fill_struct_fields_empty() { 464 fn test_fill_struct_fields_empty() {
511 let before = r" 465 check_fix(
512 struct TestStruct { 466 r#"
513 one: i32, 467struct TestStruct { one: i32, two: i64 }
514 two: i64,
515 }
516 468
517 fn test_fn() { 469fn test_fn() {
518 let s = TestStruct{}; 470 let s = TestStruct {<|>};
519 } 471}
520 "; 472"#,
521 let after = r" 473 r#"
522 struct TestStruct { 474struct TestStruct { one: i32, two: i64 }
523 one: i32,
524 two: i64,
525 }
526 475
527 fn test_fn() { 476fn test_fn() {
528 let s = TestStruct{ one: (), two: ()}; 477 let s = TestStruct { one: (), two: ()};
529 } 478}
530 "; 479"#,
531 check_apply_diagnostic_fix(before, after); 480 );
532 } 481 }
533 482
534 #[test] 483 #[test]
535 fn test_fill_struct_fields_self() { 484 fn test_fill_struct_fields_self() {
536 let before = r" 485 check_fix(
537 struct TestStruct { 486 r#"
538 one: i32, 487struct TestStruct { one: i32 }
539 }
540 488
541 impl TestStruct { 489impl TestStruct {
542 fn test_fn() { 490 fn test_fn() { let s = Self {<|>}; }
543 let s = Self {}; 491}
544 } 492"#,
545 } 493 r#"
546 "; 494struct TestStruct { one: i32 }
547 let after = r"
548 struct TestStruct {
549 one: i32,
550 }
551 495
552 impl TestStruct { 496impl TestStruct {
553 fn test_fn() { 497 fn test_fn() { let s = Self { one: ()}; }
554 let s = Self { one: ()}; 498}
555 } 499"#,
556 } 500 );
557 ";
558 check_apply_diagnostic_fix(before, after);
559 } 501 }
560 502
561 #[test] 503 #[test]
562 fn test_fill_struct_fields_enum() { 504 fn test_fill_struct_fields_enum() {
563 let before = r" 505 check_fix(
564 enum Expr { 506 r#"
565 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 507enum Expr {
566 } 508 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
509}
567 510
568 impl Expr { 511impl Expr {
569 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 512 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
570 Expr::Bin { } 513 Expr::Bin {<|> }
571 } 514 }
572 } 515}
573 "; 516"#,
574 let after = r" 517 r#"
575 enum Expr { 518enum Expr {
576 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 519 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
577 } 520}
578 521
579 impl Expr { 522impl Expr {
580 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 523 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
581 Expr::Bin { lhs: (), rhs: () } 524 Expr::Bin { lhs: (), rhs: () }
582 } 525 }
583 } 526}
584 "; 527"#,
585 check_apply_diagnostic_fix(before, after); 528 );
586 } 529 }
587 530
588 #[test] 531 #[test]
589 fn test_fill_struct_fields_partial() { 532 fn test_fill_struct_fields_partial() {
590 let before = r" 533 check_fix(
591 struct TestStruct { 534 r#"
592 one: i32, 535struct TestStruct { one: i32, two: i64 }
593 two: i64,
594 }
595 536
596 fn test_fn() { 537fn test_fn() {
597 let s = TestStruct{ two: 2 }; 538 let s = TestStruct{ two: 2<|> };
598 } 539}
599 "; 540"#,
600 let after = r" 541 r"
601 struct TestStruct { 542struct TestStruct { one: i32, two: i64 }
602 one: i32,
603 two: i64,
604 }
605 543
606 fn test_fn() { 544fn test_fn() {
607 let s = TestStruct{ two: 2, one: () }; 545 let s = TestStruct{ two: 2, one: () };
608 } 546}
609 "; 547",
610 check_apply_diagnostic_fix(before, after); 548 );
611 } 549 }
612 550
613 #[test] 551 #[test]
614 fn test_fill_struct_fields_no_diagnostic() { 552 fn test_fill_struct_fields_no_diagnostic() {
615 check_no_diagnostic( 553 check_no_diagnostics(
616 r" 554 r"
617 struct TestStruct { 555 struct TestStruct { one: i32, two: i64 }
618 one: i32,
619 two: i64,
620 }
621 556
622 fn test_fn() { 557 fn test_fn() {
623 let one = 1; 558 let one = 1;
@@ -629,12 +564,9 @@ mod tests {
629 564
630 #[test] 565 #[test]
631 fn test_fill_struct_fields_no_diagnostic_on_spread() { 566 fn test_fill_struct_fields_no_diagnostic_on_spread() {
632 check_no_diagnostic( 567 check_no_diagnostics(
633 r" 568 r"
634 struct TestStruct { 569 struct TestStruct { one: i32, two: i64 }
635 one: i32,
636 two: i64,
637 }
638 570
639 fn test_fn() { 571 fn test_fn() {
640 let one = 1; 572 let one = 1;
@@ -646,211 +578,143 @@ mod tests {
646 578
647 #[test] 579 #[test]
648 fn test_unresolved_module_diagnostic() { 580 fn test_unresolved_module_diagnostic() {
649 let (analysis, file_id) = single_file("mod foo;"); 581 check_expect(
650 let diagnostics = analysis.diagnostics(file_id).unwrap(); 582 r#"mod foo;"#,
651 assert_debug_snapshot!(diagnostics, @r###" 583 expect![[r#"
652 [ 584 [
653 Diagnostic { 585 Diagnostic {
654 message: "unresolved module", 586 message: "unresolved module",
655 range: 0..8, 587 range: 0..8,
656 severity: Error, 588 severity: Error,
657 fix: Some( 589 fix: Some(
658 Fix { 590 Fix {
659 label: "Create module", 591 label: "Create module",
660 source_change: SourceChange { 592 source_change: SourceChange {
661 source_file_edits: [], 593 source_file_edits: [],
662 file_system_edits: [ 594 file_system_edits: [
663 CreateFile { 595 CreateFile {
664 anchor: FileId( 596 anchor: FileId(
665 1, 597 1,
666 ), 598 ),
667 dst: "foo.rs", 599 dst: "foo.rs",
600 },
601 ],
602 is_snippet: false,
668 }, 603 },
669 ], 604 },
670 is_snippet: false, 605 ),
671 },
672 }, 606 },
673 ), 607 ]
674 }, 608 "#]],
675 ] 609 );
676 "###);
677 } 610 }
678 611
679 #[test] 612 #[test]
680 fn range_mapping_out_of_macros() { 613 fn range_mapping_out_of_macros() {
681 let (analysis, file_id) = single_file( 614 // FIXME: this is very wrong, but somewhat tricky to fix.
682 r" 615 check_fix(
683 fn some() {} 616 r#"
684 fn items() {} 617fn some() {}
685 fn here() {} 618fn items() {}
619fn here() {}
686 620
687 macro_rules! id { 621macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
688 ($($tt:tt)*) => { $($tt)*};
689 }
690 622
691 fn main() { 623fn main() {
692 let _x = id![Foo { a: 42 }]; 624 let _x = id![Foo { a: <|>42 }];
693 } 625}
694 626
695 pub struct Foo { 627pub struct Foo { pub a: i32, pub b: i32 }
696 pub a: i32, 628"#,
697 pub b: i32, 629 r#"
698 } 630fn {a:42, b: ()} {}
699 ", 631fn items() {}
632fn here() {}
633
634macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
635
636fn main() {
637 let _x = id![Foo { a: 42 }];
638}
639
640pub struct Foo { pub a: i32, pub b: i32 }
641"#,
700 ); 642 );
701 let diagnostics = analysis.diagnostics(file_id).unwrap();
702 assert_debug_snapshot!(diagnostics, @r###"
703 [
704 Diagnostic {
705 message: "Missing structure fields:\n- b\n",
706 range: 127..136,
707 severity: Error,
708 fix: Some(
709 Fix {
710 label: "Fill struct fields",
711 source_change: SourceChange {
712 source_file_edits: [
713 SourceFileEdit {
714 file_id: FileId(
715 1,
716 ),
717 edit: TextEdit {
718 indels: [
719 Indel {
720 insert: "{a:42, b: ()}",
721 delete: 3..9,
722 },
723 ],
724 },
725 },
726 ],
727 file_system_edits: [],
728 is_snippet: false,
729 },
730 },
731 ),
732 },
733 ]
734 "###);
735 } 643 }
736 644
737 #[test] 645 #[test]
738 fn test_check_unnecessary_braces_in_use_statement() { 646 fn test_check_unnecessary_braces_in_use_statement() {
739 check_not_applicable( 647 check_no_diagnostics(
740 " 648 r#"
741 use a; 649use a;
742 use a::{c, d::e}; 650use a::{c, d::e};
743 ", 651"#,
744 check_unnecessary_braces_in_use_statement,
745 );
746 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
747 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
748 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
749 check_apply(
750 "use a::{c, d::{e}};",
751 "use a::{c, d::e};",
752 check_unnecessary_braces_in_use_statement,
753 ); 652 );
653 check_fix(r#"use {<|>b};"#, r#"use b;"#);
654 check_fix(r#"use {b<|>};"#, r#"use b;"#);
655 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
656 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
657 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
754 } 658 }
755 659
756 #[test] 660 #[test]
757 fn test_check_struct_shorthand_initialization() { 661 fn test_check_struct_shorthand_initialization() {
758 check_not_applicable( 662 check_no_diagnostics(
759 r#" 663 r#"
760 struct A { 664struct A { a: &'static str }
761 a: &'static str 665fn main() { A { a: "hello" } }
762 } 666"#,
763
764 fn main() {
765 A {
766 a: "hello"
767 }
768 }
769 "#,
770 check_struct_shorthand_initialization,
771 ); 667 );
772 check_not_applicable( 668 check_no_diagnostics(
773 r#" 669 r#"
774 struct A(usize); 670struct A(usize);
775 671fn main() { A { 0: 0 } }
776 fn main() { 672"#,
777 A {
778 0: 0
779 }
780 }
781 "#,
782 check_struct_shorthand_initialization,
783 ); 673 );
784 674
785 check_apply( 675 check_fix(
786 r#" 676 r#"
787struct A { 677struct A { a: &'static str }
788 a: &'static str
789}
790
791fn main() { 678fn main() {
792 let a = "haha"; 679 let a = "haha";
793 A { 680 A { a<|>: a }
794 a: a
795 }
796} 681}
797 "#, 682"#,
798 r#" 683 r#"
799struct A { 684struct A { a: &'static str }
800 a: &'static str
801}
802
803fn main() { 685fn main() {
804 let a = "haha"; 686 let a = "haha";
805 A { 687 A { a }
806 a
807 }
808} 688}
809 "#, 689"#,
810 check_struct_shorthand_initialization,
811 ); 690 );
812 691
813 check_apply( 692 check_fix(
814 r#" 693 r#"
815struct A { 694struct A { a: &'static str, b: &'static str }
816 a: &'static str,
817 b: &'static str
818}
819
820fn main() { 695fn main() {
821 let a = "haha"; 696 let a = "haha";
822 let b = "bb"; 697 let b = "bb";
823 A { 698 A { a<|>: a, b }
824 a: a,
825 b
826 }
827} 699}
828 "#, 700"#,
829 r#" 701 r#"
830struct A { 702struct A { a: &'static str, b: &'static str }
831 a: &'static str,
832 b: &'static str
833}
834
835fn main() { 703fn main() {
836 let a = "haha"; 704 let a = "haha";
837 let b = "bb"; 705 let b = "bb";
838 A { 706 A { a, b }
839 a,
840 b
841 }
842} 707}
843 "#, 708"#,
844 check_struct_shorthand_initialization,
845 ); 709 );
846 } 710 }
847 711
848 #[test] 712 #[test]
849 fn test_add_field_from_usage() { 713 fn test_add_field_from_usage() {
850 check_apply_diagnostic_fix( 714 check_fix(
851 r" 715 r"
852fn main() { 716fn main() {
853 Foo { bar: 3, baz: false}; 717 Foo { bar: 3, baz<|>: false};
854} 718}
855struct Foo { 719struct Foo {
856 bar: i32 720 bar: i32
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 62d364bfa..35ab741d8 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -351,10 +351,10 @@ mod tests {
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; 351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352 352
353 fn check(ra_fixture: &str) { 353 fn check(ra_fixture: &str) {
354 check_with_config(ra_fixture, InlayHintsConfig::default()); 354 check_with_config(InlayHintsConfig::default(), ra_fixture);
355 } 355 }
356 356
357 fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) { 357 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
358 let (analysis, file_id) = single_file(ra_fixture); 358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); 359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
@@ -363,7 +363,7 @@ mod tests {
363 assert_eq!(expected, actual); 363 assert_eq!(expected, actual);
364 } 364 }
365 365
366 fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) { 366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture); 367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints) 369 expect.assert_debug_eq(&inlay_hints)
@@ -372,6 +372,12 @@ mod tests {
372 #[test] 372 #[test]
373 fn param_hints_only() { 373 fn param_hints_only() {
374 check_with_config( 374 check_with_config(
375 InlayHintsConfig {
376 parameter_hints: true,
377 type_hints: false,
378 chaining_hints: false,
379 max_length: None,
380 },
375 r#" 381 r#"
376fn foo(a: i32, b: i32) -> i32 { a + b } 382fn foo(a: i32, b: i32) -> i32 { a + b }
377fn main() { 383fn main() {
@@ -382,47 +388,41 @@ fn main() {
382 //^ b 388 //^ b
383 ); 389 );
384}"#, 390}"#,
385 InlayHintsConfig {
386 parameter_hints: true,
387 type_hints: false,
388 chaining_hints: false,
389 max_length: None,
390 },
391 ); 391 );
392 } 392 }
393 393
394 #[test] 394 #[test]
395 fn hints_disabled() { 395 fn hints_disabled() {
396 check_with_config( 396 check_with_config(
397 r#"
398fn foo(a: i32, b: i32) -> i32 { a + b }
399fn main() {
400 let _x = foo(4, 4);
401}"#,
402 InlayHintsConfig { 397 InlayHintsConfig {
403 type_hints: false, 398 type_hints: false,
404 parameter_hints: false, 399 parameter_hints: false,
405 chaining_hints: false, 400 chaining_hints: false,
406 max_length: None, 401 max_length: None,
407 }, 402 },
403 r#"
404fn foo(a: i32, b: i32) -> i32 { a + b }
405fn main() {
406 let _x = foo(4, 4);
407}"#,
408 ); 408 );
409 } 409 }
410 410
411 #[test] 411 #[test]
412 fn type_hints_only() { 412 fn type_hints_only() {
413 check_with_config( 413 check_with_config(
414 r#"
415fn foo(a: i32, b: i32) -> i32 { a + b }
416fn main() {
417 let _x = foo(4, 4);
418 //^^ i32
419}"#,
420 InlayHintsConfig { 414 InlayHintsConfig {
421 type_hints: true, 415 type_hints: true,
422 parameter_hints: false, 416 parameter_hints: false,
423 chaining_hints: false, 417 chaining_hints: false,
424 max_length: None, 418 max_length: None,
425 }, 419 },
420 r#"
421fn foo(a: i32, b: i32) -> i32 { a + b }
422fn main() {
423 let _x = foo(4, 4);
424 //^^ i32
425}"#,
426 ); 426 );
427 } 427 }
428 428
@@ -590,6 +590,7 @@ fn main() {
590 #[test] 590 #[test]
591 fn hint_truncation() { 591 fn hint_truncation() {
592 check_with_config( 592 check_with_config(
593 InlayHintsConfig { max_length: Some(8), ..Default::default() },
593 r#" 594 r#"
594struct Smol<T>(T); 595struct Smol<T>(T);
595 596
@@ -603,7 +604,6 @@ fn main() {
603 let c = Smol(Smol(0u32)) 604 let c = Smol(Smol(0u32))
604 //^ Smol<Smol<…>> 605 //^ Smol<Smol<…>>
605}"#, 606}"#,
606 InlayHintsConfig { max_length: Some(8), ..Default::default() },
607 ); 607 );
608 } 608 }
609 609
@@ -673,6 +673,7 @@ fn main() {
673 #[test] 673 #[test]
674 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
675 check_with_config( 675 check_with_config(
676 InlayHintsConfig { max_length: Some(8), ..Default::default() },
676 r#" 677 r#"
677fn map(f: i32) {} 678fn map(f: i32) {}
678fn filter(predicate: i32) {} 679fn filter(predicate: i32) {}
@@ -753,13 +754,13 @@ fn main() {
753 let _: f64 = a.div_euclid(b); 754 let _: f64 = a.div_euclid(b);
754 let _: f64 = a.abs_sub(b); 755 let _: f64 = a.abs_sub(b);
755}"#, 756}"#,
756 InlayHintsConfig { max_length: Some(8), ..Default::default() },
757 ); 757 );
758 } 758 }
759 759
760 #[test] 760 #[test]
761 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
762 check_with_config( 762 check_with_config(
763 InlayHintsConfig { max_length: Some(8), ..Default::default() },
763 r#" 764 r#"
764enum Result<T, E> { Ok(T), Err(E) } 765enum Result<T, E> { Ok(T), Err(E) }
765use Result::*; 766use Result::*;
@@ -772,13 +773,18 @@ fn main() {
772 Err(SyntheticSyntax) => (), 773 Err(SyntheticSyntax) => (),
773 } 774 }
774}"#, 775}"#,
775 InlayHintsConfig { max_length: Some(8), ..Default::default() },
776 ); 776 );
777 } 777 }
778 778
779 #[test] 779 #[test]
780 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
781 check_expect( 781 check_expect(
782 InlayHintsConfig {
783 parameter_hints: false,
784 type_hints: false,
785 chaining_hints: true,
786 max_length: None,
787 },
782 r#" 788 r#"
783struct A(B); 789struct A(B);
784impl A { fn into_b(self) -> B { self.0 } } 790impl A { fn into_b(self) -> B { self.0 } }
@@ -792,12 +798,6 @@ fn main() {
792 .into_c(); 798 .into_c();
793} 799}
794"#, 800"#,
795 InlayHintsConfig {
796 parameter_hints: false,
797 type_hints: false,
798 chaining_hints: true,
799 max_length: None,
800 },
801 expect![[r#" 801 expect![[r#"
802 [ 802 [
803 InlayHint { 803 InlayHint {
@@ -818,6 +818,12 @@ fn main() {
818 #[test] 818 #[test]
819 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
820 check_with_config( 820 check_with_config(
821 InlayHintsConfig {
822 parameter_hints: false,
823 type_hints: false,
824 chaining_hints: true,
825 max_length: None,
826 },
821 r#" 827 r#"
822struct A(B); 828struct A(B);
823impl A { fn into_b(self) -> B { self.0 } } 829impl A { fn into_b(self) -> B { self.0 } }
@@ -828,18 +834,18 @@ struct C;
828fn main() { 834fn main() {
829 let c = A(B(C)).into_b().into_c(); 835 let c = A(B(C)).into_b().into_c();
830}"#, 836}"#,
831 InlayHintsConfig {
832 parameter_hints: false,
833 type_hints: false,
834 chaining_hints: true,
835 max_length: None,
836 },
837 ); 837 );
838 } 838 }
839 839
840 #[test] 840 #[test]
841 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
842 check_expect( 842 check_expect(
843 InlayHintsConfig {
844 parameter_hints: false,
845 type_hints: false,
846 chaining_hints: true,
847 max_length: None,
848 },
843 r#" 849 r#"
844struct A { pub b: B } 850struct A { pub b: B }
845struct B { pub c: C } 851struct B { pub c: C }
@@ -858,12 +864,6 @@ fn main() {
858 let x = D 864 let x = D
859 .foo(); 865 .foo();
860}"#, 866}"#,
861 InlayHintsConfig {
862 parameter_hints: false,
863 type_hints: false,
864 chaining_hints: true,
865 max_length: None,
866 },
867 expect![[r#" 867 expect![[r#"
868 [ 868 [
869 InlayHint { 869 InlayHint {
@@ -884,6 +884,12 @@ fn main() {
884 #[test] 884 #[test]
885 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
886 check_expect( 886 check_expect(
887 InlayHintsConfig {
888 parameter_hints: false,
889 type_hints: false,
890 chaining_hints: true,
891 max_length: None,
892 },
887 r#" 893 r#"
888struct A<T>(T); 894struct A<T>(T);
889struct B<T>(T); 895struct B<T>(T);
@@ -903,12 +909,6 @@ fn main() {
903 .into_c(); 909 .into_c();
904} 910}
905"#, 911"#,
906 InlayHintsConfig {
907 parameter_hints: false,
908 type_hints: false,
909 chaining_hints: true,
910 max_length: None,
911 },
912 expect![[r#" 912 expect![[r#"
913 [ 913 [
914 InlayHint { 914 InlayHint {
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index a393d3dba..b28054688 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -71,20 +71,13 @@ impl MockAnalysis {
71 } 71 }
72 72
73 pub fn id_of(&self, path: &str) -> FileId { 73 pub fn id_of(&self, path: &str) -> FileId {
74 let (idx, _) = self 74 let (file_id, _) =
75 .files 75 self.files().find(|(_, data)| path == data.path).expect("no file in this mock");
76 .iter() 76 file_id
77 .enumerate()
78 .find(|(_, data)| path == data.path)
79 .expect("no file in this mock");
80 FileId(idx as u32 + 1)
81 } 77 }
82 pub fn annotations(&self) -> Vec<(FileRange, String)> { 78 pub fn annotations(&self) -> Vec<(FileRange, String)> {
83 self.files 79 self.files()
84 .iter() 80 .flat_map(|(file_id, fixture)| {
85 .enumerate()
86 .flat_map(|(idx, fixture)| {
87 let file_id = FileId(idx as u32 + 1);
88 let annotations = extract_annotations(&fixture.text); 81 let annotations = extract_annotations(&fixture.text);
89 annotations 82 annotations
90 .into_iter() 83 .into_iter()
@@ -92,6 +85,9 @@ impl MockAnalysis {
92 }) 85 })
93 .collect() 86 .collect()
94 } 87 }
88 pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ {
89 self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture))
90 }
95 pub fn annotation(&self) -> (FileRange, String) { 91 pub fn annotation(&self) -> (FileRange, String) {
96 let mut all = self.annotations(); 92 let mut all = self.annotations();
97 assert_eq!(all.len(), 1); 93 assert_eq!(all.len(), 1);