aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs22
-rw-r--r--crates/assists/src/handlers/move_guard.rs87
-rw-r--r--crates/assists/src/handlers/qualify_path.rs182
-rw-r--r--crates/assists/src/lib.rs5
-rw-r--r--crates/assists/src/utils/import_assets.rs6
-rw-r--r--crates/assists/src/utils/insert_use.rs4
6 files changed, 268 insertions, 38 deletions
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 48433feb9..178718c5e 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -37,6 +37,12 @@ pub(crate) fn extract_struct_from_enum_variant(
37 ast::StructKind::Tuple(field_list) => field_list, 37 ast::StructKind::Tuple(field_list) => field_list,
38 _ => return None, 38 _ => return None,
39 }; 39 };
40
41 // skip 1-tuple variants
42 if field_list.fields().count() == 1 {
43 return None;
44 }
45
40 let variant_name = variant.name()?.to_string(); 46 let variant_name = variant.name()?.to_string();
41 let variant_hir = ctx.sema.to_def(&variant)?; 47 let variant_hir = ctx.sema.to_def(&variant)?;
42 if existing_struct_def(ctx.db(), &variant_name, &variant_hir) { 48 if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
@@ -233,17 +239,6 @@ enum A { One(One) }"#,
233 } 239 }
234 240
235 #[test] 241 #[test]
236 fn test_extract_struct_one_field() {
237 check_assist(
238 extract_struct_from_enum_variant,
239 "enum A { <|>One(u32) }",
240 r#"struct One(pub u32);
241
242enum A { One(One) }"#,
243 );
244 }
245
246 #[test]
247 fn test_extract_struct_pub_visibility() { 242 fn test_extract_struct_pub_visibility() {
248 check_assist( 243 check_assist(
249 extract_struct_from_enum_variant, 244 extract_struct_from_enum_variant,
@@ -324,4 +319,9 @@ fn another_fn() {
324 enum A { <|>One(u8) }"#, 319 enum A { <|>One(u8) }"#,
325 ); 320 );
326 } 321 }
322
323 #[test]
324 fn test_extract_not_applicable_one_field() {
325 check_not_applicable(r"enum A { <|>One(u32) }");
326 }
327} 327}
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs
index 452115fe6..e1855b63d 100644
--- a/crates/assists/src/handlers/move_guard.rs
+++ b/crates/assists/src/handlers/move_guard.rs
@@ -1,5 +1,5 @@
1use syntax::{ 1use syntax::{
2 ast::{edit::AstNodeEdit, make, AstNode, IfExpr, MatchArm}, 2 ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, Expr, IfExpr, MatchArm},
3 SyntaxKind::WHITESPACE, 3 SyntaxKind::WHITESPACE,
4}; 4};
5 5
@@ -92,9 +92,20 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
92pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 92pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
93 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; 93 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
94 let match_pat = match_arm.pat()?; 94 let match_pat = match_arm.pat()?;
95
96 let arm_body = match_arm.expr()?; 95 let arm_body = match_arm.expr()?;
97 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?; 96
97 let mut replace_node = None;
98 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
99 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
100 if let Expr::IfExpr(e) = block_expr.expr()? {
101 replace_node = Some(block_expr.syntax().clone());
102 Some(e)
103 } else {
104 None
105 }
106 })?;
107 let replace_node = replace_node.unwrap_or_else(|| if_expr.syntax().clone());
108
98 let cond = if_expr.condition()?; 109 let cond = if_expr.condition()?;
99 let then_block = if_expr.then_branch()?; 110 let then_block = if_expr.then_branch()?;
100 111
@@ -109,19 +120,23 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
109 120
110 let buf = format!(" if {}", cond.syntax().text()); 121 let buf = format!(" if {}", cond.syntax().text());
111 122
112 let target = if_expr.syntax().text_range();
113 acc.add( 123 acc.add(
114 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite), 124 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
115 "Move condition to match guard", 125 "Move condition to match guard",
116 target, 126 replace_node.text_range(),
117 |edit| { 127 |edit| {
118 let then_only_expr = then_block.statements().next().is_none(); 128 let then_only_expr = then_block.statements().next().is_none();
119 129
120 match &then_block.expr() { 130 match &then_block.expr() {
121 Some(then_expr) if then_only_expr => { 131 Some(then_expr) if then_only_expr => {
122 edit.replace(if_expr.syntax().text_range(), then_expr.syntax().text()) 132 edit.replace(replace_node.text_range(), then_expr.syntax().text())
123 } 133 }
124 _ => edit.replace(if_expr.syntax().text_range(), then_block.syntax().text()), 134 _ if replace_node != *if_expr.syntax() => {
135 // Dedent because if_expr is in a BlockExpr
136 let replace_with = then_block.dedent(1.into()).syntax().text();
137 edit.replace(replace_node.text_range(), replace_with)
138 }
139 _ => edit.replace(replace_node.text_range(), then_block.syntax().text()),
125 } 140 }
126 141
127 edit.insert(match_pat.syntax().text_range().end(), buf); 142 edit.insert(match_pat.syntax().text_range().end(), buf);
@@ -225,6 +240,33 @@ fn main() {
225 } 240 }
226 241
227 #[test] 242 #[test]
243 fn move_arm_cond_in_block_to_match_guard_works() {
244 check_assist(
245 move_arm_cond_to_match_guard,
246 r#"
247fn main() {
248 match 92 {
249 x => {
250 <|>if x > 10 {
251 false
252 }
253 },
254 _ => true
255 }
256}
257"#,
258 r#"
259fn main() {
260 match 92 {
261 x if x > 10 => false,
262 _ => true
263 }
264}
265"#,
266 );
267 }
268
269 #[test]
228 fn move_arm_cond_to_match_guard_if_let_not_works() { 270 fn move_arm_cond_to_match_guard_if_let_not_works() {
229 check_assist_not_applicable( 271 check_assist_not_applicable(
230 move_arm_cond_to_match_guard, 272 move_arm_cond_to_match_guard,
@@ -290,4 +332,35 @@ fn main() {
290"#, 332"#,
291 ); 333 );
292 } 334 }
335
336 #[test]
337 fn move_arm_cond_in_block_to_match_guard_if_multiline_body_works() {
338 check_assist(
339 move_arm_cond_to_match_guard,
340 r#"
341fn main() {
342 match 92 {
343 x => {
344 if x > 10 {
345 92;<|>
346 false
347 }
348 }
349 _ => true
350 }
351}
352"#,
353 r#"
354fn main() {
355 match 92 {
356 x if x > 10 => {
357 92;
358 false
359 }
360 _ => true
361 }
362}
363"#,
364 )
365 }
293} 366}
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index f436bdbbf..d5bc4e574 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -56,12 +56,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
56 ImportCandidate::QualifierStart(_) => { 56 ImportCandidate::QualifierStart(_) => {
57 mark::hit!(qualify_path_qualifier_start); 57 mark::hit!(qualify_path_qualifier_start);
58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?; 58 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
59 let segment = path.segment()?; 59 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
60 QualifyCandidate::QualifierStart(segment) 60 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
61 } 61 }
62 ImportCandidate::UnqualifiedName(_) => { 62 ImportCandidate::UnqualifiedName(_) => {
63 mark::hit!(qualify_path_unqualified_name); 63 mark::hit!(qualify_path_unqualified_name);
64 QualifyCandidate::UnqualifiedName 64 let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65 let generics = path.segment()?.generic_arg_list();
66 QualifyCandidate::UnqualifiedName(generics)
65 } 67 }
66 ImportCandidate::TraitAssocItem(_) => { 68 ImportCandidate::TraitAssocItem(_) => {
67 mark::hit!(qualify_path_trait_assoc_item); 69 mark::hit!(qualify_path_trait_assoc_item);
@@ -96,22 +98,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
96} 98}
97 99
98enum QualifyCandidate<'db> { 100enum QualifyCandidate<'db> {
99 QualifierStart(ast::PathSegment), 101 QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
100 UnqualifiedName, 102 UnqualifiedName(Option<ast::GenericArgList>),
101 TraitAssocItem(ast::Path, ast::PathSegment), 103 TraitAssocItem(ast::Path, ast::PathSegment),
102 TraitMethod(&'db RootDatabase, ast::MethodCallExpr), 104 TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
103} 105}
104 106
105impl QualifyCandidate<'_> { 107impl QualifyCandidate<'_> {
106 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 108 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
109 let import = mod_path_to_ast(&import);
107 match self { 110 match self {
108 QualifyCandidate::QualifierStart(segment) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
109 let import = mod_path_to_ast(&import); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
110 replacer(format!("{}::{}", import, segment)); 113 replacer(format!("{}{}::{}", import, generics, segment));
114 }
115 QualifyCandidate::UnqualifiedName(generics) => {
116 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
117 replacer(format!("{}{}", import.to_string(), generics));
111 } 118 }
112 QualifyCandidate::UnqualifiedName => replacer(mod_path_to_ast(&import).to_string()),
113 QualifyCandidate::TraitAssocItem(qualifier, segment) => { 119 QualifyCandidate::TraitAssocItem(qualifier, segment) => {
114 let import = mod_path_to_ast(&import);
115 replacer(format!("<{} as {}>::{}", qualifier, import, segment)); 120 replacer(format!("<{} as {}>::{}", qualifier, import, segment));
116 } 121 }
117 &QualifyCandidate::TraitMethod(db, ref mcall_expr) => { 122 &QualifyCandidate::TraitMethod(db, ref mcall_expr) => {
@@ -124,25 +129,27 @@ impl QualifyCandidate<'_> {
124 db: &RootDatabase, 129 db: &RootDatabase,
125 mcall_expr: &ast::MethodCallExpr, 130 mcall_expr: &ast::MethodCallExpr,
126 mut replacer: impl FnMut(String), 131 mut replacer: impl FnMut(String),
127 import: hir::ModPath, 132 import: ast::Path,
128 item: hir::ItemInNs, 133 item: hir::ItemInNs,
129 ) -> Option<()> { 134 ) -> Option<()> {
130 let receiver = mcall_expr.receiver()?; 135 let receiver = mcall_expr.receiver()?;
131 let trait_method_name = mcall_expr.name_ref()?; 136 let trait_method_name = mcall_expr.name_ref()?;
137 let generics =
138 mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
132 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args()); 139 let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
133 let trait_ = item_as_trait(item)?; 140 let trait_ = item_as_trait(item)?;
134 let method = find_trait_method(db, trait_, &trait_method_name)?; 141 let method = find_trait_method(db, trait_, &trait_method_name)?;
135 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) { 142 if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
136 let import = mod_path_to_ast(&import);
137 let receiver = match self_access { 143 let receiver = match self_access {
138 hir::Access::Shared => make::expr_ref(receiver, false), 144 hir::Access::Shared => make::expr_ref(receiver, false),
139 hir::Access::Exclusive => make::expr_ref(receiver, true), 145 hir::Access::Exclusive => make::expr_ref(receiver, true),
140 hir::Access::Owned => receiver, 146 hir::Access::Owned => receiver,
141 }; 147 };
142 replacer(format!( 148 replacer(format!(
143 "{}::{}{}", 149 "{}::{}{}{}",
144 import, 150 import,
145 trait_method_name, 151 trait_method_name,
152 generics,
146 match arg_list.clone() { 153 match arg_list.clone() {
147 Some(args) => make::arg_list(iter::once(receiver).chain(args)), 154 Some(args) => make::arg_list(iter::once(receiver).chain(args)),
148 None => make::arg_list(iter::once(receiver)), 155 None => make::arg_list(iter::once(receiver)),
@@ -1045,4 +1052,153 @@ fn main() {
1045", 1052",
1046 ); 1053 );
1047 } 1054 }
1055
1056 #[test]
1057 fn keep_generic_annotations() {
1058 check_assist(
1059 qualify_path,
1060 r"
1061//- /lib.rs crate:dep
1062pub mod generic { pub struct Thing<'a, T>(&'a T); }
1063
1064//- /main.rs crate:main deps:dep
1065fn foo() -> Thin<|>g<'static, ()> {}
1066
1067fn main() {}
1068",
1069 r"
1070fn foo() -> dep::generic::Thing<'static, ()> {}
1071
1072fn main() {}
1073",
1074 );
1075 }
1076
1077 #[test]
1078 fn keep_generic_annotations_leading_colon() {
1079 check_assist(
1080 qualify_path,
1081 r"
1082//- /lib.rs crate:dep
1083pub mod generic { pub struct Thing<'a, T>(&'a T); }
1084
1085//- /main.rs crate:main deps:dep
1086fn foo() -> Thin<|>g::<'static, ()> {}
1087
1088fn main() {}
1089",
1090 r"
1091fn foo() -> dep::generic::Thing::<'static, ()> {}
1092
1093fn main() {}
1094",
1095 );
1096 }
1097
1098 #[test]
1099 fn associated_struct_const_generic() {
1100 check_assist(
1101 qualify_path,
1102 r"
1103 mod test_mod {
1104 pub struct TestStruct<T> {}
1105 impl<T> TestStruct<T> {
1106 const TEST_CONST: u8 = 42;
1107 }
1108 }
1109
1110 fn main() {
1111 TestStruct::<()>::TEST_CONST<|>
1112 }
1113 ",
1114 r"
1115 mod test_mod {
1116 pub struct TestStruct<T> {}
1117 impl<T> TestStruct<T> {
1118 const TEST_CONST: u8 = 42;
1119 }
1120 }
1121
1122 fn main() {
1123 test_mod::TestStruct::<()>::TEST_CONST
1124 }
1125 ",
1126 );
1127 }
1128
1129 #[test]
1130 fn associated_trait_const_generic() {
1131 check_assist(
1132 qualify_path,
1133 r"
1134 mod test_mod {
1135 pub trait TestTrait {
1136 const TEST_CONST: u8;
1137 }
1138 pub struct TestStruct<T> {}
1139 impl<T> TestTrait for TestStruct<T> {
1140 const TEST_CONST: u8 = 42;
1141 }
1142 }
1143
1144 fn main() {
1145 test_mod::TestStruct::<()>::TEST_CONST<|>
1146 }
1147 ",
1148 r"
1149 mod test_mod {
1150 pub trait TestTrait {
1151 const TEST_CONST: u8;
1152 }
1153 pub struct TestStruct<T> {}
1154 impl<T> TestTrait for TestStruct<T> {
1155 const TEST_CONST: u8 = 42;
1156 }
1157 }
1158
1159 fn main() {
1160 <test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1161 }
1162 ",
1163 );
1164 }
1165
1166 #[test]
1167 fn trait_method_generic() {
1168 check_assist(
1169 qualify_path,
1170 r"
1171 mod test_mod {
1172 pub trait TestTrait {
1173 fn test_method<T>(&self);
1174 }
1175 pub struct TestStruct {}
1176 impl TestTrait for TestStruct {
1177 fn test_method<T>(&self) {}
1178 }
1179 }
1180
1181 fn main() {
1182 let test_struct = test_mod::TestStruct {};
1183 test_struct.test_meth<|>od::<()>()
1184 }
1185 ",
1186 r"
1187 mod test_mod {
1188 pub trait TestTrait {
1189 fn test_method<T>(&self);
1190 }
1191 pub struct TestStruct {}
1192 impl TestTrait for TestStruct {
1193 fn test_method<T>(&self) {}
1194 }
1195 }
1196
1197 fn main() {
1198 let test_struct = test_mod::TestStruct {};
1199 test_mod::TestTrait::test_method::<()>(&test_struct)
1200 }
1201 ",
1202 );
1203 }
1048} 1204}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 70a651e10..b804e495d 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -200,7 +200,6 @@ mod handlers {
200 move_guard::move_guard_to_arm_body, 200 move_guard::move_guard_to_arm_body,
201 qualify_path::qualify_path, 201 qualify_path::qualify_path,
202 raw_string::add_hash, 202 raw_string::add_hash,
203 raw_string::make_raw_string,
204 raw_string::make_usual_string, 203 raw_string::make_usual_string,
205 raw_string::remove_hash, 204 raw_string::remove_hash,
206 remove_dbg::remove_dbg, 205 remove_dbg::remove_dbg,
@@ -211,13 +210,15 @@ mod handlers {
211 replace_impl_trait_with_generic::replace_impl_trait_with_generic, 210 replace_impl_trait_with_generic::replace_impl_trait_with_generic,
212 replace_let_with_if_let::replace_let_with_if_let, 211 replace_let_with_if_let::replace_let_with_if_let,
213 replace_qualified_name_with_use::replace_qualified_name_with_use, 212 replace_qualified_name_with_use::replace_qualified_name_with_use,
214 replace_string_with_char::replace_string_with_char,
215 replace_unwrap_with_match::replace_unwrap_with_match, 213 replace_unwrap_with_match::replace_unwrap_with_match,
216 split_import::split_import, 214 split_import::split_import,
217 unwrap_block::unwrap_block, 215 unwrap_block::unwrap_block,
218 // These are manually sorted for better priorities 216 // These are manually sorted for better priorities
219 add_missing_impl_members::add_missing_impl_members, 217 add_missing_impl_members::add_missing_impl_members,
220 add_missing_impl_members::add_missing_default_members, 218 add_missing_impl_members::add_missing_default_members,
219 //
220 replace_string_with_char::replace_string_with_char,
221 raw_string::make_raw_string,
221 // Are you sure you want to add new assist here, and not to the 222 // Are you sure you want to add new assist here, and not to the
222 // sorted list above? 223 // sorted list above?
223 ] 224 ]
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index 23db3a74b..f47edbb76 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -26,13 +26,13 @@ pub(crate) enum ImportCandidate {
26 26
27#[derive(Debug)] 27#[derive(Debug)]
28pub(crate) struct TraitImportCandidate { 28pub(crate) struct TraitImportCandidate {
29 pub ty: hir::Type, 29 pub(crate) ty: hir::Type,
30 pub name: ast::NameRef, 30 pub(crate) name: ast::NameRef,
31} 31}
32 32
33#[derive(Debug)] 33#[derive(Debug)]
34pub(crate) struct PathImportCandidate { 34pub(crate) struct PathImportCandidate {
35 pub name: ast::NameRef, 35 pub(crate) name: ast::NameRef,
36} 36}
37 37
38#[derive(Debug)] 38#[derive(Debug)]
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 033fbcedc..a76bd5ebf 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -17,13 +17,13 @@ use syntax::{
17use test_utils::mark; 17use test_utils::mark;
18 18
19#[derive(Debug)] 19#[derive(Debug)]
20pub enum ImportScope { 20pub(crate) enum ImportScope {
21 File(ast::SourceFile), 21 File(ast::SourceFile),
22 Module(ast::ItemList), 22 Module(ast::ItemList),
23} 23}
24 24
25impl ImportScope { 25impl ImportScope {
26 pub fn from(syntax: SyntaxNode) -> Option<Self> { 26 pub(crate) fn from(syntax: SyntaxNode) -> Option<Self> {
27 if let Some(module) = ast::Module::cast(syntax.clone()) { 27 if let Some(module) = ast::Module::cast(syntax.clone()) {
28 module.item_list().map(ImportScope::Module) 28 module.item_list().map(ImportScope::Module)
29 } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) { 29 } else if let this @ Some(_) = ast::SourceFile::cast(syntax.clone()) {