aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r--crates/ide_assists/src/assist_context.rs3
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs4
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs11
-rw-r--r--crates/ide_assists/src/handlers/convert_comment_block.rs4
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs352
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs10
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs169
-rw-r--r--crates/ide_assists/src/handlers/extract_type_alias.rs68
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs33
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/fix_visibility.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_is_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_projection_method.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs8
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_new.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_setter.rs2
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs17
-rw-r--r--crates/ide_assists/src/handlers/remove_dbg.rs2
-rw-r--r--crates/ide_assists/src/handlers/remove_unused_param.rs30
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs2
-rw-r--r--crates/ide_assists/src/lib.rs8
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/utils.rs2
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs2
29 files changed, 641 insertions, 114 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 8fc40f9bd..36a2bf89a 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -291,8 +291,7 @@ impl AssistBuilder {
291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 291 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
292 } 292 }
293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { 293 pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
294 let file_system_edit = 294 let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() };
295 FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() };
296 self.source_change.push_file_system_edit(file_system_edit); 295 self.source_change.push_file_system_edit(file_system_edit);
297 } 296 }
298 297
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 5c936a510..c93959e66 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -78,12 +78,12 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
78 terms.sort_by_key(|t| t.syntax().text_range().start()); 78 terms.sort_by_key(|t| t.syntax().text_range().start());
79 let mut terms = VecDeque::from(terms); 79 let mut terms = VecDeque::from(terms);
80 80
81 let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent)); 81 let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
82 82
83 let neg_expr = paren_expr 83 let neg_expr = paren_expr
84 .clone() 84 .clone()
85 .and_then(|paren_expr| paren_expr.syntax().parent()) 85 .and_then(|paren_expr| paren_expr.syntax().parent())
86 .and_then(|parent| ast::PrefixExpr::cast(parent)) 86 .and_then(ast::PrefixExpr::cast)
87 .and_then(|prefix_expr| { 87 .and_then(|prefix_expr| {
88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not { 88 if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
89 Some(prefix_expr) 89 Some(prefix_expr)
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs
index d7e39b2ae..ed936667f 100644
--- a/crates/ide_assists/src/handlers/change_visibility.rs
+++ b/crates/ide_assists/src/handlers/change_visibility.rs
@@ -1,7 +1,9 @@
1use syntax::{ 1use syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, 4 SyntaxKind::{
5 CONST, ENUM, FN, MACRO_DEF, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, USE, VISIBILITY,
6 },
5 T, 7 T,
6}; 8};
7 9
@@ -37,12 +39,15 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
37 | T![enum] 39 | T![enum]
38 | T![trait] 40 | T![trait]
39 | T![type] 41 | T![type]
42 | T![use]
43 | T![macro]
40 ) 44 )
41 }); 45 });
42 46
43 let (offset, target) = if let Some(keyword) = item_keyword { 47 let (offset, target) = if let Some(keyword) = item_keyword {
44 let parent = keyword.parent()?; 48 let parent = keyword.parent()?;
45 let def_kws = vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT]; 49 let def_kws =
50 vec![CONST, STATIC, TYPE_ALIAS, FN, MODULE, STRUCT, ENUM, TRAIT, USE, MACRO_DEF];
46 // Parent is not a definition, can't add visibility 51 // Parent is not a definition, can't add visibility
47 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { 52 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
48 return None; 53 return None;
@@ -122,6 +127,8 @@ mod tests {
122 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}"); 127 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}");
123 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}"); 128 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}");
124 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}"); 129 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}");
130 check_assist(change_visibility, "$0macro foo() {}", "pub(crate) macro foo() {}");
131 check_assist(change_visibility, "$0use foo;", "pub(crate) use foo;");
125 } 132 }
126 133
127 #[test] 134 #[test]
diff --git a/crates/ide_assists/src/handlers/convert_comment_block.rs b/crates/ide_assists/src/handlers/convert_comment_block.rs
index d202a85f9..749e8685b 100644
--- a/crates/ide_assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide_assists/src/handlers/convert_comment_block.rs
@@ -88,7 +88,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> {
88 // We pick a single indentation level for the whole block comment based on the 88 // We pick a single indentation level for the whole block comment based on the
89 // comment where the assist was invoked. This will be prepended to the 89 // comment where the assist was invoked. This will be prepended to the
90 // contents of each line comment when they're put into the block comment. 90 // contents of each line comment when they're put into the block comment.
91 let indentation = IndentLevel::from_token(&comment.syntax()); 91 let indentation = IndentLevel::from_token(comment.syntax());
92 92
93 let block_comment_body = 93 let block_comment_body =
94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n"); 94 comments.into_iter().map(|c| line_comment_text(indentation, c)).join("\n");
@@ -167,7 +167,7 @@ fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String {
167 if contents.is_empty() { 167 if contents.is_empty() {
168 contents.to_owned() 168 contents.to_owned()
169 } else { 169 } else {
170 indentation.to_string() + &contents 170 indentation.to_string() + contents
171 } 171 }
172} 172}
173 173
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 70949ca35..fc5a17f05 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use ide_db::defs::{Definition, NameRefClass}; 2use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ 3use syntax::{
3 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, 4 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind,
8 9
9// Assist: convert_tuple_struct_to_named_struct 10// Assist: convert_tuple_struct_to_named_struct
10// 11//
11// Converts tuple struct to struct with named fields. 12// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
12// 13//
13// ``` 14// ```
14// struct Point$0(f32, f32); 15// struct Point$0(f32, f32);
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
49 acc: &mut Assists, 50 acc: &mut Assists,
50 ctx: &AssistContext, 51 ctx: &AssistContext,
51) -> Option<()> { 52) -> Option<()> {
52 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 53 let strukt = ctx
53 let tuple_fields = match strukt.field_list()? { 54 .find_node_at_offset::<ast::Struct>()
55 .map(Either::Left)
56 .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
57 let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
58 let tuple_fields = match field_list {
54 ast::FieldList::TupleFieldList(it) => it, 59 ast::FieldList::TupleFieldList(it) => it,
55 ast::FieldList::RecordFieldList(_) => return None, 60 ast::FieldList::RecordFieldList(_) => return None,
56 }; 61 };
57 let strukt_def = ctx.sema.to_def(&strukt)?; 62 let strukt_def = match &strukt {
63 Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
64 Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
65 };
66 let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
58 67
59 let target = strukt.syntax().text_range();
60 acc.add( 68 acc.add(
61 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), 69 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
62 "Convert to named struct", 70 "Convert to named struct",
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
73fn edit_struct_def( 81fn edit_struct_def(
74 ctx: &AssistContext, 82 ctx: &AssistContext,
75 edit: &mut AssistBuilder, 83 edit: &mut AssistBuilder,
76 strukt: &ast::Struct, 84 strukt: &Either<ast::Struct, ast::Variant>,
77 tuple_fields: ast::TupleFieldList, 85 tuple_fields: ast::TupleFieldList,
78 names: Vec<ast::Name>, 86 names: Vec<ast::Name>,
79) { 87) {
@@ -86,27 +94,40 @@ fn edit_struct_def(
86 94
87 edit.edit_file(ctx.frange.file_id); 95 edit.edit_file(ctx.frange.file_id);
88 96
89 if let Some(w) = strukt.where_clause() { 97 if let Either::Left(strukt) = strukt {
90 edit.delete(w.syntax().text_range()); 98 if let Some(w) = strukt.where_clause() {
91 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 99 edit.delete(w.syntax().text_range());
92 edit.insert(tuple_fields_text_range.start(), w.syntax().text()); 100 edit.insert(
93 edit.insert(tuple_fields_text_range.start(), ","); 101 tuple_fields_text_range.start(),
94 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 102 ast::make::tokens::single_newline().text(),
103 );
104 edit.insert(tuple_fields_text_range.start(), w.syntax().text());
105 edit.insert(tuple_fields_text_range.start(), ",");
106 edit.insert(
107 tuple_fields_text_range.start(),
108 ast::make::tokens::single_newline().text(),
109 );
110 } else {
111 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
112 }
113 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
95 } else { 114 } else {
96 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); 115 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97 } 116 }
98 117
99 edit.replace(tuple_fields_text_range, record_fields.to_string()); 118 edit.replace(tuple_fields_text_range, record_fields.to_string());
100 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101} 119}
102 120
103fn edit_struct_references( 121fn edit_struct_references(
104 ctx: &AssistContext, 122 ctx: &AssistContext,
105 edit: &mut AssistBuilder, 123 edit: &mut AssistBuilder,
106 strukt: hir::Struct, 124 strukt: Either<hir::Struct, hir::Variant>,
107 names: &[ast::Name], 125 names: &[ast::Name],
108) { 126) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 127 let strukt_def = match strukt {
128 Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))),
129 Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)),
130 };
110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); 131 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 132
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 133 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
@@ -513,4 +534,305 @@ where
513"#, 534"#,
514 ); 535 );
515 } 536 }
537 #[test]
538 fn not_applicable_other_than_tuple_variant() {
539 check_assist_not_applicable(
540 convert_tuple_struct_to_named_struct,
541 r#"enum Enum { Variant$0 { value: usize } };"#,
542 );
543 check_assist_not_applicable(
544 convert_tuple_struct_to_named_struct,
545 r#"enum Enum { Variant$0 }"#,
546 );
547 }
548
549 #[test]
550 fn convert_simple_variant() {
551 check_assist(
552 convert_tuple_struct_to_named_struct,
553 r#"
554enum A {
555 $0Variant(usize),
556}
557
558impl A {
559 fn new(value: usize) -> A {
560 A::Variant(value)
561 }
562
563 fn new_with_default() -> A {
564 A::new(Default::default())
565 }
566
567 fn value(self) -> usize {
568 match self {
569 A::Variant(value) => value,
570 }
571 }
572}"#,
573 r#"
574enum A {
575 Variant { field1: usize },
576}
577
578impl A {
579 fn new(value: usize) -> A {
580 A::Variant { field1: value }
581 }
582
583 fn new_with_default() -> A {
584 A::new(Default::default())
585 }
586
587 fn value(self) -> usize {
588 match self {
589 A::Variant { field1: value } => value,
590 }
591 }
592}"#,
593 );
594 }
595
596 #[test]
597 fn convert_variant_referenced_via_self_kw() {
598 check_assist(
599 convert_tuple_struct_to_named_struct,
600 r#"
601enum A {
602 $0Variant(usize),
603}
604
605impl A {
606 fn new(value: usize) -> A {
607 Self::Variant(value)
608 }
609
610 fn new_with_default() -> A {
611 Self::new(Default::default())
612 }
613
614 fn value(self) -> usize {
615 match self {
616 Self::Variant(value) => value,
617 }
618 }
619}"#,
620 r#"
621enum A {
622 Variant { field1: usize },
623}
624
625impl A {
626 fn new(value: usize) -> A {
627 Self::Variant { field1: value }
628 }
629
630 fn new_with_default() -> A {
631 Self::new(Default::default())
632 }
633
634 fn value(self) -> usize {
635 match self {
636 Self::Variant { field1: value } => value,
637 }
638 }
639}"#,
640 );
641 }
642
643 #[test]
644 fn convert_destructured_variant() {
645 check_assist(
646 convert_tuple_struct_to_named_struct,
647 r#"
648enum A {
649 $0Variant(usize),
650}
651
652impl A {
653 fn into_inner(self) -> usize {
654 let A::Variant(first) = self;
655 first
656 }
657
658 fn into_inner_via_self(self) -> usize {
659 let Self::Variant(first) = self;
660 first
661 }
662}"#,
663 r#"
664enum A {
665 Variant { field1: usize },
666}
667
668impl A {
669 fn into_inner(self) -> usize {
670 let A::Variant { field1: first } = self;
671 first
672 }
673
674 fn into_inner_via_self(self) -> usize {
675 let Self::Variant { field1: first } = self;
676 first
677 }
678}"#,
679 );
680 }
681
682 #[test]
683 fn convert_variant_with_wrapped_references() {
684 check_assist(
685 convert_tuple_struct_to_named_struct,
686 r#"
687enum Inner {
688 $0Variant(usize),
689}
690enum Outer {
691 Variant(Inner),
692}
693
694impl Outer {
695 fn new() -> Self {
696 Self::Variant(Inner::Variant(42))
697 }
698
699 fn into_inner_destructed(self) -> u32 {
700 let Outer::Variant(Inner::Variant(x)) = self;
701 x
702 }
703}"#,
704 r#"
705enum Inner {
706 Variant { field1: usize },
707}
708enum Outer {
709 Variant(Inner),
710}
711
712impl Outer {
713 fn new() -> Self {
714 Self::Variant(Inner::Variant { field1: 42 })
715 }
716
717 fn into_inner_destructed(self) -> u32 {
718 let Outer::Variant(Inner::Variant { field1: x }) = self;
719 x
720 }
721}"#,
722 );
723
724 check_assist(
725 convert_tuple_struct_to_named_struct,
726 r#"
727enum Inner {
728 Variant(usize),
729}
730enum Outer {
731 $0Variant(Inner),
732}
733
734impl Outer {
735 fn new() -> Self {
736 Self::Variant(Inner::Variant(42))
737 }
738
739 fn into_inner_destructed(self) -> u32 {
740 let Outer::Variant(Inner::Variant(x)) = self;
741 x
742 }
743}"#,
744 r#"
745enum Inner {
746 Variant(usize),
747}
748enum Outer {
749 Variant { field1: Inner },
750}
751
752impl Outer {
753 fn new() -> Self {
754 Self::Variant { field1: Inner::Variant(42) }
755 }
756
757 fn into_inner_destructed(self) -> u32 {
758 let Outer::Variant { field1: Inner::Variant(x) } = self;
759 x
760 }
761}"#,
762 );
763 }
764
765 #[test]
766 fn convert_variant_with_multi_file_references() {
767 check_assist(
768 convert_tuple_struct_to_named_struct,
769 r#"
770//- /main.rs
771struct Inner;
772enum A {
773 $0Variant(Inner),
774}
775
776mod foo;
777
778//- /foo.rs
779use crate::{A, Inner};
780fn f() {
781 let a = A::Variant(Inner);
782}
783"#,
784 r#"
785//- /main.rs
786struct Inner;
787enum A {
788 Variant { field1: Inner },
789}
790
791mod foo;
792
793//- /foo.rs
794use crate::{A, Inner};
795fn f() {
796 let a = A::Variant { field1: Inner };
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn convert_directly_used_variant() {
804 check_assist(
805 convert_tuple_struct_to_named_struct,
806 r#"
807//- /main.rs
808struct Inner;
809enum A {
810 $0Variant(Inner),
811}
812
813mod foo;
814
815//- /foo.rs
816use crate::{A::Variant, Inner};
817fn f() {
818 let a = Variant(Inner);
819}
820"#,
821 r#"
822//- /main.rs
823struct Inner;
824enum A {
825 Variant { field1: Inner },
826}
827
828mod foo;
829
830//- /foo.rs
831use crate::{A::Variant, Inner};
832fn f() {
833 let a = Variant { field1: Inner };
834}
835"#,
836 );
837 }
516} 838}
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index 5eb6a57f0..ef4a7cb50 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -108,7 +108,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
108 "Convert to guarded return", 108 "Convert to guarded return",
109 target, 109 target,
110 |edit| { 110 |edit| {
111 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 111 let if_indent_level = IndentLevel::from_node(if_expr.syntax());
112 let new_block = match if_let_pat { 112 let new_block = match if_let_pat {
113 None => { 113 None => {
114 // If. 114 // If.
@@ -174,7 +174,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
174 .take_while(|i| *i != end_of_then), 174 .take_while(|i| *i != end_of_then),
175 ); 175 );
176 replace_children( 176 replace_children(
177 &parent_block.syntax(), 177 parent_block.syntax(),
178 RangeInclusive::new( 178 RangeInclusive::new(
179 if_expr.clone().syntax().clone().into(), 179 if_expr.clone().syntax().clone().into(),
180 if_expr.syntax().clone().into(), 180 if_expr.syntax().clone().into(),
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index a2dba915c..f2be091f4 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -76,7 +76,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
76 let module = ctx.sema.scope(&insert_after).module()?; 76 let module = ctx.sema.scope(&insert_after).module()?;
77 77
78 let vars_defined_in_body_and_outlive = 78 let vars_defined_in_body_and_outlive =
79 vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node)); 79 vars_defined_in_body_and_outlive(ctx, &body, node.parent().as_ref().unwrap_or(&node));
80 let ret_ty = body_return_ty(ctx, &body)?; 80 let ret_ty = body_return_ty(ctx, &body)?;
81 81
82 // FIXME: we compute variables that outlive here just to check `never!` condition 82 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -782,7 +782,7 @@ fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Optio
782 Some(false) 782 Some(false)
783} 783}
784 784
785/// Container of local varaible usages 785/// Container of local variable usages
786/// 786///
787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface 787/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
788struct LocalUsages(ide_db::search::UsageSearchResult); 788struct LocalUsages(ide_db::search::UsageSearchResult);
@@ -808,7 +808,7 @@ trait HasTokenAtOffset {
808 808
809impl HasTokenAtOffset for SyntaxNode { 809impl HasTokenAtOffset for SyntaxNode {
810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> { 810 fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken> {
811 SyntaxNode::token_at_offset(&self, offset) 811 SyntaxNode::token_at_offset(self, offset)
812 } 812 }
813} 813}
814 814
@@ -854,7 +854,7 @@ fn vars_defined_in_body_and_outlive(
854 body: &FunctionBody, 854 body: &FunctionBody,
855 parent: &SyntaxNode, 855 parent: &SyntaxNode,
856) -> Vec<OutlivedLocal> { 856) -> Vec<OutlivedLocal> {
857 let vars_defined_in_body = vars_defined_in_body(&body, ctx); 857 let vars_defined_in_body = vars_defined_in_body(body, ctx);
858 vars_defined_in_body 858 vars_defined_in_body
859 .into_iter() 859 .into_iter()
860 .filter_map(|var| var_outlives_body(ctx, body, var, parent)) 860 .filter_map(|var| var_outlives_body(ctx, body, var, parent))
@@ -868,7 +868,7 @@ fn is_defined_before(
868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>, 868 src: &hir::InFile<Either<ast::IdentPat, ast::SelfParam>>,
869) -> bool { 869) -> bool {
870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id 870 src.file_id.original_file(ctx.db()) == ctx.frange.file_id
871 && !body.contains_node(&either_syntax(&src.value)) 871 && !body.contains_node(either_syntax(&src.value))
872} 872}
873 873
874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode { 874fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 007aba23d..d3ff7b65c 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -11,14 +11,19 @@ use ide_db::{
11 search::FileReference, 11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use itertools::Itertools;
14use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
15use syntax::{ 16use syntax::{
16 algo::find_node_at_offset, 17 ast::{
17 ast::{self, make, AstNode, NameOwner, VisibilityOwner}, 18 self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner,
18 ted, SyntaxNode, T, 19 VisibilityOwner,
20 },
21 match_ast,
22 ted::{self, Position},
23 SyntaxNode, T,
19}; 24};
20 25
21use crate::{AssistContext, AssistId, AssistKind, Assists}; 26use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
22 27
23// Assist: extract_struct_from_enum_variant 28// Assist: extract_struct_from_enum_variant
24// 29//
@@ -70,11 +75,10 @@ pub(crate) fn extract_struct_from_enum_variant(
70 continue; 75 continue;
71 } 76 }
72 builder.edit_file(file_id); 77 builder.edit_file(file_id);
73 let source_file = builder.make_mut(ctx.sema.parse(file_id));
74 let processed = process_references( 78 let processed = process_references(
75 ctx, 79 ctx,
80 builder,
76 &mut visited_modules_set, 81 &mut visited_modules_set,
77 source_file.syntax(),
78 &enum_module_def, 82 &enum_module_def,
79 &variant_hir_name, 83 &variant_hir_name,
80 references, 84 references,
@@ -84,13 +88,12 @@ pub(crate) fn extract_struct_from_enum_variant(
84 }); 88 });
85 } 89 }
86 builder.edit_file(ctx.frange.file_id); 90 builder.edit_file(ctx.frange.file_id);
87 let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id));
88 let variant = builder.make_mut(variant.clone()); 91 let variant = builder.make_mut(variant.clone());
89 if let Some(references) = def_file_references { 92 if let Some(references) = def_file_references {
90 let processed = process_references( 93 let processed = process_references(
91 ctx, 94 ctx,
95 builder,
92 &mut visited_modules_set, 96 &mut visited_modules_set,
93 source_file.syntax(),
94 &enum_module_def, 97 &enum_module_def,
95 &variant_hir_name, 98 &variant_hir_name,
96 references, 99 references,
@@ -100,12 +103,12 @@ pub(crate) fn extract_struct_from_enum_variant(
100 }); 103 });
101 } 104 }
102 105
103 let def = create_struct_def(variant_name.clone(), &field_list, enum_ast.visibility()); 106 let def = create_struct_def(variant_name.clone(), &field_list, &enum_ast);
104 let start_offset = &variant.parent_enum().syntax().clone(); 107 let start_offset = &variant.parent_enum().syntax().clone();
105 ted::insert_raw(ted::Position::before(start_offset), def.syntax()); 108 ted::insert_raw(ted::Position::before(start_offset), def.syntax());
106 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); 109 ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line());
107 110
108 update_variant(&variant); 111 update_variant(&variant, enum_ast.generic_param_list());
109 }, 112 },
110 ) 113 )
111} 114}
@@ -149,7 +152,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
149fn create_struct_def( 152fn create_struct_def(
150 variant_name: ast::Name, 153 variant_name: ast::Name,
151 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, 154 field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
152 visibility: Option<ast::Visibility>, 155 enum_: &ast::Enum,
153) -> ast::Struct { 156) -> ast::Struct {
154 let pub_vis = make::visibility_pub(); 157 let pub_vis = make::visibility_pub();
155 158
@@ -184,12 +187,38 @@ fn create_struct_def(
184 } 187 }
185 }; 188 };
186 189
187 make::struct_(visibility, variant_name, None, field_list).clone_for_update() 190 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
191 let strukt =
192 make::struct_(enum_.visibility(), variant_name, enum_.generic_param_list(), field_list)
193 .clone_for_update();
194
195 // copy attributes
196 ted::insert_all(
197 Position::first_child_of(strukt.syntax()),
198 enum_.attrs().map(|it| it.syntax().clone_for_update().into()).collect(),
199 );
200 strukt
188} 201}
189 202
190fn update_variant(variant: &ast::Variant) -> Option<()> { 203fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>) -> Option<()> {
191 let name = variant.name()?; 204 let name = variant.name()?;
192 let tuple_field = make::tuple_field(None, make::ty(&name.text())); 205 let ty = match generic {
206 // FIXME: This uses all the generic params of the enum, but the variant might not use all of them.
207 Some(gpl) => {
208 let gpl = gpl.clone_for_update();
209 gpl.generic_params().for_each(|gp| {
210 match gp {
211 ast::GenericParam::LifetimeParam(it) => it.type_bound_list(),
212 ast::GenericParam::TypeParam(it) => it.type_bound_list(),
213 ast::GenericParam::ConstParam(_) => return,
214 }
215 .map(|it| it.remove());
216 });
217 make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", ")))
218 }
219 None => make::ty(&name.text()),
220 };
221 let tuple_field = make::tuple_field(None, ty);
193 let replacement = make::variant( 222 let replacement = make::variant(
194 name, 223 name,
195 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), 224 Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))),
@@ -208,18 +237,17 @@ fn apply_references(
208 if let Some((scope, path)) = import { 237 if let Some((scope, path)) = import {
209 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); 238 insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg);
210 } 239 }
211 ted::insert_raw( 240 // deep clone to prevent cycle
212 ted::Position::before(segment.syntax()), 241 let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
213 make::path_from_text(&format!("{}", segment)).clone_for_update().syntax(), 242 ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
214 );
215 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); 243 ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
216 ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); 244 ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
217} 245}
218 246
219fn process_references( 247fn process_references(
220 ctx: &AssistContext, 248 ctx: &AssistContext,
249 builder: &mut AssistBuilder,
221 visited_modules: &mut FxHashSet<Module>, 250 visited_modules: &mut FxHashSet<Module>,
222 source_file: &SyntaxNode,
223 enum_module_def: &ModuleDef, 251 enum_module_def: &ModuleDef,
224 variant_hir_name: &Name, 252 variant_hir_name: &Name,
225 refs: Vec<FileReference>, 253 refs: Vec<FileReference>,
@@ -228,8 +256,9 @@ fn process_references(
228 // and corresponding nodes up front 256 // and corresponding nodes up front
229 refs.into_iter() 257 refs.into_iter()
230 .flat_map(|reference| { 258 .flat_map(|reference| {
231 let (segment, scope_node, module) = 259 let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
232 reference_to_node(&ctx.sema, source_file, reference)?; 260 let segment = builder.make_mut(segment);
261 let scope_node = builder.make_syntax_mut(scope_node);
233 if !visited_modules.contains(&module) { 262 if !visited_modules.contains(&module) {
234 let mod_path = module.find_use_path_prefixed( 263 let mod_path = module.find_use_path_prefixed(
235 ctx.sema.db, 264 ctx.sema.db,
@@ -251,23 +280,22 @@ fn process_references(
251 280
252fn reference_to_node( 281fn reference_to_node(
253 sema: &hir::Semantics<RootDatabase>, 282 sema: &hir::Semantics<RootDatabase>,
254 source_file: &SyntaxNode,
255 reference: FileReference, 283 reference: FileReference,
256) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { 284) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> {
257 let offset = reference.range.start(); 285 let segment =
258 if let Some(path_expr) = find_node_at_offset::<ast::PathExpr>(source_file, offset) { 286 reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?;
259 // tuple variant 287 let parent = segment.parent_path().syntax().parent()?;
260 Some((path_expr.path()?.segment()?, path_expr.syntax().parent()?)) 288 let expr_or_pat = match_ast! {
261 } else if let Some(record_expr) = find_node_at_offset::<ast::RecordExpr>(source_file, offset) { 289 match parent {
262 // record variant 290 ast::PathExpr(_it) => parent.parent()?,
263 Some((record_expr.path()?.segment()?, record_expr.syntax().clone())) 291 ast::RecordExpr(_it) => parent,
264 } else { 292 ast::TupleStructPat(_it) => parent,
265 None 293 ast::RecordPat(_it) => parent,
266 } 294 _ => return None,
267 .and_then(|(segment, expr)| { 295 }
268 let module = sema.scope(&expr).module()?; 296 };
269 Some((segment, expr, module)) 297 let module = sema.scope(&expr_or_pat).module()?;
270 }) 298 Some((segment, expr_or_pat, module))
271} 299}
272 300
273#[cfg(test)] 301#[cfg(test)]
@@ -278,6 +306,12 @@ mod tests {
278 306
279 use super::*; 307 use super::*;
280 308
309 fn check_not_applicable(ra_fixture: &str) {
310 let fixture =
311 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
312 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
313 }
314
281 #[test] 315 #[test]
282 fn test_extract_struct_several_fields_tuple() { 316 fn test_extract_struct_several_fields_tuple() {
283 check_assist( 317 check_assist(
@@ -312,6 +346,32 @@ enum A { One(One) }"#,
312 } 346 }
313 347
314 #[test] 348 #[test]
349 fn test_extract_struct_carries_over_generics() {
350 check_assist(
351 extract_struct_from_enum_variant,
352 r"enum En<T> { Var { a: T$0 } }",
353 r#"struct Var<T>{ pub a: T }
354
355enum En<T> { Var(Var<T>) }"#,
356 );
357 }
358
359 #[test]
360 fn test_extract_struct_carries_over_attributes() {
361 check_assist(
362 extract_struct_from_enum_variant,
363 r#"#[derive(Debug)]
364#[derive(Clone)]
365enum Enum { Variant{ field: u32$0 } }"#,
366 r#"#[derive(Debug)]#[derive(Clone)] struct Variant{ pub field: u32 }
367
368#[derive(Debug)]
369#[derive(Clone)]
370enum Enum { Variant(Variant) }"#,
371 );
372 }
373
374 #[test]
315 fn test_extract_struct_keep_comments_and_attrs_one_field_named() { 375 fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
316 check_assist( 376 check_assist(
317 extract_struct_from_enum_variant, 377 extract_struct_from_enum_variant,
@@ -496,7 +556,7 @@ enum E {
496} 556}
497 557
498fn f() { 558fn f() {
499 let e = E::V { i: 9, j: 2 }; 559 let E::V { i, j } = E::V { i: 9, j: 2 };
500} 560}
501"#, 561"#,
502 r#" 562 r#"
@@ -507,7 +567,34 @@ enum E {
507} 567}
508 568
509fn f() { 569fn f() {
510 let e = E::V(V { i: 9, j: 2 }); 570 let E::V(V { i, j }) = E::V(V { i: 9, j: 2 });
571}
572"#,
573 )
574 }
575
576 #[test]
577 fn extract_record_fix_references2() {
578 check_assist(
579 extract_struct_from_enum_variant,
580 r#"
581enum E {
582 $0V(i32, i32)
583}
584
585fn f() {
586 let E::V(i, j) = E::V(9, 2);
587}
588"#,
589 r#"
590struct V(pub i32, pub i32);
591
592enum E {
593 V(V)
594}
595
596fn f() {
597 let E::V(V(i, j)) = E::V(V(9, 2));
511} 598}
512"#, 599"#,
513 ) 600 )
@@ -610,12 +697,6 @@ fn foo() {
610 ); 697 );
611 } 698 }
612 699
613 fn check_not_applicable(ra_fixture: &str) {
614 let fixture =
615 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
616 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
617 }
618
619 #[test] 700 #[test]
620 fn test_extract_enum_not_applicable_for_element_with_no_fields() { 701 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
621 check_not_applicable("enum A { $0One }"); 702 check_not_applicable("enum A { $0One }");
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs
index 998e0de7b..eac8857c6 100644
--- a/crates/ide_assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide_assists/src/handlers/extract_type_alias.rs
@@ -1,4 +1,7 @@
1use syntax::ast::{self, AstNode}; 1use syntax::{
2 ast::{self, edit::IndentLevel, AstNode},
3 match_ast,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
4 7
@@ -25,12 +28,15 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
25 } 28 }
26 29
27 let node = ctx.find_node_at_range::<ast::Type>()?; 30 let node = ctx.find_node_at_range::<ast::Type>()?;
28 let insert = ctx 31 let item = ctx.find_node_at_offset::<ast::Item>()?;
29 .find_node_at_offset::<ast::Impl>() 32 let insert = match_ast! {
30 .map(|imp| imp.syntax().clone()) 33 match (item.syntax().parent()?) {
31 .or_else(|| ctx.find_node_at_offset::<ast::Item>().map(|item| item.syntax().clone()))? 34 ast::AssocItemList(it) => it.syntax().parent()?,
32 .text_range() 35 _ => item.syntax().clone(),
33 .start(); 36 }
37 };
38 let indent = IndentLevel::from_node(&insert);
39 let insert = insert.text_range().start();
34 let target = node.syntax().text_range(); 40 let target = node.syntax().text_range();
35 41
36 acc.add( 42 acc.add(
@@ -42,10 +48,14 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti
42 builder.replace(target, "Type"); 48 builder.replace(target, "Type");
43 match ctx.config.snippet_cap { 49 match ctx.config.snippet_cap {
44 Some(cap) => { 50 Some(cap) => {
45 builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); 51 builder.insert_snippet(
52 cap,
53 insert,
54 format!("type $0Type = {};\n\n{}", node, indent),
55 );
46 } 56 }
47 None => { 57 None => {
48 builder.insert(insert, format!("type Type = {};\n\n", node)); 58 builder.insert(insert, format!("type Type = {};\n\n{}", node, indent));
49 } 59 }
50 } 60 }
51 }, 61 },
@@ -153,9 +163,9 @@ struct S {
153 } 163 }
154 164
155 #[test] 165 #[test]
156 fn extract_from_impl() { 166 fn extract_from_impl_or_trait() {
157 // When invoked in an impl, extracted type alias should be placed next to the impl, not 167 // When invoked in an impl/trait, extracted type alias should be placed next to the
158 // inside. 168 // impl/trait, not inside.
159 check_assist( 169 check_assist(
160 extract_type_alias, 170 extract_type_alias,
161 r#" 171 r#"
@@ -171,5 +181,39 @@ impl S {
171} 181}
172 "#, 182 "#,
173 ); 183 );
184 check_assist(
185 extract_type_alias,
186 r#"
187trait Tr {
188 fn f() -> $0(u8, u8)$0 {}
189}
190 "#,
191 r#"
192type $0Type = (u8, u8);
193
194trait Tr {
195 fn f() -> Type {}
196}
197 "#,
198 );
199 }
200
201 #[test]
202 fn indentation() {
203 check_assist(
204 extract_type_alias,
205 r#"
206mod m {
207 fn f() -> $0u8$0 {}
208}
209 "#,
210 r#"
211mod m {
212 type $0Type = u8;
213
214 fn f() -> Type {}
215}
216 "#,
217 );
174 } 218 }
175} 219}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index ae084c86c..46b54a5f5 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -36,6 +36,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
36 return None; 36 return None;
37 } 37 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 38 let to_extract = node.ancestors().find_map(valid_target_expr)?;
39 if let Some(ty) = ctx.sema.type_of_expr(&to_extract) {
40 if ty.is_unit() {
41 return None;
42 }
43 }
39 let anchor = Anchor::from(&to_extract)?; 44 let anchor = Anchor::from(&to_extract)?;
40 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone(); 45 let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
41 let target = to_extract.syntax().text_range(); 46 let target = to_extract.syntax().text_range();
@@ -275,15 +280,23 @@ fn foo() {
275 check_assist( 280 check_assist(
276 extract_variable, 281 extract_variable,
277 r#" 282 r#"
278fn foo() { 283fn foo() -> i32 {
279 $0bar(1 + 1)$0 284 $0bar(1 + 1)$0
280} 285}
286
287fn bar(i: i32) -> i32 {
288 i
289}
281"#, 290"#,
282 r#" 291 r#"
283fn foo() { 292fn foo() -> i32 {
284 let $0bar = bar(1 + 1); 293 let $0bar = bar(1 + 1);
285 bar 294 bar
286} 295}
296
297fn bar(i: i32) -> i32 {
298 i
299}
287"#, 300"#,
288 ) 301 )
289 } 302 }
@@ -796,6 +809,22 @@ fn foo() {
796 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }"); 809 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
797 } 810 }
798 811
812 #[test]
813 fn test_extract_var_unit_expr_not_applicable() {
814 check_assist_not_applicable(
815 extract_variable,
816 r#"
817fn foo() {
818 let mut i = 3;
819 $0if i >= 0 {
820 i += 1;
821 } else {
822 i -= 1;
823 }$0
824}"#,
825 );
826 }
827
799 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 828 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
800 #[test] 829 #[test]
801 fn extract_var_target() { 830 fn extract_var_target() {
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 3d2cd739a..5a43bdd6f 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -202,7 +202,7 @@ impl ExtendedEnum {
202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> { 202 fn variants(self, db: &RootDatabase) -> Vec<ExtendedVariant> {
203 match self { 203 match self {
204 ExtendedEnum::Enum(e) => { 204 ExtendedEnum::Enum(e) => {
205 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>() 205 e.variants(db).into_iter().map(ExtendedVariant::Variant).collect::<Vec<_>>()
206 } 206 }
207 ExtendedEnum::Bool => { 207 ExtendedEnum::Bool => {
208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False]) 208 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
@@ -212,7 +212,7 @@ impl ExtendedEnum {
212} 212}
213 213
214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> { 214fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
215 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 215 sema.type_of_expr(expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)), 216 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
217 _ => { 217 _ => {
218 if ty.is_bool() { 218 if ty.is_bool() {
@@ -228,7 +228,7 @@ fn resolve_tuple_of_enum_def(
228 sema: &Semantics<RootDatabase>, 228 sema: &Semantics<RootDatabase>,
229 expr: &ast::Expr, 229 expr: &ast::Expr,
230) -> Option<Vec<ExtendedEnum>> { 230) -> Option<Vec<ExtendedEnum>> {
231 sema.type_of_expr(&expr)? 231 sema.type_of_expr(expr)?
232 .tuple_fields(sema.db) 232 .tuple_fields(sema.db)
233 .iter() 233 .iter()
234 .map(|ty| { 234 .map(|ty| {
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs
index 89f7b2c2c..9b432e92f 100644
--- a/crates/ide_assists/src/handlers/fix_visibility.rs
+++ b/crates/ide_assists/src/handlers/fix_visibility.rs
@@ -43,7 +43,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
43 _ => return None, 43 _ => return None,
44 }; 44 };
45 45
46 let current_module = ctx.sema.scope(&path.syntax()).module()?; 46 let current_module = ctx.sema.scope(path.syntax()).module()?;
47 let target_module = def.module(ctx.db())?; 47 let target_module = def.module(ctx.db())?;
48 48
49 let vis = target_module.visibility_of(ctx.db(), &def)?; 49 let vis = target_module.visibility_of(ctx.db(), &def)?;
diff --git a/crates/ide_assists/src/handlers/generate_enum_is_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index a9f71a703..24939f262 100644
--- a/crates/ide_assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -47,7 +47,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) ->
47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text())); 47 let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text()));
48 48
49 // Return early if we've found an existing new fn 49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 50 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
51 51
52 let target = variant.syntax().text_range(); 52 let target = variant.syntax().text_range();
53 acc.add( 53 acc.add(
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
index e2f572ba3..986fb2315 100644
--- a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
@@ -136,7 +136,7 @@ fn generate_enum_projection_method(
136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text())); 136 format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
137 137
138 // Return early if we've found an existing new fn 138 // Return early if we've found an existing new fn
139 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?; 139 let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
140 140
141 let target = variant.syntax().text_range(); 141 let target = variant.syntax().text_range();
142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| { 142 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index bc9fc524b..706c995ac 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -59,7 +59,7 @@ pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Optio
59 None => None, 59 None => None,
60 }; 60 };
61 61
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add( 65 acc.add(
@@ -128,12 +128,12 @@ impl FunctionBuilder {
128 file = in_file; 128 file = in_file;
129 target 129 target
130 } 130 }
131 None => next_space_for_fn_after_call_site(&call)?, 131 None => next_space_for_fn_after_call_site(call)?,
132 }; 132 };
133 let needs_pub = target_module.is_some(); 133 let needs_pub = target_module.is_some();
134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; 134 let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?;
135 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(path)?;
136 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, call)?;
137 137
138 // should_render_snippet intends to express a rough level of confidence about 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type. 139 // the correctness of the return type.
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index 09971226e..cc020c92c 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -75,7 +75,7 @@ pub(crate) fn generate_getter_impl(
75 if mutable { 75 if mutable {
76 format_to!(fn_name, "_mut"); 76 format_to!(fn_name, "_mut");
77 } 77 }
78 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 78 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
79 79
80 let (id, label) = if mutable { 80 let (id, label) = if mutable {
81 ("generate_getter_mut", "Generate a mut getter method") 81 ("generate_getter_mut", "Generate a mut getter method")
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs
index 959a1f86c..b65e8387b 100644
--- a/crates/ide_assists/src/handlers/generate_new.rs
+++ b/crates/ide_assists/src/handlers/generate_new.rs
@@ -36,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
36 }; 36 };
37 37
38 // Return early if we've found an existing new fn 38 // Return early if we've found an existing new fn
39 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; 39 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
40 40
41 let target = strukt.syntax().text_range(); 41 let target = strukt.syntax().text_range();
42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { 42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
diff --git a/crates/ide_assists/src/handlers/generate_setter.rs b/crates/ide_assists/src/handlers/generate_setter.rs
index 288cf745d..5bdf6b3f4 100644
--- a/crates/ide_assists/src/handlers/generate_setter.rs
+++ b/crates/ide_assists/src/handlers/generate_setter.rs
@@ -39,7 +39,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
39 // Return early if we've found an existing fn 39 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string()); 40 let fn_name = to_lower_snake_case(&field_name.to_string());
41 let impl_def = find_struct_impl( 41 let impl_def = find_struct_impl(
42 &ctx, 42 ctx,
43 &ast::Adt::Struct(strukt.clone()), 43 &ast::Adt::Struct(strukt.clone()),
44 format!("set_{}", fn_name).as_str(), 44 format!("set_{}", fn_name).as_str(),
45 )?; 45 )?;
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index f5dafc8cb..2441dbb8b 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -182,6 +182,10 @@ fn inline_usage(ctx: &AssistContext) -> Option<InlineData> {
182 PathResolution::Local(local) => local, 182 PathResolution::Local(local) => local,
183 _ => return None, 183 _ => return None,
184 }; 184 };
185 if local.is_mut(ctx.sema.db) {
186 cov_mark::hit!(test_not_inline_mut_variable_use);
187 return None;
188 }
185 189
186 let bind_pat = match local.source(ctx.db()).value { 190 let bind_pat = match local.source(ctx.db()).value {
187 Either::Left(ident) => ident, 191 Either::Left(ident) => ident,
@@ -427,6 +431,19 @@ fn foo() {
427 } 431 }
428 432
429 #[test] 433 #[test]
434 fn test_not_inline_mut_variable_use() {
435 cov_mark::check!(test_not_inline_mut_variable_use);
436 check_assist_not_applicable(
437 inline_local_variable,
438 r"
439fn foo() {
440 let mut a = 1 + 1;
441 a$0 + 1;
442}",
443 );
444 }
445
446 #[test]
430 fn test_call_expr() { 447 fn test_call_expr() {
431 check_assist( 448 check_assist(
432 inline_local_variable, 449 inline_local_variable,
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs
index c8226550f..b20fe992d 100644
--- a/crates/ide_assists/src/handlers/remove_dbg.rs
+++ b/crates/ide_assists/src/handlers/remove_dbg.rs
@@ -85,7 +85,7 @@ fn whitespace_start(it: SyntaxElement) -> Option<TextSize> {
85} 85}
86 86
87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { 87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
88 let contents = get_valid_macrocall_contents(&macro_call, "dbg")?; 88 let contents = get_valid_macrocall_contents(macro_call, "dbg")?;
89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); 89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new( 90 let macro_text_in_brackets = macro_text_with_brackets.slice(TextRange::new(
91 TextSize::of('('), 91 TextSize::of('('),
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs
index 2699d2861..fabfe7e93 100644
--- a/crates/ide_assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide_assists/src/handlers/remove_unused_param.rs
@@ -37,8 +37,20 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
37 _ => return None, 37 _ => return None,
38 }; 38 };
39 let func = param.syntax().ancestors().find_map(ast::Fn::cast)?; 39 let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
40 let param_position = func.param_list()?.params().position(|it| it == param)?;
41 40
41 // check if fn is in impl Trait for ..
42 if func
43 .syntax()
44 .parent() // AssocItemList
45 .and_then(|x| x.parent())
46 .and_then(ast::Impl::cast)
47 .map_or(false, |imp| imp.trait_().is_some())
48 {
49 cov_mark::hit!(trait_impl);
50 return None;
51 }
52
53 let param_position = func.param_list()?.params().position(|it| it == param)?;
42 let fn_def = { 54 let fn_def = {
43 let func = ctx.sema.to_def(&func)?; 55 let func = ctx.sema.to_def(&func)?;
44 Definition::ModuleDef(func.into()) 56 Definition::ModuleDef(func.into())
@@ -254,6 +266,22 @@ fn main() { foo(9, 2) }
254 } 266 }
255 267
256 #[test] 268 #[test]
269 fn trait_impl() {
270 cov_mark::check!(trait_impl);
271 check_assist_not_applicable(
272 remove_unused_param,
273 r#"
274trait Trait {
275 fn foo(x: i32);
276}
277impl Trait for () {
278 fn foo($0x: i32) {}
279}
280"#,
281 );
282 }
283
284 #[test]
257 fn remove_across_files() { 285 fn remove_across_files() {
258 check_assist( 286 check_assist(
259 remove_unused_param, 287 remove_unused_param,
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 933acead1..f6a926042 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -28,7 +28,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<(
28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?; 28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?;
29 29
30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?; 30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
31 let ranks = compute_fields_ranks(&path, &ctx)?; 31 let ranks = compute_fields_ranks(&path, ctx)?;
32 let get_rank_of_field = 32 let get_rank_of_field =
33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); 33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
34 34
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 10d9cec31..f9474c9f5 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -112,7 +112,7 @@ fn add_assist(
112 let insert_pos = adt.syntax().text_range().end(); 112 let insert_pos = adt.syntax().text_range().end();
113 let impl_def_with_items = 113 let impl_def_with_items =
114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path); 114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path);
115 update_attribute(builder, &input, &trait_name, &attr); 115 update_attribute(builder, input, &trait_name, attr);
116 let trait_path = format!("{}", trait_path); 116 let trait_path = format!("{}", trait_path);
117 match (ctx.config.snippet_cap, impl_def_with_items) { 117 match (ctx.config.snippet_cap, impl_def_with_items) {
118 (None, _) => { 118 (None, _) => {
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index aee880625..9404aa26d 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -169,7 +169,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
169} 169}
170 170
171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { 171fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
172 sema.type_of_pat(&pat) 172 sema.type_of_pat(pat)
173 .and_then(|ty| TryEnum::from_ty(sema, &ty)) 173 .and_then(|ty| TryEnum::from_ty(sema, &ty))
174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) 174 .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text())
175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) 175 .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_)))
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index 2f1da82c7..140e27356 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -123,7 +123,7 @@ impl TailReturnCollector {
123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { 123 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
124 match expr { 124 match expr {
125 Expr::BlockExpr(block_expr) => { 125 Expr::BlockExpr(block_expr) => {
126 self.collect_jump_exprs(&block_expr, collect_break); 126 self.collect_jump_exprs(block_expr, collect_break);
127 } 127 }
128 Expr::ReturnExpr(ret_expr) => { 128 Expr::ReturnExpr(ret_expr) => {
129 if let Some(ret_expr_arg) = &ret_expr.expr() { 129 if let Some(ret_expr_arg) = &ret_expr.expr() {
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 16af72927..331a6df2b 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -47,14 +47,14 @@ impl AssistKind {
47 } 47 }
48 48
49 match self { 49 match self {
50 AssistKind::None | AssistKind::Generate => return true, 50 AssistKind::None | AssistKind::Generate => true,
51 AssistKind::Refactor => match other { 51 AssistKind::Refactor => match other {
52 AssistKind::RefactorExtract 52 AssistKind::RefactorExtract
53 | AssistKind::RefactorInline 53 | AssistKind::RefactorInline
54 | AssistKind::RefactorRewrite => return true, 54 | AssistKind::RefactorRewrite => true,
55 _ => return false, 55 _ => false,
56 }, 56 },
57 _ => return false, 57 _ => false,
58 } 58 }
59 } 59 }
60 60
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 2b7c2d581..bdf9cb71c 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -74,7 +74,7 @@ pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
74#[track_caller] 74#[track_caller]
75fn check_doc_test(assist_id: &str, before: &str, after: &str) { 75fn check_doc_test(assist_id: &str, before: &str, after: &str) {
76 let after = trim_indent(after); 76 let after = trim_indent(after);
77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before); 77 let (db, file_id, selection) = RootDatabase::with_range_or_offset(before);
78 let before = db.file_text(file_id).to_string(); 78 let before = db.file_text(file_id).to_string();
79 let frange = FileRange { file_id, range: selection.into() }; 79 let frange = FileRange { file_id, range: selection.into() };
80 80
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 30128a24a..068df005b 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -492,7 +492,7 @@ pub(crate) fn add_method_to_adt(
492 let start_offset = impl_def 492 let start_offset = impl_def
493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf)) 493 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
494 .unwrap_or_else(|| { 494 .unwrap_or_else(|| {
495 buf = generate_impl_text(&adt, &buf); 495 buf = generate_impl_text(adt, &buf);
496 adt.syntax().text_range().end() 496 adt.syntax().text_range().end()
497 }); 497 });
498 498
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index b3aabeab3..cb8bc8b2f 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -187,7 +187,7 @@ fn from_method_call(expr: &ast::Expr) -> Option<String> {
187 } 187 }
188 } 188 }
189 189
190 normalize(&name) 190 normalize(name)
191} 191}
192 192
193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> { 193fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {