aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/auto_import.rs96
-rw-r--r--crates/ra_hir/src/ty.rs4
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_nested_generics_crash.snap12
-rw-r--r--crates/ra_hir/src/ty/tests.rs19
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 @@
1use hir::db::HirDatabase; 1use hir::db::HirDatabase;
2use ra_syntax::{ 2use 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};
6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; 6use 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
483fn 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
483pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 501pub(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 "
559std::fmt<|>::Debug
560 ",
561 "
562use std::fmt;
563
564fmt<|>::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 "
772use 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 "
782mod foo {
783 mod bar {
784 std::fmt::Debug<|>
785 }
786}
787 ",
788 "
789mod 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---
2created: "2019-02-11T21:59:04.302375838Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&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]
723fn infer_nested_generics_crash() {
724 // another crash found typechecking rustc
725 check_inference(
726 "infer_nested_generics_crash",
727 r#"
728struct Canonical<V> {
729 value: V,
730}
731struct QueryResponse<V> {
732 value: V,
733}
734fn test<R>(query_response: Canonical<QueryResponse<R>>) {
735 &query_response.value;
736}
737"#,
738 );
739}
740
722fn infer(content: &str) -> String { 741fn 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);