diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 684 | ||||
-rw-r--r-- | crates/ra_ide/src/mock_analysis.rs | 20 |
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" } | |||
36 | hir = { path = "../ra_hir", package = "ra_hir" } | 36 | hir = { path = "../ra_hir", package = "ra_hir" } |
37 | 37 | ||
38 | [dev-dependencies] | 38 | [dev-dependencies] |
39 | insta = "0.16.0" | ||
40 | expect = { path = "../expect" } | 39 | expect = { 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)] |
283 | mod tests { | 283 | mod 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 | 343 | use core::result::Result::{self, Ok, Err}; | |
386 | fn div(x: i32, y: i32) -> Result<i32, ()> { | 344 | |
387 | if y == 0 { | 345 | fn 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) } | 352 | pub 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 | 357 | use core::result::Result::{self, Ok, Err}; | |
400 | fn div(x: i32, y: i32) -> Result<i32, ()> { | 358 | |
401 | if y == 0 { | 359 | fn 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 | 374 | use core::result::Result::{self, Ok, Err}; | |
416 | fn div<T>(x: T) -> Result<T, i32> { | 375 | |
417 | if x == 0 { | 376 | fn 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) } | 383 | pub 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 | 388 | use core::result::Result::{self, Ok, Err}; | |
430 | fn div<T>(x: T) -> Result<T, i32> { | 389 | |
431 | if x == 0 { | 390 | fn 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 |
405 | use core::result::Result::{self, Ok, Err}; | ||
445 | 406 | ||
446 | type MyResult<T> = Result<T, ()>; | 407 | type MyResult<T> = Result<T, ()>; |
447 | 408 | ||
448 | fn div(x: i32, y: i32) -> MyResult<i32> { | 409 | fn 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 { | 416 | pub 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}; | 421 | use core::result::Result::{self, Ok, Err}; |
461 | 422 | ||
462 | type MyResult<T> = Result<T, ()>; | 423 | type MyResult<T> = Result<T, ()>; |
463 | 424 | ||
464 | fn div(x: i32, y: i32) -> MyResult<i32> { | 425 | fn 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}; | 440 | use core::result::Result::{self, Ok, Err}; |
480 | 441 | ||
481 | fn foo() -> Result<(), i32> { | 442 | fn foo() -> Result<(), i32> { 0 } |
482 | 0<|> | ||
483 | } | ||
484 | 443 | ||
485 | //- /core/lib.rs | 444 | //- /core/lib.rs |
486 | pub mod result { | 445 | pub 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}; | 457 | use core::result::Result::{self, Ok, Err}; |
499 | 458 | ||
500 | enum SomeOtherEnum { | 459 | enum SomeOtherEnum { Ok(i32), Err(String) } |
501 | Ok(i32), | ||
502 | Err(String), | ||
503 | } | ||
504 | 460 | ||
505 | fn foo() -> SomeOtherEnum { | 461 | fn foo() -> SomeOtherEnum { 0 } |
506 | 0<|> | ||
507 | } | ||
508 | 462 | ||
509 | //- /core/lib.rs | 463 | //- /core/lib.rs |
510 | pub mod result { | 464 | pub 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, | 475 | struct TestStruct { one: i32, two: i64 } |
522 | two: i64, | ||
523 | } | ||
524 | 476 | ||
525 | fn test_fn() { | 477 | fn test_fn() { |
526 | let s = TestStruct{}; | 478 | let s = TestStruct {<|>}; |
527 | } | 479 | } |
528 | "; | 480 | "#, |
529 | let after = r" | 481 | r#" |
530 | struct TestStruct { | 482 | struct TestStruct { one: i32, two: i64 } |
531 | one: i32, | ||
532 | two: i64, | ||
533 | } | ||
534 | 483 | ||
535 | fn test_fn() { | 484 | fn 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, | 495 | struct TestStruct { one: i32 } |
547 | } | ||
548 | 496 | ||
549 | impl TestStruct { | 497 | impl TestStruct { |
550 | fn test_fn() { | 498 | fn test_fn() { let s = Self {<|>}; } |
551 | let s = Self {}; | 499 | } |
552 | } | 500 | "#, |
553 | } | 501 | r#" |
554 | "; | 502 | struct TestStruct { one: i32 } |
555 | let after = r" | ||
556 | struct TestStruct { | ||
557 | one: i32, | ||
558 | } | ||
559 | 503 | ||
560 | impl TestStruct { | 504 | impl 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> } | 515 | enum Expr { |
574 | } | 516 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } |
517 | } | ||
575 | 518 | ||
576 | impl Expr { | 519 | impl 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 { | 526 | enum Expr { |
584 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } | 527 | Bin { lhs: Box<Expr>, rhs: Box<Expr> } |
585 | } | 528 | } |
586 | 529 | ||
587 | impl Expr { | 530 | impl 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, | 543 | struct TestStruct { one: i32, two: i64 } |
601 | two: i64, | ||
602 | } | ||
603 | 544 | ||
604 | fn test_fn() { | 545 | fn 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 { | 550 | struct TestStruct { one: i32, two: i64 } |
610 | one: i32, | ||
611 | two: i64, | ||
612 | } | ||
613 | 551 | ||
614 | fn test_fn() { | 552 | fn 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() {} | 625 | fn some() {} |
693 | fn here() {} | 626 | fn items() {} |
627 | fn here() {} | ||
694 | 628 | ||
695 | macro_rules! id { | 629 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } |
696 | ($($tt:tt)*) => { $($tt)*}; | ||
697 | } | ||
698 | 630 | ||
699 | fn main() { | 631 | fn main() { |
700 | let _x = id![Foo { a: 42 }]; | 632 | let _x = id![Foo { a: <|>42 }]; |
701 | } | 633 | } |
702 | 634 | ||
703 | pub struct Foo { | 635 | pub struct Foo { pub a: i32, pub b: i32 } |
704 | pub a: i32, | 636 | "#, |
705 | pub b: i32, | 637 | r#" |
706 | } | 638 | fn {a:42, b: ()} {} |
707 | ", | 639 | fn items() {} |
640 | fn here() {} | ||
641 | |||
642 | macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } | ||
643 | |||
644 | fn main() { | ||
645 | let _x = id![Foo { a: 42 }]; | ||
646 | } | ||
647 | |||
648 | pub 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; | 657 | use a; |
750 | use a::{c, d::e}; | 658 | use 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 { | 672 | struct A { a: &'static str } |
769 | a: &'static str | 673 | fn 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); | 678 | struct A(usize); |
783 | 679 | fn 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#" |
795 | struct A { | 685 | struct A { a: &'static str } |
796 | a: &'static str | ||
797 | } | ||
798 | |||
799 | fn main() { | 686 | fn 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#" |
807 | struct A { | 692 | struct A { a: &'static str } |
808 | a: &'static str | ||
809 | } | ||
810 | |||
811 | fn main() { | 693 | fn 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#" |
823 | struct A { | 702 | struct A { a: &'static str, b: &'static str } |
824 | a: &'static str, | ||
825 | b: &'static str | ||
826 | } | ||
827 | |||
828 | fn main() { | 703 | fn 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#" |
838 | struct A { | 710 | struct A { a: &'static str, b: &'static str } |
839 | a: &'static str, | ||
840 | b: &'static str | ||
841 | } | ||
842 | |||
843 | fn main() { | 711 | fn 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" |
860 | fn main() { | 724 | fn main() { |
861 | Foo { bar: 3, baz: false}; | 725 | Foo { bar: 3, baz<|>: false}; |
862 | } | 726 | } |
863 | struct Foo { | 727 | struct 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); |