diff options
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 96 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/snapshots/tests__infer_nested_generics_crash.snap | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 19 |
4 files changed, 115 insertions, 16 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index b251c9369..52c2a0b2b 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast, AstNode, SyntaxNode, Direction, TextRange, | 3 | ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, |
4 | SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } | 4 | SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } |
5 | }; | 5 | }; |
6 | use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; | 6 | use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; |
@@ -345,9 +345,9 @@ fn best_action_for_target<'b, 'a: 'b>( | |||
345 | match best_action { | 345 | match best_action { |
346 | Some(action) => return action, | 346 | Some(action) => return action, |
347 | None => { | 347 | None => { |
348 | // We have no action we no use item was found in container so we find | 348 | // We have no action and no UseItem was found in container so we find |
349 | // another item and we use it as anchor. | 349 | // another item and we use it as anchor. |
350 | // If there are not items, we choose the target path itself as anchor. | 350 | // If there are no items, we choose the target path itself as anchor. |
351 | let anchor = container | 351 | let anchor = container |
352 | .children() | 352 | .children() |
353 | .find_map(ast::ModuleItem::cast) | 353 | .find_map(ast::ModuleItem::cast) |
@@ -480,6 +480,24 @@ fn make_assist_add_nested_import( | |||
480 | } | 480 | } |
481 | } | 481 | } |
482 | 482 | ||
483 | fn apply_auto_import<'a>( | ||
484 | container: &SyntaxNode, | ||
485 | path: &ast::Path, | ||
486 | target: &[&'a ast::PathSegment], | ||
487 | edit: &mut AssistBuilder, | ||
488 | ) { | ||
489 | let action = best_action_for_target(container, path, target); | ||
490 | make_assist(&action, target, edit); | ||
491 | if let (Some(first), Some(last)) = (target.first(), target.last()) { | ||
492 | // Here we are assuming the assist will provide a correct use statement | ||
493 | // so we can delete the path qualifier | ||
494 | edit.delete(TextRange::from_to( | ||
495 | first.syntax().range().start(), | ||
496 | last.syntax().range().start(), | ||
497 | )); | ||
498 | } | ||
499 | } | ||
500 | |||
483 | pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 501 | pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
484 | let node = ctx.covering_node(); | 502 | let node = ctx.covering_node(); |
485 | let current_file = node.ancestors().find_map(ast::SourceFile::cast)?; | 503 | let current_file = node.ancestors().find_map(ast::SourceFile::cast)?; |
@@ -495,18 +513,20 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist | |||
495 | return None; | 513 | return None; |
496 | } | 514 | } |
497 | 515 | ||
498 | ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| { | 516 | if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) { |
499 | let action = best_action_for_target(current_file.syntax(), path, &segments); | 517 | if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) { |
500 | make_assist(&action, segments.as_slice(), edit); | 518 | ctx.add_action( |
501 | if let Some(last_segment) = path.segment() { | 519 | format!("import {} in mod {}", fmt_segments(&segments), name.text()), |
502 | // Here we are assuming the assist will provide a correct use statement | 520 | |edit| { |
503 | // so we can delete the path qualifier | 521 | apply_auto_import(item_list.syntax(), path, &segments, edit); |
504 | edit.delete(TextRange::from_to( | 522 | }, |
505 | path.syntax().range().start(), | 523 | ); |
506 | last_segment.syntax().range().start(), | ||
507 | )); | ||
508 | } | 524 | } |
509 | }); | 525 | } else { |
526 | ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| { | ||
527 | apply_auto_import(current_file.syntax(), path, &segments, edit); | ||
528 | }); | ||
529 | } | ||
510 | 530 | ||
511 | ctx.build() | 531 | ctx.build() |
512 | } | 532 | } |
@@ -532,6 +552,21 @@ Debug<|> | |||
532 | } | 552 | } |
533 | 553 | ||
534 | #[test] | 554 | #[test] |
555 | fn test_auto_import_file_add_use_no_anchor_2seg() { | ||
556 | check_assist( | ||
557 | auto_import, | ||
558 | " | ||
559 | std::fmt<|>::Debug | ||
560 | ", | ||
561 | " | ||
562 | use std::fmt; | ||
563 | |||
564 | fmt<|>::Debug | ||
565 | ", | ||
566 | ); | ||
567 | } | ||
568 | |||
569 | #[test] | ||
535 | fn test_auto_import_file_add_use() { | 570 | fn test_auto_import_file_add_use() { |
536 | check_assist( | 571 | check_assist( |
537 | auto_import, | 572 | auto_import, |
@@ -728,4 +763,37 @@ impl foo<|> for Foo { | |||
728 | ", | 763 | ", |
729 | ); | 764 | ); |
730 | } | 765 | } |
766 | |||
767 | #[test] | ||
768 | fn test_auto_import_not_applicable_in_use() { | ||
769 | check_assist_not_applicable( | ||
770 | auto_import, | ||
771 | " | ||
772 | use std::fmt<|>; | ||
773 | ", | ||
774 | ); | ||
775 | } | ||
776 | |||
777 | #[test] | ||
778 | fn test_auto_import_file_add_use_no_anchor_in_mod_mod() { | ||
779 | check_assist( | ||
780 | auto_import, | ||
781 | " | ||
782 | mod foo { | ||
783 | mod bar { | ||
784 | std::fmt::Debug<|> | ||
785 | } | ||
786 | } | ||
787 | ", | ||
788 | " | ||
789 | mod foo { | ||
790 | mod bar { | ||
791 | use std::fmt::Debug; | ||
792 | |||
793 | Debug<|> | ||
794 | } | ||
795 | } | ||
796 | ", | ||
797 | ); | ||
798 | } | ||
731 | } | 799 | } |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 2dc1de41a..f28a7e731 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -450,7 +450,6 @@ impl Ty { | |||
450 | } | 450 | } |
451 | 451 | ||
452 | pub fn walk(&self, f: &mut impl FnMut(&Ty)) { | 452 | pub fn walk(&self, f: &mut impl FnMut(&Ty)) { |
453 | f(self); | ||
454 | match self { | 453 | match self { |
455 | Ty::Slice(t) | Ty::Array(t) => t.walk(f), | 454 | Ty::Slice(t) | Ty::Array(t) => t.walk(f), |
456 | Ty::RawPtr(t, _) => t.walk(f), | 455 | Ty::RawPtr(t, _) => t.walk(f), |
@@ -490,10 +489,10 @@ impl Ty { | |||
490 | | Ty::Infer(_) | 489 | | Ty::Infer(_) |
491 | | Ty::Unknown => {} | 490 | | Ty::Unknown => {} |
492 | } | 491 | } |
492 | f(self); | ||
493 | } | 493 | } |
494 | 494 | ||
495 | fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { | 495 | fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { |
496 | f(self); | ||
497 | match self { | 496 | match self { |
498 | Ty::Slice(t) | Ty::Array(t) => Arc::make_mut(t).walk_mut(f), | 497 | Ty::Slice(t) | Ty::Array(t) => Arc::make_mut(t).walk_mut(f), |
499 | Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f), | 498 | Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f), |
@@ -544,6 +543,7 @@ impl Ty { | |||
544 | | Ty::Infer(_) | 543 | | Ty::Infer(_) |
545 | | Ty::Unknown => {} | 544 | | Ty::Unknown => {} |
546 | } | 545 | } |
546 | f(self); | ||
547 | } | 547 | } |
548 | 548 | ||
549 | fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty { | 549 | fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty { |
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_nested_generics_crash.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_nested_generics_crash.snap new file mode 100644 index 000000000..209454a91 --- /dev/null +++ b/crates/ra_hir/src/ty/snapshots/tests__infer_nested_generics_crash.snap | |||
@@ -0,0 +1,12 @@ | |||
1 | --- | ||
2 | created: "2019-02-11T21:59:04.302375838Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_hir/src/ty/tests.rs | ||
5 | expression: "&result" | ||
6 | --- | ||
7 | [92; 106) 'query_response': Canonical<QueryResponse<R>> | ||
8 | [137; 167) '{ ...lue; }': () | ||
9 | [143; 164) '&query....value': &QueryResponse<R> | ||
10 | [144; 158) 'query_response': Canonical<QueryResponse<R>> | ||
11 | [144; 164) 'query_....value': QueryResponse<R> | ||
12 | |||
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e64fd2749..203f1fe4d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -719,6 +719,25 @@ fn extra_compiler_flags() { | |||
719 | ); | 719 | ); |
720 | } | 720 | } |
721 | 721 | ||
722 | #[test] | ||
723 | fn infer_nested_generics_crash() { | ||
724 | // another crash found typechecking rustc | ||
725 | check_inference( | ||
726 | "infer_nested_generics_crash", | ||
727 | r#" | ||
728 | struct Canonical<V> { | ||
729 | value: V, | ||
730 | } | ||
731 | struct QueryResponse<V> { | ||
732 | value: V, | ||
733 | } | ||
734 | fn test<R>(query_response: Canonical<QueryResponse<R>>) { | ||
735 | &query_response.value; | ||
736 | } | ||
737 | "#, | ||
738 | ); | ||
739 | } | ||
740 | |||
722 | fn infer(content: &str) -> String { | 741 | fn infer(content: &str) -> String { |
723 | let (db, _, file_id) = MockDatabase::with_single_file(content); | 742 | let (db, _, file_id) = MockDatabase::with_single_file(content); |
724 | let source_file = db.parse(file_id); | 743 | let source_file = db.parse(file_id); |