aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/Cargo.toml1
-rw-r--r--crates/ra_ide/src/diagnostics.rs684
-rw-r--r--crates/ra_ide/src/mock_analysis.rs20
3 files changed, 282 insertions, 423 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 5c51828ea..df2fad520 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -36,5 +36,4 @@ ra_ssr = { path = "../ra_ssr" }
36hir = { path = "../ra_hir", package = "ra_hir" } 36hir = { path = "../ra_hir", package = "ra_hir" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39insta = "0.16.0"
40expect = { path = "../expect" } 39expect = { path = "../expect" }
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 46f8c31c7..00f6bb186 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -281,53 +281,21 @@ fn check_struct_shorthand_initialization(
281 281
282#[cfg(test)] 282#[cfg(test)]
283mod tests { 283mod tests {
284 use insta::assert_debug_snapshot;
285 use ra_syntax::SourceFile;
286 use stdx::trim_indent; 284 use stdx::trim_indent;
287 use test_utils::assert_eq_text; 285 use test_utils::assert_eq_text;
288 286
289 use crate::mock_analysis::{analysis_and_position, single_file}; 287 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
290 288 use expect::{expect, Expect};
291 use super::*;
292
293 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
294
295 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
296 let parse = SourceFile::parse(code);
297 let mut diagnostics = Vec::new();
298 for node in parse.tree().syntax().descendants() {
299 func(&mut diagnostics, FileId(0), &node);
300 }
301 assert!(diagnostics.is_empty());
302 }
303
304 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
305 let parse = SourceFile::parse(before);
306 let mut diagnostics = Vec::new();
307 for node in parse.tree().syntax().descendants() {
308 func(&mut diagnostics, FileId(0), &node);
309 }
310 let diagnostic =
311 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
312 let mut fix = diagnostic.fix.unwrap();
313 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
314 let actual = {
315 let mut actual = before.to_string();
316 edit.apply(&mut actual);
317 actual
318 };
319 assert_eq_text!(after, &actual);
320 }
321 289
322 /// Takes a multi-file input fixture with annotated cursor positions, 290 /// Takes a multi-file input fixture with annotated cursor positions,
323 /// and checks that: 291 /// and checks that:
324 /// * a diagnostic is produced 292 /// * a diagnostic is produced
325 /// * this diagnostic touches the input cursor position 293 /// * this diagnostic touches the input cursor position
326 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 294 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
327 fn check_apply_diagnostic_fix_from_position(ra_fixture: &str, after: &str) { 295 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
328 let after = trim_indent(after); 296 let after = trim_indent(ra_fixture_after);
329 297
330 let (analysis, file_position) = analysis_and_position(ra_fixture); 298 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
331 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 299 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
332 let mut fix = diagnostic.fix.unwrap(); 300 let mut fix = diagnostic.fix.unwrap();
333 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 301 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -348,284 +316,251 @@ mod tests {
348 ); 316 );
349 } 317 }
350 318
351 fn check_apply_diagnostic_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
352 let ra_fixture_after = &trim_indent(ra_fixture_after);
353 let (analysis, file_id) = single_file(ra_fixture_before);
354 let before = analysis.file_text(file_id).unwrap();
355 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap();
356 let mut fix = diagnostic.fix.unwrap();
357 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
358 let actual = {
359 let mut actual = before.to_string();
360 edit.apply(&mut actual);
361 actual
362 };
363 assert_eq_text!(ra_fixture_after, &actual);
364 }
365
366 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 319 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
367 /// apply to the file containing the cursor. 320 /// apply to the file containing the cursor.
368 fn check_no_diagnostic_for_target_file(ra_fixture: &str) { 321 fn check_no_diagnostics(ra_fixture: &str) {
369 let (analysis, file_position) = analysis_and_position(ra_fixture); 322 let mock = MockAnalysis::with_files(ra_fixture);
370 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 323 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
371 assert_eq!(diagnostics.len(), 0); 324 let analysis = mock.analysis();
372 } 325 let diagnostics = files
373 326 .into_iter()
374 fn check_no_diagnostic(ra_fixture: &str) { 327 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
328 .collect::<Vec<_>>();
329 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
330 }
331
332 fn check_expect(ra_fixture: &str, expect: Expect) {
375 let (analysis, file_id) = single_file(ra_fixture); 333 let (analysis, file_id) = single_file(ra_fixture);
376 let diagnostics = analysis.diagnostics(file_id).unwrap(); 334 let diagnostics = analysis.diagnostics(file_id).unwrap();
377 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 335 expect.assert_debug_eq(&diagnostics)
378 } 336 }
379 337
380 #[test] 338 #[test]
381 fn test_wrap_return_type() { 339 fn test_wrap_return_type() {
382 let before = r#" 340 check_fix(
383 //- /main.rs 341 r#"
384 use core::result::Result::{self, Ok, Err}; 342//- /main.rs
385 343use core::result::Result::{self, Ok, Err};
386 fn div(x: i32, y: i32) -> Result<i32, ()> { 344
387 if y == 0 { 345fn div(x: i32, y: i32) -> Result<i32, ()> {
388 return Err(()); 346 if y == 0 {
389 } 347 return Err(());
390 x / y<|> 348 }
391 } 349 x / y<|>
392 //- /core/lib.rs 350}
393 pub mod result { 351//- /core/lib.rs
394 pub enum Result<T, E> { Ok(T), Err(E) } 352pub mod result {
395 } 353 pub enum Result<T, E> { Ok(T), Err(E) }
396 "#; 354}
397 let after = r#" 355"#,
398 use core::result::Result::{self, Ok, Err}; 356 r#"
399 357use core::result::Result::{self, Ok, Err};
400 fn div(x: i32, y: i32) -> Result<i32, ()> { 358
401 if y == 0 { 359fn div(x: i32, y: i32) -> Result<i32, ()> {
402 return Err(()); 360 if y == 0 {
403 } 361 return Err(());
404 Ok(x / y) 362 }
405 } 363 Ok(x / y)
406 "#; 364}
407 check_apply_diagnostic_fix_from_position(before, after); 365"#,
366 );
408 } 367 }
409 368
410 #[test] 369 #[test]
411 fn test_wrap_return_type_handles_generic_functions() { 370 fn test_wrap_return_type_handles_generic_functions() {
412 let before = r#" 371 check_fix(
413 //- /main.rs 372 r#"
414 use core::result::Result::{self, Ok, Err}; 373//- /main.rs
415 374use core::result::Result::{self, Ok, Err};
416 fn div<T>(x: T) -> Result<T, i32> { 375
417 if x == 0 { 376fn div<T>(x: T) -> Result<T, i32> {
418 return Err(7); 377 if x == 0 {
419 } 378 return Err(7);
420 <|>x 379 }
421 } 380 <|>x
422 //- /core/lib.rs 381}
423 pub mod result { 382//- /core/lib.rs
424 pub enum Result<T, E> { Ok(T), Err(E) } 383pub mod result {
425 } 384 pub enum Result<T, E> { Ok(T), Err(E) }
426 "#; 385}
427 let after = r#" 386"#,
428 use core::result::Result::{self, Ok, Err}; 387 r#"
429 388use core::result::Result::{self, Ok, Err};
430 fn div<T>(x: T) -> Result<T, i32> { 389
431 if x == 0 { 390fn div<T>(x: T) -> Result<T, i32> {
432 return Err(7); 391 if x == 0 {
433 } 392 return Err(7);
434 Ok(x) 393 }
435 } 394 Ok(x)
436 "#; 395}
437 check_apply_diagnostic_fix_from_position(before, after); 396"#,
397 );
438 } 398 }
439 399
440 #[test] 400 #[test]
441 fn test_wrap_return_type_handles_type_aliases() { 401 fn test_wrap_return_type_handles_type_aliases() {
442 let before = r#" 402 check_fix(
443 //- /main.rs 403 r#"
444 use core::result::Result::{self, Ok, Err}; 404//- /main.rs
405use core::result::Result::{self, Ok, Err};
445 406
446 type MyResult<T> = Result<T, ()>; 407type MyResult<T> = Result<T, ()>;
447 408
448 fn div(x: i32, y: i32) -> MyResult<i32> { 409fn div(x: i32, y: i32) -> MyResult<i32> {
449 if y == 0 { 410 if y == 0 {
450 return Err(()); 411 return Err(());
451 } 412 }
452 x <|>/ y 413 x <|>/ y
453 } 414}
454 //- /core/lib.rs 415//- /core/lib.rs
455 pub mod result { 416pub mod result {
456 pub enum Result<T, E> { Ok(T), Err(E) } 417 pub enum Result<T, E> { Ok(T), Err(E) }
457 } 418}
458 "#; 419"#,
459 let after = r#" 420 r#"
460 use core::result::Result::{self, Ok, Err}; 421use core::result::Result::{self, Ok, Err};
461 422
462 type MyResult<T> = Result<T, ()>; 423type MyResult<T> = Result<T, ()>;
463 424
464 fn div(x: i32, y: i32) -> MyResult<i32> { 425fn div(x: i32, y: i32) -> MyResult<i32> {
465 if y == 0 { 426 if y == 0 {
466 return Err(()); 427 return Err(());
467 } 428 }
468 Ok(x / y) 429 Ok(x / y)
469 } 430}
470 "#; 431"#,
471 check_apply_diagnostic_fix_from_position(before, after); 432 );
472 } 433 }
473 434
474 #[test] 435 #[test]
475 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 436 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
476 check_no_diagnostic_for_target_file( 437 check_no_diagnostics(
477 r" 438 r#"
478 //- /main.rs 439//- /main.rs
479 use core::result::Result::{self, Ok, Err}; 440use core::result::Result::{self, Ok, Err};
480 441
481 fn foo() -> Result<(), i32> { 442fn foo() -> Result<(), i32> { 0 }
482 0<|>
483 }
484 443
485 //- /core/lib.rs 444//- /core/lib.rs
486 pub mod result { 445pub mod result {
487 pub enum Result<T, E> { Ok(T), Err(E) } 446 pub enum Result<T, E> { Ok(T), Err(E) }
488 } 447}
489 ", 448"#,
490 ); 449 );
491 } 450 }
492 451
493 #[test] 452 #[test]
494 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 453 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
495 check_no_diagnostic_for_target_file( 454 check_no_diagnostics(
496 r" 455 r#"
497 //- /main.rs 456//- /main.rs
498 use core::result::Result::{self, Ok, Err}; 457use core::result::Result::{self, Ok, Err};
499 458
500 enum SomeOtherEnum { 459enum SomeOtherEnum { Ok(i32), Err(String) }
501 Ok(i32),
502 Err(String),
503 }
504 460
505 fn foo() -> SomeOtherEnum { 461fn foo() -> SomeOtherEnum { 0 }
506 0<|>
507 }
508 462
509 //- /core/lib.rs 463//- /core/lib.rs
510 pub mod result { 464pub mod result {
511 pub enum Result<T, E> { Ok(T), Err(E) } 465 pub enum Result<T, E> { Ok(T), Err(E) }
512 } 466}
513 ", 467"#,
514 ); 468 );
515 } 469 }
516 470
517 #[test] 471 #[test]
518 fn test_fill_struct_fields_empty() { 472 fn test_fill_struct_fields_empty() {
519 let before = r" 473 check_fix(
520 struct TestStruct { 474 r#"
521 one: i32, 475struct TestStruct { one: i32, two: i64 }
522 two: i64,
523 }
524 476
525 fn test_fn() { 477fn test_fn() {
526 let s = TestStruct{}; 478 let s = TestStruct {<|>};
527 } 479}
528 "; 480"#,
529 let after = r" 481 r#"
530 struct TestStruct { 482struct TestStruct { one: i32, two: i64 }
531 one: i32,
532 two: i64,
533 }
534 483
535 fn test_fn() { 484fn test_fn() {
536 let s = TestStruct{ one: (), two: ()}; 485 let s = TestStruct { one: (), two: ()};
537 } 486}
538 "; 487"#,
539 check_apply_diagnostic_fix(before, after); 488 );
540 } 489 }
541 490
542 #[test] 491 #[test]
543 fn test_fill_struct_fields_self() { 492 fn test_fill_struct_fields_self() {
544 let before = r" 493 check_fix(
545 struct TestStruct { 494 r#"
546 one: i32, 495struct TestStruct { one: i32 }
547 }
548 496
549 impl TestStruct { 497impl TestStruct {
550 fn test_fn() { 498 fn test_fn() { let s = Self {<|>}; }
551 let s = Self {}; 499}
552 } 500"#,
553 } 501 r#"
554 "; 502struct TestStruct { one: i32 }
555 let after = r"
556 struct TestStruct {
557 one: i32,
558 }
559 503
560 impl TestStruct { 504impl TestStruct {
561 fn test_fn() { 505 fn test_fn() { let s = Self { one: ()}; }
562 let s = Self { one: ()}; 506}
563 } 507"#,
564 } 508 );
565 ";
566 check_apply_diagnostic_fix(before, after);
567 } 509 }
568 510
569 #[test] 511 #[test]
570 fn test_fill_struct_fields_enum() { 512 fn test_fill_struct_fields_enum() {
571 let before = r" 513 check_fix(
572 enum Expr { 514 r#"
573 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 515enum Expr {
574 } 516 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
517}
575 518
576 impl Expr { 519impl Expr {
577 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 520 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
578 Expr::Bin { } 521 Expr::Bin {<|> }
579 } 522 }
580 } 523}
581 "; 524"#,
582 let after = r" 525 r#"
583 enum Expr { 526enum Expr {
584 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 527 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
585 } 528}
586 529
587 impl Expr { 530impl Expr {
588 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 531 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
589 Expr::Bin { lhs: (), rhs: () } 532 Expr::Bin { lhs: (), rhs: () }
590 } 533 }
591 } 534}
592 "; 535"#,
593 check_apply_diagnostic_fix(before, after); 536 );
594 } 537 }
595 538
596 #[test] 539 #[test]
597 fn test_fill_struct_fields_partial() { 540 fn test_fill_struct_fields_partial() {
598 let before = r" 541 check_fix(
599 struct TestStruct { 542 r#"
600 one: i32, 543struct TestStruct { one: i32, two: i64 }
601 two: i64,
602 }
603 544
604 fn test_fn() { 545fn test_fn() {
605 let s = TestStruct{ two: 2 }; 546 let s = TestStruct{ two: 2<|> };
606 } 547}
607 "; 548"#,
608 let after = r" 549 r"
609 struct TestStruct { 550struct TestStruct { one: i32, two: i64 }
610 one: i32,
611 two: i64,
612 }
613 551
614 fn test_fn() { 552fn test_fn() {
615 let s = TestStruct{ two: 2, one: () }; 553 let s = TestStruct{ two: 2, one: () };
616 } 554}
617 "; 555",
618 check_apply_diagnostic_fix(before, after); 556 );
619 } 557 }
620 558
621 #[test] 559 #[test]
622 fn test_fill_struct_fields_no_diagnostic() { 560 fn test_fill_struct_fields_no_diagnostic() {
623 check_no_diagnostic( 561 check_no_diagnostics(
624 r" 562 r"
625 struct TestStruct { 563 struct TestStruct { one: i32, two: i64 }
626 one: i32,
627 two: i64,
628 }
629 564
630 fn test_fn() { 565 fn test_fn() {
631 let one = 1; 566 let one = 1;
@@ -637,12 +572,9 @@ mod tests {
637 572
638 #[test] 573 #[test]
639 fn test_fill_struct_fields_no_diagnostic_on_spread() { 574 fn test_fill_struct_fields_no_diagnostic_on_spread() {
640 check_no_diagnostic( 575 check_no_diagnostics(
641 r" 576 r"
642 struct TestStruct { 577 struct TestStruct { one: i32, two: i64 }
643 one: i32,
644 two: i64,
645 }
646 578
647 fn test_fn() { 579 fn test_fn() {
648 let one = 1; 580 let one = 1;
@@ -654,211 +586,143 @@ mod tests {
654 586
655 #[test] 587 #[test]
656 fn test_unresolved_module_diagnostic() { 588 fn test_unresolved_module_diagnostic() {
657 let (analysis, file_id) = single_file("mod foo;"); 589 check_expect(
658 let diagnostics = analysis.diagnostics(file_id).unwrap(); 590 r#"mod foo;"#,
659 assert_debug_snapshot!(diagnostics, @r###" 591 expect![[r#"
660 [ 592 [
661 Diagnostic { 593 Diagnostic {
662 message: "unresolved module", 594 message: "unresolved module",
663 range: 0..8, 595 range: 0..8,
664 severity: Error, 596 severity: Error,
665 fix: Some( 597 fix: Some(
666 Fix { 598 Fix {
667 label: "Create module", 599 label: "Create module",
668 source_change: SourceChange { 600 source_change: SourceChange {
669 source_file_edits: [], 601 source_file_edits: [],
670 file_system_edits: [ 602 file_system_edits: [
671 CreateFile { 603 CreateFile {
672 anchor: FileId( 604 anchor: FileId(
673 1, 605 1,
674 ), 606 ),
675 dst: "foo.rs", 607 dst: "foo.rs",
608 },
609 ],
610 is_snippet: false,
676 }, 611 },
677 ], 612 },
678 is_snippet: false, 613 ),
679 },
680 }, 614 },
681 ), 615 ]
682 }, 616 "#]],
683 ] 617 );
684 "###);
685 } 618 }
686 619
687 #[test] 620 #[test]
688 fn range_mapping_out_of_macros() { 621 fn range_mapping_out_of_macros() {
689 let (analysis, file_id) = single_file( 622 // FIXME: this is very wrong, but somewhat tricky to fix.
690 r" 623 check_fix(
691 fn some() {} 624 r#"
692 fn items() {} 625fn some() {}
693 fn here() {} 626fn items() {}
627fn here() {}
694 628
695 macro_rules! id { 629macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
696 ($($tt:tt)*) => { $($tt)*};
697 }
698 630
699 fn main() { 631fn main() {
700 let _x = id![Foo { a: 42 }]; 632 let _x = id![Foo { a: <|>42 }];
701 } 633}
702 634
703 pub struct Foo { 635pub struct Foo { pub a: i32, pub b: i32 }
704 pub a: i32, 636"#,
705 pub b: i32, 637 r#"
706 } 638fn {a:42, b: ()} {}
707 ", 639fn items() {}
640fn here() {}
641
642macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
643
644fn main() {
645 let _x = id![Foo { a: 42 }];
646}
647
648pub struct Foo { pub a: i32, pub b: i32 }
649"#,
708 ); 650 );
709 let diagnostics = analysis.diagnostics(file_id).unwrap();
710 assert_debug_snapshot!(diagnostics, @r###"
711 [
712 Diagnostic {
713 message: "Missing structure fields:\n- b\n",
714 range: 127..136,
715 severity: Error,
716 fix: Some(
717 Fix {
718 label: "Fill struct fields",
719 source_change: SourceChange {
720 source_file_edits: [
721 SourceFileEdit {
722 file_id: FileId(
723 1,
724 ),
725 edit: TextEdit {
726 indels: [
727 Indel {
728 insert: "{a:42, b: ()}",
729 delete: 3..9,
730 },
731 ],
732 },
733 },
734 ],
735 file_system_edits: [],
736 is_snippet: false,
737 },
738 },
739 ),
740 },
741 ]
742 "###);
743 } 651 }
744 652
745 #[test] 653 #[test]
746 fn test_check_unnecessary_braces_in_use_statement() { 654 fn test_check_unnecessary_braces_in_use_statement() {
747 check_not_applicable( 655 check_no_diagnostics(
748 " 656 r#"
749 use a; 657use a;
750 use a::{c, d::e}; 658use a::{c, d::e};
751 ", 659"#,
752 check_unnecessary_braces_in_use_statement,
753 );
754 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
755 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
756 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
757 check_apply(
758 "use a::{c, d::{e}};",
759 "use a::{c, d::e};",
760 check_unnecessary_braces_in_use_statement,
761 ); 660 );
661 check_fix(r#"use {<|>b};"#, r#"use b;"#);
662 check_fix(r#"use {b<|>};"#, r#"use b;"#);
663 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
664 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
665 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
762 } 666 }
763 667
764 #[test] 668 #[test]
765 fn test_check_struct_shorthand_initialization() { 669 fn test_check_struct_shorthand_initialization() {
766 check_not_applicable( 670 check_no_diagnostics(
767 r#" 671 r#"
768 struct A { 672struct A { a: &'static str }
769 a: &'static str 673fn main() { A { a: "hello" } }
770 } 674"#,
771
772 fn main() {
773 A {
774 a: "hello"
775 }
776 }
777 "#,
778 check_struct_shorthand_initialization,
779 ); 675 );
780 check_not_applicable( 676 check_no_diagnostics(
781 r#" 677 r#"
782 struct A(usize); 678struct A(usize);
783 679fn main() { A { 0: 0 } }
784 fn main() { 680"#,
785 A {
786 0: 0
787 }
788 }
789 "#,
790 check_struct_shorthand_initialization,
791 ); 681 );
792 682
793 check_apply( 683 check_fix(
794 r#" 684 r#"
795struct A { 685struct A { a: &'static str }
796 a: &'static str
797}
798
799fn main() { 686fn main() {
800 let a = "haha"; 687 let a = "haha";
801 A { 688 A { a<|>: a }
802 a: a
803 }
804} 689}
805 "#, 690"#,
806 r#" 691 r#"
807struct A { 692struct A { a: &'static str }
808 a: &'static str
809}
810
811fn main() { 693fn main() {
812 let a = "haha"; 694 let a = "haha";
813 A { 695 A { a }
814 a
815 }
816} 696}
817 "#, 697"#,
818 check_struct_shorthand_initialization,
819 ); 698 );
820 699
821 check_apply( 700 check_fix(
822 r#" 701 r#"
823struct A { 702struct A { a: &'static str, b: &'static str }
824 a: &'static str,
825 b: &'static str
826}
827
828fn main() { 703fn main() {
829 let a = "haha"; 704 let a = "haha";
830 let b = "bb"; 705 let b = "bb";
831 A { 706 A { a<|>: a, b }
832 a: a,
833 b
834 }
835} 707}
836 "#, 708"#,
837 r#" 709 r#"
838struct A { 710struct A { a: &'static str, b: &'static str }
839 a: &'static str,
840 b: &'static str
841}
842
843fn main() { 711fn main() {
844 let a = "haha"; 712 let a = "haha";
845 let b = "bb"; 713 let b = "bb";
846 A { 714 A { a, b }
847 a,
848 b
849 }
850} 715}
851 "#, 716"#,
852 check_struct_shorthand_initialization,
853 ); 717 );
854 } 718 }
855 719
856 #[test] 720 #[test]
857 fn test_add_field_from_usage() { 721 fn test_add_field_from_usage() {
858 check_apply_diagnostic_fix( 722 check_fix(
859 r" 723 r"
860fn main() { 724fn main() {
861 Foo { bar: 3, baz: false}; 725 Foo { bar: 3, baz<|>: false};
862} 726}
863struct Foo { 727struct Foo {
864 bar: i32 728 bar: i32
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);