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/handlers/add_turbo_fish.rs22
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs10
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs51
-rw-r--r--crates/ide_assists/src/handlers/change_visibility.rs7
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs31
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs6
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs15
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs6
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs11
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs373
-rw-r--r--crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs7
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs129
-rw-r--r--crates/ide_assists/src/handlers/infer_function_return_type.rs37
-rw-r--r--crates/ide_assists/src/handlers/inline_function.rs5
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs19
-rw-r--r--crates/ide_assists/src/handlers/move_module_to_file.rs5
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs52
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs63
-rw-r--r--crates/ide_assists/src/handlers/raw_string.rs7
-rw-r--r--crates/ide_assists/src/handlers/remove_unused_param.rs6
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs7
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs7
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs33
-rw-r--r--crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs5
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs7
-rw-r--r--crates/ide_assists/src/handlers/replace_string_with_char.rs136
-rw-r--r--crates/ide_assists/src/handlers/unmerge_use.rs5
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs5
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests.rs1
-rw-r--r--crates/ide_assists/src/tests/generated.rs31
31 files changed, 832 insertions, 269 deletions
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs
index a08b55ebb..3b6efbab4 100644
--- a/crates/ide_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs
@@ -1,6 +1,5 @@
1use ide_db::defs::{Definition, NameRefClass}; 1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T}; 2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4 3
5use crate::{ 4use crate::{
6 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
@@ -30,13 +29,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
30 if arg_list.args().count() > 0 { 29 if arg_list.args().count() > 0 {
31 return None; 30 return None;
32 } 31 }
33 mark::hit!(add_turbo_fish_after_call); 32 cov_mark::hit!(add_turbo_fish_after_call);
34 mark::hit!(add_type_ascription_after_call); 33 cov_mark::hit!(add_type_ascription_after_call);
35 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) 34 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
36 })?; 35 })?;
37 let next_token = ident.next_token()?; 36 let next_token = ident.next_token()?;
38 if next_token.kind() == T![::] { 37 if next_token.kind() == T![::] {
39 mark::hit!(add_turbo_fish_one_fish_is_enough); 38 cov_mark::hit!(add_turbo_fish_one_fish_is_enough);
40 return None; 39 return None;
41 } 40 }
42 let name_ref = ast::NameRef::cast(ident.parent())?; 41 let name_ref = ast::NameRef::cast(ident.parent())?;
@@ -50,7 +49,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
50 }; 49 };
51 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); 50 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
52 if generics.is_empty() { 51 if generics.is_empty() {
53 mark::hit!(add_turbo_fish_non_generic); 52 cov_mark::hit!(add_turbo_fish_non_generic);
54 return None; 53 return None;
55 } 54 }
56 55
@@ -67,7 +66,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
67 }, 66 },
68 )? 67 )?
69 } else { 68 } else {
70 mark::hit!(add_type_ascription_already_typed); 69 cov_mark::hit!(add_type_ascription_already_typed);
71 } 70 }
72 } 71 }
73 72
@@ -87,7 +86,6 @@ mod tests {
87 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; 86 use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable};
88 87
89 use super::*; 88 use super::*;
90 use test_utils::mark;
91 89
92 #[test] 90 #[test]
93 fn add_turbo_fish_function() { 91 fn add_turbo_fish_function() {
@@ -110,7 +108,7 @@ fn main() {
110 108
111 #[test] 109 #[test]
112 fn add_turbo_fish_after_call() { 110 fn add_turbo_fish_after_call() {
113 mark::check!(add_turbo_fish_after_call); 111 cov_mark::check!(add_turbo_fish_after_call);
114 check_assist( 112 check_assist(
115 add_turbo_fish, 113 add_turbo_fish,
116 r#" 114 r#"
@@ -155,7 +153,7 @@ fn main() {
155 153
156 #[test] 154 #[test]
157 fn add_turbo_fish_one_fish_is_enough() { 155 fn add_turbo_fish_one_fish_is_enough() {
158 mark::check!(add_turbo_fish_one_fish_is_enough); 156 cov_mark::check!(add_turbo_fish_one_fish_is_enough);
159 check_assist_not_applicable( 157 check_assist_not_applicable(
160 add_turbo_fish, 158 add_turbo_fish,
161 r#" 159 r#"
@@ -169,7 +167,7 @@ fn main() {
169 167
170 #[test] 168 #[test]
171 fn add_turbo_fish_non_generic() { 169 fn add_turbo_fish_non_generic() {
172 mark::check!(add_turbo_fish_non_generic); 170 cov_mark::check!(add_turbo_fish_non_generic);
173 check_assist_not_applicable( 171 check_assist_not_applicable(
174 add_turbo_fish, 172 add_turbo_fish,
175 r#" 173 r#"
@@ -203,7 +201,7 @@ fn main() {
203 201
204 #[test] 202 #[test]
205 fn add_type_ascription_after_call() { 203 fn add_type_ascription_after_call() {
206 mark::check!(add_type_ascription_after_call); 204 cov_mark::check!(add_type_ascription_after_call);
207 check_assist_by_label( 205 check_assist_by_label(
208 add_turbo_fish, 206 add_turbo_fish,
209 r#" 207 r#"
@@ -250,7 +248,7 @@ fn main() {
250 248
251 #[test] 249 #[test]
252 fn add_type_ascription_already_typed() { 250 fn add_type_ascription_already_typed() {
253 mark::check!(add_type_ascription_already_typed); 251 cov_mark::check!(add_type_ascription_already_typed);
254 check_assist( 252 check_assist(
255 add_turbo_fish, 253 add_turbo_fish,
256 r#" 254 r#"
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs
index 128b1eb56..a1c339603 100644
--- a/crates/ide_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide_assists/src/handlers/apply_demorgan.rs
@@ -1,5 +1,4 @@
1use syntax::ast::{self, AstNode}; 1use syntax::ast::{self, AstNode};
2use test_utils::mark;
3 2
4use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
5 4
@@ -64,10 +63,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
64 edit.replace(lhs_range, not_lhs.syntax().text()); 63 edit.replace(lhs_range, not_lhs.syntax().text());
65 edit.replace(rhs_range, not_rhs.syntax().text()); 64 edit.replace(rhs_range, not_rhs.syntax().text());
66 if let Some(neg_expr) = neg_expr { 65 if let Some(neg_expr) = neg_expr {
67 mark::hit!(demorgan_double_negation); 66 cov_mark::hit!(demorgan_double_negation);
68 edit.replace(neg_expr.op_token().unwrap().text_range(), ""); 67 edit.replace(neg_expr.op_token().unwrap().text_range(), "");
69 } else { 68 } else {
70 mark::hit!(demorgan_double_parens); 69 cov_mark::hit!(demorgan_double_parens);
71 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); 70 edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
72 } 71 }
73 } else { 72 } else {
@@ -90,7 +89,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use ide_db::helpers::FamousDefs; 91 use ide_db::helpers::FamousDefs;
93 use test_utils::mark;
94 92
95 use super::*; 93 use super::*;
96 94
@@ -188,13 +186,13 @@ fn f() {
188 186
189 #[test] 187 #[test]
190 fn demorgan_doesnt_double_negation() { 188 fn demorgan_doesnt_double_negation() {
191 mark::check!(demorgan_double_negation); 189 cov_mark::check!(demorgan_double_negation);
192 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") 190 check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
193 } 191 }
194 192
195 #[test] 193 #[test]
196 fn demorgan_doesnt_double_parens() { 194 fn demorgan_doesnt_double_parens() {
197 mark::check!(demorgan_double_parens); 195 cov_mark::check!(demorgan_double_parens);
198 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") 196 check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
199 } 197 }
200} 198}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index dc38f90e9..7caee8df0 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -1,7 +1,7 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate}, 2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 mod_path_to_ast, 4 item_name, mod_path_to_ast,
5}; 5};
6use syntax::{ast, AstNode, SyntaxNode}; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
@@ -92,15 +92,19 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
92 let range = ctx.sema.original_range(&syntax_under_caret).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate()); 93 let group = import_group_message(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for (import, _) in proposed_imports { 95 for import in proposed_imports {
96 let name = match item_name(ctx.db(), import.original_item) {
97 Some(name) => name,
98 None => continue,
99 };
96 acc.add_group( 100 acc.add_group(
97 &group, 101 &group,
98 AssistId("auto_import", AssistKind::QuickFix), 102 AssistId("auto_import", AssistKind::QuickFix),
99 format!("Import `{}`", &import), 103 format!("Import `{}`", name),
100 range, 104 range,
101 |builder| { 105 |builder| {
102 let rewriter = 106 let rewriter =
103 insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); 107 insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use);
104 builder.rewrite(rewriter); 108 builder.rewrite(rewriter);
105 }, 109 },
106 ); 110 );
@@ -126,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel {
126 let name = match import_candidate { 130 let name = match import_candidate {
127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), 131 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
128 ImportCandidate::TraitAssocItem(candidate) => { 132 ImportCandidate::TraitAssocItem(candidate) => {
129 format!("Import a trait for item {}", candidate.name.text()) 133 format!("Import a trait for item {}", candidate.assoc_item_name.text())
130 } 134 }
131 ImportCandidate::TraitMethod(candidate) => { 135 ImportCandidate::TraitMethod(candidate) => {
132 format!("Import a trait for method {}", candidate.name.text()) 136 format!("Import a trait for method {}", candidate.assoc_item_name.text())
133 } 137 }
134 }; 138 };
135 GroupLabel(name) 139 GroupLabel(name)
@@ -222,41 +226,6 @@ mod tests {
222 } 226 }
223 227
224 #[test] 228 #[test]
225 fn auto_imports_are_merged() {
226 check_assist(
227 auto_import,
228 r"
229 use PubMod::PubStruct1;
230
231 struct Test {
232 test: Pub$0Struct2<u8>,
233 }
234
235 pub mod PubMod {
236 pub struct PubStruct1;
237 pub struct PubStruct2<T> {
238 _t: T,
239 }
240 }
241 ",
242 r"
243 use PubMod::{PubStruct1, PubStruct2};
244
245 struct Test {
246 test: PubStruct2<u8>,
247 }
248
249 pub mod PubMod {
250 pub struct PubStruct1;
251 pub struct PubStruct2<T> {
252 _t: T,
253 }
254 }
255 ",
256 );
257 }
258
259 #[test]
260 fn applicable_when_found_multiple_imports() { 229 fn applicable_when_found_multiple_imports() {
261 check_assist( 230 check_assist(
262 auto_import, 231 auto_import,
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs
index ac8c44124..ec99a5505 100644
--- a/crates/ide_assists/src/handlers/change_visibility.rs
+++ b/crates/ide_assists/src/handlers/change_visibility.rs
@@ -4,7 +4,6 @@ use syntax::{
4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, 4 SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY},
5 T, 5 T,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; 8use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -56,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
56 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { 55 } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() {
57 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; 56 let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?;
58 if field.name()? != field_name { 57 if field.name()? != field_name {
59 mark::hit!(change_visibility_field_false_positive); 58 cov_mark::hit!(change_visibility_field_false_positive);
60 return None; 59 return None;
61 } 60 }
62 if field.visibility().is_some() { 61 if field.visibility().is_some() {
@@ -110,8 +109,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
110 109
111#[cfg(test)] 110#[cfg(test)]
112mod tests { 111mod tests {
113 use test_utils::mark;
114
115 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 112 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
116 113
117 use super::*; 114 use super::*;
@@ -139,7 +136,7 @@ mod tests {
139 136
140 #[test] 137 #[test]
141 fn change_visibility_field_false_positive() { 138 fn change_visibility_field_false_positive() {
142 mark::check!(change_visibility_field_false_positive); 139 cov_mark::check!(change_visibility_field_false_positive);
143 check_assist_not_applicable( 140 check_assist_not_applicable(
144 change_visibility, 141 change_visibility,
145 r"struct S { field: [(); { let $0x = ();}] }", 142 r"struct S { field: [(); { let $0x = ();}] }",
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 9f34cc725..dd4501709 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -20,7 +20,6 @@ use syntax::{
20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 20 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 21 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
22}; 22};
23use test_utils::mark;
24 23
25use crate::{ 24use crate::{
26 assist_context::{AssistContext, Assists}, 25 assist_context::{AssistContext, Assists},
@@ -59,7 +58,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
59 58
60 let node = ctx.covering_element(); 59 let node = ctx.covering_element();
61 if node.kind() == COMMENT { 60 if node.kind() == COMMENT {
62 mark::hit!(extract_function_in_comment_is_not_applicable); 61 cov_mark::hit!(extract_function_in_comment_is_not_applicable);
63 return None; 62 return None;
64 } 63 }
65 64
@@ -112,7 +111,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
112 111
113 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); 112 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent);
114 let insert_offset = insert_after.text_range().end(); 113 let insert_offset = insert_after.text_range().end();
115 builder.insert(insert_offset, fn_def); 114 match ctx.config.snippet_cap {
115 Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def),
116 None => builder.insert(insert_offset, fn_def),
117 }
116 }, 118 },
117 ) 119 )
118} 120}
@@ -194,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
194 if let Some(kind) = expr_err_kind(&expr, ctx) { 196 if let Some(kind) = expr_err_kind(&expr, ctx) {
195 Some(FlowKind::TryReturn { expr, kind }) 197 Some(FlowKind::TryReturn { expr, kind })
196 } else { 198 } else {
197 mark::hit!(external_control_flow_try_and_return_non_err); 199 cov_mark::hit!(external_control_flow_try_and_return_non_err);
198 return None; 200 return None;
199 } 201 }
200 } 202 }
201 None => return None, 203 None => return None,
202 }, 204 },
203 (Some(_), _, _, _) => { 205 (Some(_), _, _, _) => {
204 mark::hit!(external_control_flow_try_and_bc); 206 cov_mark::hit!(external_control_flow_try_and_bc);
205 return None; 207 return None;
206 } 208 }
207 (None, Some(r), None, None) => match r.expr() { 209 (None, Some(r), None, None) => match r.expr() {
@@ -209,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con
209 None => Some(FlowKind::Return), 211 None => Some(FlowKind::Return),
210 }, 212 },
211 (None, Some(_), _, _) => { 213 (None, Some(_), _, _) => {
212 mark::hit!(external_control_flow_return_and_bc); 214 cov_mark::hit!(external_control_flow_return_and_bc);
213 return None; 215 return None;
214 } 216 }
215 (None, None, Some(_), Some(_)) => { 217 (None, None, Some(_), Some(_)) => {
216 mark::hit!(external_control_flow_break_and_continue); 218 cov_mark::hit!(external_control_flow_break_and_continue);
217 return None; 219 return None;
218 } 220 }
219 (None, None, Some(b), None) => match b.expr() { 221 (None, None, Some(b), None) => match b.expr() {
@@ -1079,7 +1081,10 @@ fn format_function(
1079 let params = make_param_list(ctx, module, fun); 1081 let params = make_param_list(ctx, module, fun);
1080 let ret_ty = make_ret_ty(ctx, module, fun); 1082 let ret_ty = make_ret_ty(ctx, module, fun);
1081 let body = make_body(ctx, old_indent, new_indent, fun); 1083 let body = make_body(ctx, old_indent, new_indent, fun);
1082 format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params); 1084 match ctx.config.snippet_cap {
1085 Some(_) => format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params),
1086 None => format_to!(fn_def, "\n\n{}fn {}{}", new_indent, fun.name, params),
1087 }
1083 if let Some(ret_ty) = ret_ty { 1088 if let Some(ret_ty) = ret_ty {
1084 format_to!(fn_def, " {}", ret_ty); 1089 format_to!(fn_def, " {}", ret_ty);
1085 } 1090 }
@@ -1831,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 {
1831 1836
1832 #[test] 1837 #[test]
1833 fn in_comment_is_not_applicable() { 1838 fn in_comment_is_not_applicable() {
1834 mark::check!(extract_function_in_comment_is_not_applicable); 1839 cov_mark::check!(extract_function_in_comment_is_not_applicable);
1835 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); 1840 check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }");
1836 } 1841 }
1837 1842
@@ -2816,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> {
2816 2821
2817 #[test] 2822 #[test]
2818 fn break_and_continue() { 2823 fn break_and_continue() {
2819 mark::check!(external_control_flow_break_and_continue); 2824 cov_mark::check!(external_control_flow_break_and_continue);
2820 check_assist_not_applicable( 2825 check_assist_not_applicable(
2821 extract_function, 2826 extract_function,
2822 r##" 2827 r##"
@@ -2836,7 +2841,7 @@ fn foo() {
2836 2841
2837 #[test] 2842 #[test]
2838 fn return_and_break() { 2843 fn return_and_break() {
2839 mark::check!(external_control_flow_return_and_bc); 2844 cov_mark::check!(external_control_flow_return_and_bc);
2840 check_assist_not_applicable( 2845 check_assist_not_applicable(
2841 extract_function, 2846 extract_function,
2842 r##" 2847 r##"
@@ -3335,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> {
3335 3340
3336 #[test] 3341 #[test]
3337 fn try_and_break() { 3342 fn try_and_break() {
3338 mark::check!(external_control_flow_try_and_bc); 3343 cov_mark::check!(external_control_flow_try_and_bc);
3339 check_assist_not_applicable( 3344 check_assist_not_applicable(
3340 extract_function, 3345 extract_function,
3341 r##" 3346 r##"
@@ -3357,7 +3362,7 @@ fn foo() -> Option<()> {
3357 3362
3358 #[test] 3363 #[test]
3359 fn try_and_return_ok() { 3364 fn try_and_return_ok() {
3360 mark::check!(external_control_flow_try_and_return_non_err); 3365 cov_mark::check!(external_control_flow_try_and_return_non_err);
3361 check_assist_not_applicable( 3366 check_assist_not_applicable(
3362 extract_function, 3367 extract_function,
3363 r##" 3368 r##"
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 5c7678b53..335e0ed95 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
@@ -1,7 +1,7 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{Module, ModuleDef, Name, Variant};
5use ide_db::{ 5use ide_db::{
6 defs::Definition, 6 defs::Definition,
7 helpers::{ 7 helpers::{
@@ -133,7 +133,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
133 ), 133 ),
134 _ => false, 134 _ => false,
135 }) 135 })
136 .any(|(name, _)| name == variant_name.as_name()) 136 .any(|(name, _)| name.to_string() == variant_name.to_string())
137} 137}
138 138
139fn insert_import( 139fn insert_import(
@@ -154,7 +154,7 @@ fn insert_import(
154 mod_path.pop_segment(); 154 mod_path.pop_segment();
155 mod_path.push_segment(variant_hir_name.clone()); 155 mod_path.push_segment(variant_hir_name.clone());
156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; 156 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 157 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use);
158 } 158 }
159 Some(()) 159 Some(())
160} 160}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 312ac7ac4..7a32483dc 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -6,7 +6,6 @@ use syntax::{
6 }, 6 },
7 SyntaxNode, 7 SyntaxNode,
8}; 8};
9use test_utils::mark;
10 9
11use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; 10use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
12 11
@@ -32,7 +31,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
32 } 31 }
33 let node = ctx.covering_element(); 32 let node = ctx.covering_element();
34 if node.kind() == COMMENT { 33 if node.kind() == COMMENT {
35 mark::hit!(extract_var_in_comment_is_not_applicable); 34 cov_mark::hit!(extract_var_in_comment_is_not_applicable);
36 return None; 35 return None;
37 } 36 }
38 let to_extract = node.ancestors().find_map(valid_target_expr)?; 37 let to_extract = node.ancestors().find_map(valid_target_expr)?;
@@ -69,7 +68,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
69 format_to!(buf, "{}", to_extract.syntax()); 68 format_to!(buf, "{}", to_extract.syntax());
70 69
71 if let Anchor::Replace(stmt) = anchor { 70 if let Anchor::Replace(stmt) = anchor {
72 mark::hit!(test_extract_var_expr_stmt); 71 cov_mark::hit!(test_extract_var_expr_stmt);
73 if stmt.semicolon_token().is_none() { 72 if stmt.semicolon_token().is_none() {
74 buf.push_str(";"); 73 buf.push_str(";");
75 } 74 }
@@ -142,7 +141,7 @@ impl Anchor {
142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) 141 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr())
143 { 142 {
144 if expr.syntax() == &node { 143 if expr.syntax() == &node {
145 mark::hit!(test_extract_var_last_expr); 144 cov_mark::hit!(test_extract_var_last_expr);
146 return Some(Anchor::Before(node)); 145 return Some(Anchor::Before(node));
147 } 146 }
148 } 147 }
@@ -175,8 +174,6 @@ impl Anchor {
175 174
176#[cfg(test)] 175#[cfg(test)]
177mod tests { 176mod tests {
178 use test_utils::mark;
179
180 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 177 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
181 178
182 use super::*; 179 use super::*;
@@ -199,13 +196,13 @@ fn foo() {
199 196
200 #[test] 197 #[test]
201 fn extract_var_in_comment_is_not_applicable() { 198 fn extract_var_in_comment_is_not_applicable() {
202 mark::check!(extract_var_in_comment_is_not_applicable); 199 cov_mark::check!(extract_var_in_comment_is_not_applicable);
203 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); 200 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
204 } 201 }
205 202
206 #[test] 203 #[test]
207 fn test_extract_var_expr_stmt() { 204 fn test_extract_var_expr_stmt() {
208 mark::check!(test_extract_var_expr_stmt); 205 cov_mark::check!(test_extract_var_expr_stmt);
209 check_assist( 206 check_assist(
210 extract_variable, 207 extract_variable,
211 r#" 208 r#"
@@ -250,7 +247,7 @@ fn foo() {
250 247
251 #[test] 248 #[test]
252 fn test_extract_var_last_expr() { 249 fn test_extract_var_last_expr() {
253 mark::check!(test_extract_var_last_expr); 250 cov_mark::check!(test_extract_var_last_expr);
254 check_assist( 251 check_assist(
255 extract_variable, 252 extract_variable,
256 r#" 253 r#"
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 7086e47d2..878b3a3fa 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -5,7 +5,6 @@ use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use itertools::Itertools; 6use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8use test_utils::mark;
9 8
10use crate::{ 9use crate::{
11 utils::{does_pat_match_variant, render_snippet, Cursor}, 10 utils::{does_pat_match_variant, render_snippet, Cursor},
@@ -62,7 +61,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
62 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
63 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
64 // Match `Some` variant first. 63 // Match `Some` variant first.
65 mark::hit!(option_order); 64 cov_mark::hit!(option_order);
66 variants.reverse() 65 variants.reverse()
67 } 66 }
68 variants 67 variants
@@ -195,7 +194,6 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
195#[cfg(test)] 194#[cfg(test)]
196mod tests { 195mod tests {
197 use ide_db::helpers::FamousDefs; 196 use ide_db::helpers::FamousDefs;
198 use test_utils::mark;
199 197
200 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 198 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
201 199
@@ -730,7 +728,7 @@ fn main() {
730 728
731 #[test] 729 #[test]
732 fn option_order() { 730 fn option_order() {
733 mark::check!(option_order); 731 cov_mark::check!(option_order);
734 let before = r#" 732 let before = r#"
735fn foo(opt: Option<i32>) { 733fn foo(opt: Option<i32>) {
736 match opt$0 { 734 match opt$0 {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
index 6a2ab9596..588ee1350 100644
--- a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -38,12 +37,12 @@ pub(crate) fn generate_default_from_enum_variant(
38 let variant_name = variant.name()?; 37 let variant_name = variant.name()?;
39 let enum_name = variant.parent_enum().name()?; 38 let enum_name = variant.parent_enum().name()?;
40 if !matches!(variant.kind(), ast::StructKind::Unit) { 39 if !matches!(variant.kind(), ast::StructKind::Unit) {
41 mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); 40 cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42 return None; 41 return None;
43 } 42 }
44 43
45 if existing_default_impl(&ctx.sema, &variant).is_some() { 44 if existing_default_impl(&ctx.sema, &variant).is_some() {
46 mark::hit!(test_gen_default_impl_already_exists); 45 cov_mark::hit!(test_gen_default_impl_already_exists);
47 return None; 46 return None;
48 } 47 }
49 48
@@ -89,8 +88,6 @@ fn existing_default_impl(
89 88
90#[cfg(test)] 89#[cfg(test)]
91mod tests { 90mod tests {
92 use test_utils::mark;
93
94 use crate::tests::{check_assist, check_assist_not_applicable}; 91 use crate::tests::{check_assist, check_assist_not_applicable};
95 92
96 use super::*; 93 use super::*;
@@ -127,7 +124,7 @@ impl Default for Variant {
127 124
128 #[test] 125 #[test]
129 fn test_generate_default_already_implemented() { 126 fn test_generate_default_already_implemented() {
130 mark::check!(test_gen_default_impl_already_exists); 127 cov_mark::check!(test_gen_default_impl_already_exists);
131 check_not_applicable( 128 check_not_applicable(
132 r#" 129 r#"
133enum Variant { 130enum Variant {
@@ -146,7 +143,7 @@ impl Default for Variant {
146 143
147 #[test] 144 #[test]
148 fn test_add_from_impl_no_element() { 145 fn test_add_from_impl_no_element() {
149 mark::check!(test_gen_default_on_non_unit_variant_not_implemented); 146 cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
150 check_not_applicable( 147 check_not_applicable(
151 r#" 148 r#"
152enum Variant { 149enum Variant {
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs
new file mode 100644
index 000000000..81c54ba3e
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs
@@ -0,0 +1,373 @@
1use crate::{
2 assist_context::{AssistContext, Assists},
3 AssistId,
4};
5use ide_db::helpers::FamousDefs;
6use syntax::{
7 ast::{self, Impl, NameOwner},
8 AstNode,
9};
10
11// Assist: generate_default_from_new
12//
13// Generates default implementation from new method.
14//
15// ```
16// struct Example { _inner: () }
17//
18// impl Example {
19// pub fn n$0ew() -> Self {
20// Self { _inner: () }
21// }
22// }
23// ```
24// ->
25// ```
26// struct Example { _inner: () }
27//
28// impl Example {
29// pub fn new() -> Self {
30// Self { _inner: () }
31// }
32// }
33//
34// impl Default for Example {
35// fn default() -> Self {
36// Self::new()
37// }
38// }
39// ```
40pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
42 let fn_name = fn_node.name()?;
43
44 if fn_name.text() != "new" {
45 cov_mark::hit!(other_function_than_new);
46 return None;
47 }
48
49 if fn_node.param_list()?.params().next().is_some() {
50 cov_mark::hit!(new_function_with_parameters);
51 return None;
52 }
53
54 let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
55 if is_default_implemented(ctx, &impl_) {
56 cov_mark::hit!(default_block_is_already_present);
57 cov_mark::hit!(struct_in_module_with_default);
58 return None;
59 }
60
61 let insert_location = impl_.syntax().text_range();
62
63 acc.add(
64 AssistId("generate_default_from_new", crate::AssistKind::Generate),
65 "Generate a Default impl from a new fn",
66 insert_location,
67 move |builder| {
68 let code = default_fn_node_for_new(impl_);
69 builder.insert(insert_location.end(), code);
70 },
71 )
72}
73
74fn default_fn_node_for_new(impl_: Impl) -> String {
75 format!(
76 "
77
78impl Default for {} {{
79 fn default() -> Self {{
80 Self::new()
81 }}
82}}",
83 impl_.self_ty().unwrap().syntax().text()
84 )
85}
86
87fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
88 let db = ctx.sema.db;
89 let impl_ = ctx.sema.to_def(impl_);
90 let impl_def = match impl_ {
91 Some(value) => value,
92 None => return false,
93 };
94
95 let ty = impl_def.target_ty(db);
96 let krate = impl_def.module(db).krate();
97 let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
98 let default_trait = match default {
99 Some(value) => value,
100 None => return false,
101 };
102
103 ty.impls_trait(db, default_trait, &[])
104}
105
106#[cfg(test)]
107mod tests {
108 use ide_db::helpers::FamousDefs;
109
110 use crate::tests::{check_assist, check_assist_not_applicable};
111
112 use super::*;
113
114 #[test]
115 fn generate_default() {
116 check_pass(
117 r#"
118struct Example { _inner: () }
119
120impl Example {
121 pub fn ne$0w() -> Self {
122 Self { _inner: () }
123 }
124}
125
126fn main() {}
127"#,
128 r#"
129struct Example { _inner: () }
130
131impl Example {
132 pub fn new() -> Self {
133 Self { _inner: () }
134 }
135}
136
137impl Default for Example {
138 fn default() -> Self {
139 Self::new()
140 }
141}
142
143fn main() {}
144"#,
145 );
146 }
147
148 #[test]
149 fn generate_default2() {
150 check_pass(
151 r#"
152struct Test { value: u32 }
153
154impl Test {
155 pub fn ne$0w() -> Self {
156 Self { value: 0 }
157 }
158}
159"#,
160 r#"
161struct Test { value: u32 }
162
163impl Test {
164 pub fn new() -> Self {
165 Self { value: 0 }
166 }
167}
168
169impl Default for Test {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174"#,
175 );
176 }
177
178 #[test]
179 fn new_function_with_parameters() {
180 cov_mark::check!(new_function_with_parameters);
181 check_not_applicable(
182 r#"
183struct Example { _inner: () }
184
185impl Example {
186 pub fn $0new(value: ()) -> Self {
187 Self { _inner: value }
188 }
189}
190"#,
191 );
192 }
193
194 #[test]
195 fn other_function_than_new() {
196 cov_mark::check!(other_function_than_new);
197 check_not_applicable(
198 r#"
199struct Example { _inner: () }
200
201impl Example {
202 pub fn a$0dd() -> Self {
203 Self { _inner: () }
204 }
205}
206
207"#,
208 );
209 }
210
211 #[test]
212 fn default_block_is_already_present() {
213 cov_mark::check!(default_block_is_already_present);
214 check_not_applicable(
215 r#"
216struct Example { _inner: () }
217
218impl Example {
219 pub fn n$0ew() -> Self {
220 Self { _inner: () }
221 }
222}
223
224impl Default for Example {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229"#,
230 );
231 }
232
233 #[test]
234 fn standalone_new_function() {
235 check_not_applicable(
236 r#"
237fn n$0ew() -> u32 {
238 0
239}
240"#,
241 );
242 }
243
244 #[test]
245 fn multiple_struct_blocks() {
246 check_pass(
247 r#"
248struct Example { _inner: () }
249struct Test { value: u32 }
250
251impl Example {
252 pub fn new$0() -> Self {
253 Self { _inner: () }
254 }
255}
256"#,
257 r#"
258struct Example { _inner: () }
259struct Test { value: u32 }
260
261impl Example {
262 pub fn new() -> Self {
263 Self { _inner: () }
264 }
265}
266
267impl Default for Example {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272"#,
273 );
274 }
275
276 #[test]
277 fn when_struct_is_after_impl() {
278 check_pass(
279 r#"
280impl Example {
281 pub fn $0new() -> Self {
282 Self { _inner: () }
283 }
284}
285
286struct Example { _inner: () }
287"#,
288 r#"
289impl Example {
290 pub fn new() -> Self {
291 Self { _inner: () }
292 }
293}
294
295impl Default for Example {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301struct Example { _inner: () }
302"#,
303 );
304 }
305
306 #[test]
307 fn struct_in_module() {
308 check_pass(
309 r#"
310mod test {
311 struct Example { _inner: () }
312
313 impl Example {
314 pub fn n$0ew() -> Self {
315 Self { _inner: () }
316 }
317 }
318}
319"#,
320 r#"
321mod test {
322 struct Example { _inner: () }
323
324 impl Example {
325 pub fn new() -> Self {
326 Self { _inner: () }
327 }
328 }
329
330impl Default for Example {
331 fn default() -> Self {
332 Self::new()
333 }
334}
335}
336"#,
337 );
338 }
339
340 #[test]
341 fn struct_in_module_with_default() {
342 cov_mark::check!(struct_in_module_with_default);
343 check_not_applicable(
344 r#"
345mod test {
346 struct Example { _inner: () }
347
348 impl Example {
349 pub fn n$0ew() -> Self {
350 Self { _inner: () }
351 }
352 }
353
354 impl Default for Example {
355 fn default() -> Self {
356 Self::new()
357 }
358 }
359}
360"#,
361 );
362 }
363
364 fn check_pass(before: &str, after: &str) {
365 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
366 check_assist(generate_default_from_new, before, after);
367 }
368
369 fn check_not_applicable(before: &str) {
370 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
371 check_assist_not_applicable(generate_default_from_new, before);
372 }
373}
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
index d9388a737..c13c6eebe 100644
--- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,7 +1,6 @@
1use ide_db::helpers::FamousDefs; 1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; 5use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -44,7 +43,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
44 }; 43 };
45 44
46 if existing_from_impl(&ctx.sema, &variant).is_some() { 45 if existing_from_impl(&ctx.sema, &variant).is_some() {
47 mark::hit!(test_add_from_impl_already_exists); 46 cov_mark::hit!(test_add_from_impl_already_exists);
48 return None; 47 return None;
49 } 48 }
50 49
@@ -103,8 +102,6 @@ fn existing_from_impl(
103 102
104#[cfg(test)] 103#[cfg(test)]
105mod tests { 104mod tests {
106 use test_utils::mark;
107
108 use crate::tests::{check_assist, check_assist_not_applicable}; 105 use crate::tests::{check_assist, check_assist_not_applicable};
109 106
110 use super::*; 107 use super::*;
@@ -172,7 +169,7 @@ impl From<u32> for A {
172 169
173 #[test] 170 #[test]
174 fn test_add_from_impl_already_exists() { 171 fn test_add_from_impl_already_exists() {
175 mark::check!(test_add_from_impl_already_exists); 172 cov_mark::check!(test_add_from_impl_already_exists);
176 check_not_applicable( 173 check_not_applicable(
177 r#" 174 r#"
178enum A { $0One(u32), } 175enum A { $0One(u32), }
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 959824981..6f95b1a07 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -1,6 +1,7 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use ide_db::{base_db::FileId, helpers::SnippetCap}; 2use ide_db::{base_db::FileId, helpers::SnippetCap};
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4use stdx::to_lower_snake_case;
4use syntax::{ 5use syntax::{
5 ast::{ 6 ast::{
6 self, 7 self,
@@ -82,17 +83,18 @@ struct FunctionTemplate {
82 leading_ws: String, 83 leading_ws: String,
83 fn_def: ast::Fn, 84 fn_def: ast::Fn,
84 ret_type: ast::RetType, 85 ret_type: ast::RetType,
86 should_render_snippet: bool,
85 trailing_ws: String, 87 trailing_ws: String,
86 file: FileId, 88 file: FileId,
87} 89}
88 90
89impl FunctionTemplate { 91impl FunctionTemplate {
90 fn to_string(&self, cap: Option<SnippetCap>) -> String { 92 fn to_string(&self, cap: Option<SnippetCap>) -> String {
91 let f = match cap { 93 let f = match (cap, self.should_render_snippet) {
92 Some(cap) => { 94 (Some(cap), true) => {
93 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) 95 render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax()))
94 } 96 }
95 None => self.fn_def.to_string(), 97 _ => self.fn_def.to_string(),
96 }; 98 };
97 format!("{}{}{}", self.leading_ws, f, self.trailing_ws) 99 format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
98 } 100 }
@@ -103,6 +105,8 @@ struct FunctionBuilder {
103 fn_name: ast::Name, 105 fn_name: ast::Name,
104 type_params: Option<ast::GenericParamList>, 106 type_params: Option<ast::GenericParamList>,
105 params: ast::ParamList, 107 params: ast::ParamList,
108 ret_type: ast::RetType,
109 should_render_snippet: bool,
106 file: FileId, 110 file: FileId,
107 needs_pub: bool, 111 needs_pub: bool,
108} 112}
@@ -131,7 +135,43 @@ impl FunctionBuilder {
131 let fn_name = fn_name(&path)?; 135 let fn_name = fn_name(&path)?;
132 let (type_params, params) = fn_args(ctx, target_module, &call)?; 136 let (type_params, params) = fn_args(ctx, target_module, &call)?;
133 137
134 Some(Self { target, fn_name, type_params, params, file, needs_pub }) 138 // should_render_snippet intends to express a rough level of confidence about
139 // the correctness of the return type.
140 //
141 // If we are able to infer some return type, and that return type is not unit, we
142 // don't want to render the snippet. The assumption here is in this situation the
143 // return type is just as likely to be correct as any other part of the generated
144 // function.
145 //
146 // In the case where the return type is inferred as unit it is likely that the
147 // user does in fact intend for this generated function to return some non unit
148 // type, but that the current state of their code doesn't allow that return type
149 // to be accurately inferred.
150 let (ret_ty, should_render_snippet) = {
151 match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) {
152 Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true),
153 Some(ty) => {
154 let rendered = ty.display_source_code(ctx.db(), target_module.into());
155 match rendered {
156 Ok(rendered) => (make::ty(&rendered), false),
157 Err(_) => (make::ty_unit(), true),
158 }
159 }
160 None => (make::ty_unit(), true),
161 }
162 };
163 let ret_type = make::ret_type(ret_ty);
164
165 Some(Self {
166 target,
167 fn_name,
168 type_params,
169 params,
170 ret_type,
171 should_render_snippet,
172 file,
173 needs_pub,
174 })
135 } 175 }
136 176
137 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
@@ -144,7 +184,7 @@ impl FunctionBuilder {
144 self.type_params, 184 self.type_params,
145 self.params, 185 self.params,
146 fn_body, 186 fn_body,
147 Some(make::ret_type(make::ty_unit())), 187 Some(self.ret_type),
148 ); 188 );
149 let leading_ws; 189 let leading_ws;
150 let trailing_ws; 190 let trailing_ws;
@@ -170,6 +210,7 @@ impl FunctionBuilder {
170 insert_offset, 210 insert_offset,
171 leading_ws, 211 leading_ws,
172 ret_type: fn_def.ret_type().unwrap(), 212 ret_type: fn_def.ret_type().unwrap(),
213 should_render_snippet: self.should_render_snippet,
173 fn_def, 214 fn_def,
174 trailing_ws, 215 trailing_ws,
175 file: self.file, 216 file: self.file,
@@ -257,14 +298,15 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) {
257fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { 298fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> {
258 match fn_arg { 299 match fn_arg {
259 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), 300 ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?),
260 _ => Some( 301 _ => {
261 fn_arg 302 let s = fn_arg
262 .syntax() 303 .syntax()
263 .descendants() 304 .descendants()
264 .filter(|d| ast::NameRef::can_cast(d.kind())) 305 .filter(|d| ast::NameRef::can_cast(d.kind()))
265 .last()? 306 .last()?
266 .to_string(), 307 .to_string();
267 ), 308 Some(to_lower_snake_case(&s))
309 }
268 } 310 }
269} 311}
270 312
@@ -448,6 +490,52 @@ mod baz {
448 } 490 }
449 491
450 #[test] 492 #[test]
493 fn add_function_with_upper_camel_case_arg() {
494 check_assist(
495 generate_function,
496 r"
497struct BazBaz;
498fn foo() {
499 bar$0(BazBaz);
500}
501",
502 r"
503struct BazBaz;
504fn foo() {
505 bar(BazBaz);
506}
507
508fn bar(baz_baz: BazBaz) ${0:-> ()} {
509 todo!()
510}
511",
512 );
513 }
514
515 #[test]
516 fn add_function_with_upper_camel_case_arg_as_cast() {
517 check_assist(
518 generate_function,
519 r"
520struct BazBaz;
521fn foo() {
522 bar$0(&BazBaz as *const BazBaz);
523}
524",
525 r"
526struct BazBaz;
527fn foo() {
528 bar(&BazBaz as *const BazBaz);
529}
530
531fn bar(baz_baz: *const BazBaz) ${0:-> ()} {
532 todo!()
533}
534",
535 );
536 }
537
538 #[test]
451 fn add_function_with_function_call_arg() { 539 fn add_function_with_function_call_arg() {
452 check_assist( 540 check_assist(
453 generate_function, 541 generate_function,
@@ -498,7 +586,7 @@ impl Baz {
498 } 586 }
499} 587}
500 588
501fn bar(baz: Baz) ${0:-> ()} { 589fn bar(baz: Baz) -> Baz {
502 todo!() 590 todo!()
503} 591}
504", 592",
@@ -1012,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} {
1012 } 1100 }
1013 1101
1014 #[test] 1102 #[test]
1103 fn add_function_with_return_type() {
1104 check_assist(
1105 generate_function,
1106 r"
1107fn main() {
1108 let x: u32 = foo$0();
1109}
1110",
1111 r"
1112fn main() {
1113 let x: u32 = foo();
1114}
1115
1116fn foo() -> u32 {
1117 todo!()
1118}
1119",
1120 )
1121 }
1122
1123 #[test]
1015 fn add_function_not_applicable_if_function_already_exists() { 1124 fn add_function_not_applicable_if_function_already_exists() {
1016 check_assist_not_applicable( 1125 check_assist_not_applicable(
1017 generate_function, 1126 generate_function,
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs
index 5279af1f3..66113751c 100644
--- a/crates/ide_assists/src/handlers/infer_function_return_type.rs
+++ b/crates/ide_assists/src/handlers/infer_function_return_type.rs
@@ -1,6 +1,5 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use syntax::{ast, AstNode, TextRange, TextSize}; 2use syntax::{ast, AstNode, TextRange, TextSize};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -42,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext)
42 } 41 }
43 } 42 }
44 if let FnType::Closure { wrap_expr: true } = fn_type { 43 if let FnType::Closure { wrap_expr: true } = fn_type {
45 mark::hit!(wrap_closure_non_block_expr); 44 cov_mark::hit!(wrap_closure_non_block_expr);
46 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block 45 // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block
47 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); 46 builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr));
48 } 47 }
@@ -61,13 +60,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio
61 match ret_ty { 60 match ret_ty {
62 Some(ret_ty) => match ret_ty.ty() { 61 Some(ret_ty) => match ret_ty.ty() {
63 Some(ast::Type::InferType(_)) | None => { 62 Some(ast::Type::InferType(_)) | None => {
64 mark::hit!(existing_infer_ret_type); 63 cov_mark::hit!(existing_infer_ret_type);
65 mark::hit!(existing_infer_ret_type_closure); 64 cov_mark::hit!(existing_infer_ret_type_closure);
66 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) 65 Some(InsertOrReplace::Replace(ret_ty.syntax().text_range()))
67 } 66 }
68 _ => { 67 _ => {
69 mark::hit!(existing_ret_type); 68 cov_mark::hit!(existing_ret_type);
70 mark::hit!(existing_ret_type_closure); 69 cov_mark::hit!(existing_ret_type_closure);
71 None 70 None
72 } 71 }
73 }, 72 },
@@ -109,11 +108,11 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
109 }; 108 };
110 let frange = ctx.frange.range; 109 let frange = ctx.frange.range;
111 if return_type_range.contains_range(frange) { 110 if return_type_range.contains_range(frange) {
112 mark::hit!(cursor_in_ret_position); 111 cov_mark::hit!(cursor_in_ret_position);
113 mark::hit!(cursor_in_ret_position_closure); 112 cov_mark::hit!(cursor_in_ret_position_closure);
114 } else if tail_expr.syntax().text_range().contains_range(frange) { 113 } else if tail_expr.syntax().text_range().contains_range(frange) {
115 mark::hit!(cursor_on_tail); 114 cov_mark::hit!(cursor_on_tail);
116 mark::hit!(cursor_on_tail_closure); 115 cov_mark::hit!(cursor_on_tail_closure);
117 } else { 116 } else {
118 return None; 117 return None;
119 } 118 }
@@ -128,7 +127,7 @@ mod tests {
128 127
129 #[test] 128 #[test]
130 fn infer_return_type_specified_inferred() { 129 fn infer_return_type_specified_inferred() {
131 mark::check!(existing_infer_ret_type); 130 cov_mark::check!(existing_infer_ret_type);
132 check_assist( 131 check_assist(
133 infer_function_return_type, 132 infer_function_return_type,
134 r#"fn foo() -> $0_ { 133 r#"fn foo() -> $0_ {
@@ -142,7 +141,7 @@ mod tests {
142 141
143 #[test] 142 #[test]
144 fn infer_return_type_specified_inferred_closure() { 143 fn infer_return_type_specified_inferred_closure() {
145 mark::check!(existing_infer_ret_type_closure); 144 cov_mark::check!(existing_infer_ret_type_closure);
146 check_assist( 145 check_assist(
147 infer_function_return_type, 146 infer_function_return_type,
148 r#"fn foo() { 147 r#"fn foo() {
@@ -156,7 +155,7 @@ mod tests {
156 155
157 #[test] 156 #[test]
158 fn infer_return_type_cursor_at_return_type_pos() { 157 fn infer_return_type_cursor_at_return_type_pos() {
159 mark::check!(cursor_in_ret_position); 158 cov_mark::check!(cursor_in_ret_position);
160 check_assist( 159 check_assist(
161 infer_function_return_type, 160 infer_function_return_type,
162 r#"fn foo() $0{ 161 r#"fn foo() $0{
@@ -170,7 +169,7 @@ mod tests {
170 169
171 #[test] 170 #[test]
172 fn infer_return_type_cursor_at_return_type_pos_closure() { 171 fn infer_return_type_cursor_at_return_type_pos_closure() {
173 mark::check!(cursor_in_ret_position_closure); 172 cov_mark::check!(cursor_in_ret_position_closure);
174 check_assist( 173 check_assist(
175 infer_function_return_type, 174 infer_function_return_type,
176 r#"fn foo() { 175 r#"fn foo() {
@@ -184,7 +183,7 @@ mod tests {
184 183
185 #[test] 184 #[test]
186 fn infer_return_type() { 185 fn infer_return_type() {
187 mark::check!(cursor_on_tail); 186 cov_mark::check!(cursor_on_tail);
188 check_assist( 187 check_assist(
189 infer_function_return_type, 188 infer_function_return_type,
190 r#"fn foo() { 189 r#"fn foo() {
@@ -219,7 +218,7 @@ mod tests {
219 218
220 #[test] 219 #[test]
221 fn not_applicable_ret_type_specified() { 220 fn not_applicable_ret_type_specified() {
222 mark::check!(existing_ret_type); 221 cov_mark::check!(existing_ret_type);
223 check_assist_not_applicable( 222 check_assist_not_applicable(
224 infer_function_return_type, 223 infer_function_return_type,
225 r#"fn foo() -> i32 { 224 r#"fn foo() -> i32 {
@@ -251,7 +250,7 @@ mod tests {
251 250
252 #[test] 251 #[test]
253 fn infer_return_type_closure_block() { 252 fn infer_return_type_closure_block() {
254 mark::check!(cursor_on_tail_closure); 253 cov_mark::check!(cursor_on_tail_closure);
255 check_assist( 254 check_assist(
256 infer_function_return_type, 255 infer_function_return_type,
257 r#"fn foo() { 256 r#"fn foo() {
@@ -282,7 +281,7 @@ mod tests {
282 281
283 #[test] 282 #[test]
284 fn infer_return_type_closure_wrap() { 283 fn infer_return_type_closure_wrap() {
285 mark::check!(wrap_closure_non_block_expr); 284 cov_mark::check!(wrap_closure_non_block_expr);
286 check_assist( 285 check_assist(
287 infer_function_return_type, 286 infer_function_return_type,
288 r#"fn foo() { 287 r#"fn foo() {
@@ -321,7 +320,7 @@ mod tests {
321 320
322 #[test] 321 #[test]
323 fn not_applicable_ret_type_specified_closure() { 322 fn not_applicable_ret_type_specified_closure() {
324 mark::check!(existing_ret_type_closure); 323 cov_mark::check!(existing_ret_type_closure);
325 check_assist_not_applicable( 324 check_assist_not_applicable(
326 infer_function_return_type, 325 infer_function_return_type,
327 r#"fn foo() { 326 r#"fn foo() {
diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs
index 6ec99b09b..8e56029cb 100644
--- a/crates/ide_assists/src/handlers/inline_function.rs
+++ b/crates/ide_assists/src/handlers/inline_function.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner}, 4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode, 5 AstNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -49,7 +48,7 @@ pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<
49 if arguments.len() != parameters.len() { 48 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of 49 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function 50 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments); 51 cov_mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None; 52 return None;
54 } 53 }
55 54
@@ -155,7 +154,7 @@ fn main() { Foo.bar$0(); }
155 154
156 #[test] 155 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() { 156 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments); 157 cov_mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable( 158 check_assist_not_applicable(
160 inline_function, 159 inline_function,
161 r#" 160 r#"
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index da5522670..ea1466dc8 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, AstNode, AstToken}, 4 ast::{self, AstNode, AstToken},
5 TextRange, 5 TextRange,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{ 8use crate::{
10 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
@@ -34,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
34 _ => return None, 33 _ => return None,
35 }; 34 };
36 if bind_pat.mut_token().is_some() { 35 if bind_pat.mut_token().is_some() {
37 mark::hit!(test_not_inline_mut_variable); 36 cov_mark::hit!(test_not_inline_mut_variable);
38 return None; 37 return None;
39 } 38 }
40 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { 39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
41 mark::hit!(not_applicable_outside_of_bind_pat); 40 cov_mark::hit!(not_applicable_outside_of_bind_pat);
42 return None; 41 return None;
43 } 42 }
44 let initializer_expr = let_stmt.initializer()?; 43 let initializer_expr = let_stmt.initializer()?;
@@ -47,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
47 let def = Definition::Local(def); 46 let def = Definition::Local(def);
48 let usages = def.usages(&ctx.sema).all(); 47 let usages = def.usages(&ctx.sema).all();
49 if usages.is_empty() { 48 if usages.is_empty() {
50 mark::hit!(test_not_applicable_if_variable_unused); 49 cov_mark::hit!(test_not_applicable_if_variable_unused);
51 return None; 50 return None;
52 }; 51 };
53 52
@@ -130,7 +129,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
130 Some(name_ref) 129 Some(name_ref)
131 if ast::RecordExprField::for_field_name(name_ref).is_some() => 130 if ast::RecordExprField::for_field_name(name_ref).is_some() =>
132 { 131 {
133 mark::hit!(inline_field_shorthand); 132 cov_mark::hit!(inline_field_shorthand);
134 builder.insert(reference.range.end(), format!(": {}", replacement)); 133 builder.insert(reference.range.end(), format!(": {}", replacement));
135 } 134 }
136 _ => builder.replace(reference.range, replacement), 135 _ => builder.replace(reference.range, replacement),
@@ -143,8 +142,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
143 142
144#[cfg(test)] 143#[cfg(test)]
145mod tests { 144mod tests {
146 use test_utils::mark;
147
148 use crate::tests::{check_assist, check_assist_not_applicable}; 145 use crate::tests::{check_assist, check_assist_not_applicable};
149 146
150 use super::*; 147 use super::*;
@@ -351,7 +348,7 @@ fn foo() {
351 348
352 #[test] 349 #[test]
353 fn test_not_inline_mut_variable() { 350 fn test_not_inline_mut_variable() {
354 mark::check!(test_not_inline_mut_variable); 351 cov_mark::check!(test_not_inline_mut_variable);
355 check_assist_not_applicable( 352 check_assist_not_applicable(
356 inline_local_variable, 353 inline_local_variable,
357 r" 354 r"
@@ -684,7 +681,7 @@ fn foo() {
684 681
685 #[test] 682 #[test]
686 fn inline_field_shorthand() { 683 fn inline_field_shorthand() {
687 mark::check!(inline_field_shorthand); 684 cov_mark::check!(inline_field_shorthand);
688 check_assist( 685 check_assist(
689 inline_local_variable, 686 inline_local_variable,
690 r" 687 r"
@@ -705,7 +702,7 @@ fn main() {
705 702
706 #[test] 703 #[test]
707 fn test_not_applicable_if_variable_unused() { 704 fn test_not_applicable_if_variable_unused() {
708 mark::check!(test_not_applicable_if_variable_unused); 705 cov_mark::check!(test_not_applicable_if_variable_unused);
709 check_assist_not_applicable( 706 check_assist_not_applicable(
710 inline_local_variable, 707 inline_local_variable,
711 r" 708 r"
@@ -718,7 +715,7 @@ fn foo() {
718 715
719 #[test] 716 #[test]
720 fn not_applicable_outside_of_bind_pat() { 717 fn not_applicable_outside_of_bind_pat() {
721 mark::check!(not_applicable_outside_of_bind_pat); 718 cov_mark::check!(not_applicable_outside_of_bind_pat);
722 check_assist_not_applicable( 719 check_assist_not_applicable(
723 inline_local_variable, 720 inline_local_variable,
724 r" 721 r"
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs
index 91c395c1b..6e685b4b2 100644
--- a/crates/ide_assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide_assists/src/handlers/move_module_to_file.rs
@@ -5,7 +5,6 @@ use syntax::{
5 ast::{self, edit::AstNodeEdit, NameOwner}, 5 ast::{self, edit::AstNodeEdit, NameOwner},
6 AstNode, TextRange, 6 AstNode, TextRange,
7}; 7};
8use test_utils::mark;
9 8
10use crate::{AssistContext, AssistId, AssistKind, Assists}; 9use crate::{AssistContext, AssistId, AssistKind, Assists};
11 10
@@ -28,7 +27,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt
28 27
29 let l_curly_offset = module_items.syntax().text_range().start(); 28 let l_curly_offset = module_items.syntax().text_range().start();
30 if l_curly_offset <= ctx.offset() { 29 if l_curly_offset <= ctx.offset() {
31 mark::hit!(available_before_curly); 30 cov_mark::hit!(available_before_curly);
32 return None; 31 return None;
33 } 32 }
34 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); 33 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset);
@@ -182,7 +181,7 @@ pub(crate) mod tests;
182 181
183 #[test] 182 #[test]
184 fn available_before_curly() { 183 fn available_before_curly() {
185 mark::check!(available_before_curly); 184 cov_mark::check!(available_before_curly);
186 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); 185 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
187 } 186 }
188} 187}
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 13e1cb754..04bae4e58 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -2,7 +2,6 @@ use syntax::{
2 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, edit::AstNodeEdit, make},
3 AstNode, 3 AstNode,
4}; 4};
5use test_utils::mark;
6 5
7use crate::{ 6use crate::{
8 assist_context::{AssistContext, Assists}, 7 assist_context::{AssistContext, Assists},
@@ -104,7 +103,7 @@ fn exprify_if(
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?) 103 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 } 104 }
106 ast::ElseBranch::IfExpr(expr) => { 105 ast::ElseBranch::IfExpr(expr) => {
107 mark::hit!(test_pull_assignment_up_chained_if); 106 cov_mark::hit!(test_pull_assignment_up_chained_if);
108 ast::ElseBranch::IfExpr(ast::IfExpr::cast( 107 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
109 exprify_if(&expr, sema, name)?.syntax().to_owned(), 108 exprify_if(&expr, sema, name)?.syntax().to_owned(),
110 )?) 109 )?)
@@ -144,7 +143,7 @@ fn is_equivalent(
144) -> bool { 143) -> bool {
145 match (expr0, expr1) { 144 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { 145 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment); 146 cov_mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) 147 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 } 148 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { 149 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
@@ -156,6 +155,17 @@ fn is_equivalent(
156 false 155 false
157 } 156 }
158 } 157 }
158 (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
159 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
160 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
161 {
162 cov_mark::hit!(test_pull_assignment_up_deref);
163 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
164 is_equivalent(sema, &prefix0, &prefix1)
165 } else {
166 false
167 }
168 }
159 _ => false, 169 _ => false,
160 } 170 }
161} 171}
@@ -252,7 +262,7 @@ fn foo() {
252 262
253 #[test] 263 #[test]
254 fn test_pull_assignment_up_chained_if() { 264 fn test_pull_assignment_up_chained_if() {
255 mark::check!(test_pull_assignment_up_chained_if); 265 cov_mark::check!(test_pull_assignment_up_chained_if);
256 check_assist( 266 check_assist(
257 pull_assignment_up, 267 pull_assignment_up,
258 r#" 268 r#"
@@ -368,7 +378,7 @@ fn foo() {
368 378
369 #[test] 379 #[test]
370 fn test_pull_assignment_up_field_assignment() { 380 fn test_pull_assignment_up_field_assignment() {
371 mark::check!(test_pull_assignment_up_field_assignment); 381 cov_mark::check!(test_pull_assignment_up_field_assignment);
372 check_assist( 382 check_assist(
373 pull_assignment_up, 383 pull_assignment_up,
374 r#" 384 r#"
@@ -397,4 +407,36 @@ fn foo() {
397}"#, 407}"#,
398 ) 408 )
399 } 409 }
410
411 #[test]
412 fn test_pull_assignment_up_deref() {
413 cov_mark::check!(test_pull_assignment_up_deref);
414 check_assist(
415 pull_assignment_up,
416 r#"
417fn foo() {
418 let mut a = 1;
419 let b = &mut a;
420
421 if true {
422 $0*b = 2;
423 } else {
424 *b = 3;
425 }
426}
427"#,
428 r#"
429fn foo() {
430 let mut a = 1;
431 let b = &mut a;
432
433 *b = if true {
434 2
435 } else {
436 3
437 };
438}
439"#,
440 )
441 }
400} 442}
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index b0b0d31b4..272874ae3 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -1,14 +1,16 @@
1use std::iter; 1use std::iter;
2 2
3use hir::{AsAssocItem, AsName}; 3use hir::AsAssocItem;
4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; 4use ide_db::helpers::{
5 import_assets::{ImportCandidate, LocatedImport},
6 item_name, mod_path_to_ast,
7};
5use ide_db::RootDatabase; 8use ide_db::RootDatabase;
6use syntax::{ 9use syntax::{
7 ast, 10 ast,
8 ast::{make, ArgListOwner}, 11 ast::{make, ArgListOwner},
9 AstNode, 12 AstNode,
10}; 13};
11use test_utils::mark;
12 14
13use crate::{ 15use crate::{
14 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
@@ -47,42 +49,42 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 let qualify_candidate = match candidate { 49 let qualify_candidate = match candidate {
48 ImportCandidate::Path(candidate) => { 50 ImportCandidate::Path(candidate) => {
49 if candidate.qualifier.is_some() { 51 if candidate.qualifier.is_some() {
50 mark::hit!(qualify_path_qualifier_start); 52 cov_mark::hit!(qualify_path_qualifier_start);
51 let path = ast::Path::cast(syntax_under_caret)?; 53 let path = ast::Path::cast(syntax_under_caret)?;
52 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); 54 let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
53 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) 55 QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
54 } else { 56 } else {
55 mark::hit!(qualify_path_unqualified_name); 57 cov_mark::hit!(qualify_path_unqualified_name);
56 let path = ast::Path::cast(syntax_under_caret)?; 58 let path = ast::Path::cast(syntax_under_caret)?;
57 let generics = path.segment()?.generic_arg_list(); 59 let generics = path.segment()?.generic_arg_list();
58 QualifyCandidate::UnqualifiedName(generics) 60 QualifyCandidate::UnqualifiedName(generics)
59 } 61 }
60 } 62 }
61 ImportCandidate::TraitAssocItem(_) => { 63 ImportCandidate::TraitAssocItem(_) => {
62 mark::hit!(qualify_path_trait_assoc_item); 64 cov_mark::hit!(qualify_path_trait_assoc_item);
63 let path = ast::Path::cast(syntax_under_caret)?; 65 let path = ast::Path::cast(syntax_under_caret)?;
64 let (qualifier, segment) = (path.qualifier()?, path.segment()?); 66 let (qualifier, segment) = (path.qualifier()?, path.segment()?);
65 QualifyCandidate::TraitAssocItem(qualifier, segment) 67 QualifyCandidate::TraitAssocItem(qualifier, segment)
66 } 68 }
67 ImportCandidate::TraitMethod(_) => { 69 ImportCandidate::TraitMethod(_) => {
68 mark::hit!(qualify_path_trait_method); 70 cov_mark::hit!(qualify_path_trait_method);
69 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; 71 let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?;
70 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) 72 QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr)
71 } 73 }
72 }; 74 };
73 75
74 let group_label = group_label(candidate); 76 let group_label = group_label(candidate);
75 for (import, item) in proposed_imports { 77 for import in proposed_imports {
76 acc.add_group( 78 acc.add_group(
77 &group_label, 79 &group_label,
78 AssistId("qualify_path", AssistKind::QuickFix), 80 AssistId("qualify_path", AssistKind::QuickFix),
79 label(candidate, &import), 81 label(ctx.db(), candidate, &import),
80 range, 82 range,
81 |builder| { 83 |builder| {
82 qualify_candidate.qualify( 84 qualify_candidate.qualify(
83 |replace_with: String| builder.replace(range, replace_with), 85 |replace_with: String| builder.replace(range, replace_with),
84 import, 86 &import.import_path,
85 item, 87 import.item_to_import,
86 ) 88 )
87 }, 89 },
88 ); 90 );
@@ -98,8 +100,13 @@ enum QualifyCandidate<'db> {
98} 100}
99 101
100impl QualifyCandidate<'_> { 102impl QualifyCandidate<'_> {
101 fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { 103 fn qualify(
102 let import = mod_path_to_ast(&import); 104 &self,
105 mut replacer: impl FnMut(String),
106 import: &hir::ModPath,
107 item: hir::ItemInNs,
108 ) {
109 let import = mod_path_to_ast(import);
103 match self { 110 match self {
104 QualifyCandidate::QualifierStart(segment, generics) => { 111 QualifyCandidate::QualifierStart(segment, generics) => {
105 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); 112 let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
@@ -160,7 +167,9 @@ fn find_trait_method(
160) -> Option<hir::Function> { 167) -> Option<hir::Function> {
161 if let Some(hir::AssocItem::Function(method)) = 168 if let Some(hir::AssocItem::Function(method)) =
162 trait_.items(db).into_iter().find(|item: &hir::AssocItem| { 169 trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
163 item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) 170 item.name(db)
171 .map(|name| name.to_string() == trait_method_name.to_string())
172 .unwrap_or(false)
164 }) 173 })
165 { 174 {
166 Some(method) 175 Some(method)
@@ -182,23 +191,29 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> {
182fn group_label(candidate: &ImportCandidate) -> GroupLabel { 191fn group_label(candidate: &ImportCandidate) -> GroupLabel {
183 let name = match candidate { 192 let name = match candidate {
184 ImportCandidate::Path(it) => &it.name, 193 ImportCandidate::Path(it) => &it.name,
185 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, 194 ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => {
195 &it.assoc_item_name
196 }
186 } 197 }
187 .text(); 198 .text();
188 GroupLabel(format!("Qualify {}", name)) 199 GroupLabel(format!("Qualify {}", name))
189} 200}
190 201
191fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { 202fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
203 let display_path = match item_name(db, import.original_item) {
204 Some(display_path) => display_path.to_string(),
205 None => "{unknown}".to_string(),
206 };
192 match candidate { 207 match candidate {
193 ImportCandidate::Path(candidate) => { 208 ImportCandidate::Path(candidate) => {
194 if candidate.qualifier.is_some() { 209 if candidate.qualifier.is_some() {
195 format!("Qualify with `{}`", &import) 210 format!("Qualify with `{}`", display_path)
196 } else { 211 } else {
197 format!("Qualify as `{}`", &import) 212 format!("Qualify as `{}`", display_path)
198 } 213 }
199 } 214 }
200 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), 215 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path),
201 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), 216 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path),
202 } 217 }
203} 218}
204 219
@@ -210,7 +225,7 @@ mod tests {
210 225
211 #[test] 226 #[test]
212 fn applicable_when_found_an_import_partial() { 227 fn applicable_when_found_an_import_partial() {
213 mark::check!(qualify_path_unqualified_name); 228 cov_mark::check!(qualify_path_unqualified_name);
214 check_assist( 229 check_assist(
215 qualify_path, 230 qualify_path,
216 r" 231 r"
@@ -502,7 +517,7 @@ fn main() {
502 517
503 #[test] 518 #[test]
504 fn associated_struct_const() { 519 fn associated_struct_const() {
505 mark::check!(qualify_path_qualifier_start); 520 cov_mark::check!(qualify_path_qualifier_start);
506 check_assist( 521 check_assist(
507 qualify_path, 522 qualify_path,
508 r" 523 r"
@@ -603,7 +618,7 @@ fn main() {
603 618
604 #[test] 619 #[test]
605 fn associated_trait_const() { 620 fn associated_trait_const() {
606 mark::check!(qualify_path_trait_assoc_item); 621 cov_mark::check!(qualify_path_trait_assoc_item);
607 check_assist( 622 check_assist(
608 qualify_path, 623 qualify_path,
609 r" 624 r"
@@ -673,7 +688,7 @@ fn main() {
673 688
674 #[test] 689 #[test]
675 fn trait_method() { 690 fn trait_method() {
676 mark::check!(qualify_path_trait_method); 691 cov_mark::check!(qualify_path_trait_method);
677 check_assist( 692 check_assist(
678 qualify_path, 693 qualify_path,
679 r" 694 r"
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs
index d95267607..d0f1613f3 100644
--- a/crates/ide_assists/src/handlers/raw_string.rs
+++ b/crates/ide_assists/src/handlers/raw_string.rs
@@ -1,7 +1,6 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use syntax::{ast, AstToken, TextRange, TextSize}; 3use syntax::{ast, AstToken, TextRange, TextSize};
4use test_utils::mark;
5 4
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
7 6
@@ -149,7 +148,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
149 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; 148 let internal_text = &text[token.text_range_between_quotes()? - text_range.start()];
150 149
151 if existing_hashes == required_hashes(internal_text) { 150 if existing_hashes == required_hashes(internal_text) {
152 mark::hit!(cant_remove_required_hash); 151 cov_mark::hit!(cant_remove_required_hash);
153 return None; 152 return None;
154 } 153 }
155 154
@@ -182,8 +181,6 @@ fn test_required_hashes() {
182 181
183#[cfg(test)] 182#[cfg(test)]
184mod tests { 183mod tests {
185 use test_utils::mark;
186
187 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 184 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
188 185
189 use super::*; 186 use super::*;
@@ -396,7 +393,7 @@ string"###;
396 393
397 #[test] 394 #[test]
398 fn cant_remove_required_hash() { 395 fn cant_remove_required_hash() {
399 mark::check!(cant_remove_required_hash); 396 cov_mark::check!(cant_remove_required_hash);
400 check_assist_not_applicable( 397 check_assist_not_applicable(
401 remove_hash, 398 remove_hash,
402 r##" 399 r##"
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs
index c961680e2..2699d2861 100644
--- a/crates/ide_assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide_assists/src/handlers/remove_unused_param.rs
@@ -4,7 +4,7 @@ use syntax::{
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
9 9
10use crate::{ 10use crate::{
@@ -49,7 +49,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
49 Definition::Local(local) 49 Definition::Local(local)
50 }; 50 };
51 if param_def.usages(&ctx.sema).at_least_one() { 51 if param_def.usages(&ctx.sema).at_least_one() {
52 mark::hit!(keep_used); 52 cov_mark::hit!(keep_used);
53 return None; 53 return None;
54 } 54 }
55 acc.add( 55 acc.add(
@@ -243,7 +243,7 @@ fn b2() { foo(9) }
243 243
244 #[test] 244 #[test]
245 fn keep_used() { 245 fn keep_used() {
246 mark::check!(keep_used); 246 cov_mark::check!(keep_used);
247 check_assist_not_applicable( 247 check_assist_not_applicable(
248 remove_unused_param, 248 remove_unused_param,
249 r#" 249 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index fba7d6ddb..794c89323 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -4,7 +4,6 @@ use rustc_hash::FxHashMap;
4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; 6use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 }); 38 });
40 39
41 if sorted_fields == fields { 40 if sorted_fields == fields {
42 mark::hit!(reorder_sorted_fields); 41 cov_mark::hit!(reorder_sorted_fields);
43 return None; 42 return None;
44 } 43 }
45 44
@@ -109,15 +108,13 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
109 108
110#[cfg(test)] 109#[cfg(test)]
111mod tests { 110mod tests {
112 use test_utils::mark;
113
114 use crate::tests::{check_assist, check_assist_not_applicable}; 111 use crate::tests::{check_assist, check_assist_not_applicable};
115 112
116 use super::*; 113 use super::*;
117 114
118 #[test] 115 #[test]
119 fn reorder_sorted_fields() { 116 fn reorder_sorted_fields() {
120 mark::check!(reorder_sorted_fields); 117 cov_mark::check!(reorder_sorted_fields);
121 check_assist_not_applicable( 118 check_assist_not_applicable(
122 reorder_fields, 119 reorder_fields,
123 r#" 120 r#"
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 309f291c8..edf4b0bfe 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -8,7 +8,6 @@ use syntax::{
8 ast::{self, NameOwner}, 8 ast::{self, NameOwner},
9 AstNode, 9 AstNode,
10}; 10};
11use test_utils::mark;
12 11
13use crate::{AssistContext, AssistId, AssistKind, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
14 13
@@ -71,7 +70,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
71 70
72 // Don't edit already sorted methods: 71 // Don't edit already sorted methods:
73 if methods == sorted { 72 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted); 73 cov_mark::hit!(not_applicable_if_sorted);
75 return None; 74 return None;
76 } 75 }
77 76
@@ -121,15 +120,13 @@ fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
121 120
122#[cfg(test)] 121#[cfg(test)]
123mod tests { 122mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable}; 123 use crate::tests::{check_assist, check_assist_not_applicable};
127 124
128 use super::*; 125 use super::*;
129 126
130 #[test] 127 #[test]
131 fn not_applicable_if_sorted() { 128 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted); 129 cov_mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable( 130 check_assist_not_applicable(
134 reorder_impl, 131 reorder_impl,
135 r#" 132 r#"
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 c69bc5cac..88fe2fe90 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
@@ -1,5 +1,6 @@
1use hir::ModuleDef;
1use ide_db::helpers::mod_path_to_ast; 2use ide_db::helpers::mod_path_to_ast;
2use ide_db::imports_locator; 3use ide_db::items_locator;
3use itertools::Itertools; 4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 ast::{self, make, AstNode, NameOwner}, 6 ast::{self, make, AstNode, NameOwner},
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl(
64 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
65 let current_crate = current_module.krate(); 66 let current_crate = current_module.krate();
66 67
67 let found_traits = imports_locator::find_exact_imports( 68 let found_traits =
68 &ctx.sema, 69 items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string())
69 current_crate, 70 .into_iter()
70 trait_token.text().to_string(), 71 .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) {
71 ) 72 ModuleDef::Trait(trait_) => Some(trait_),
72 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 73 _ => None,
73 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 74 })
74 _ => None, 75 .flat_map(|trait_| {
75 }) 76 current_module
76 .flat_map(|trait_| { 77 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
77 current_module 78 .as_ref()
78 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 79 .map(mod_path_to_ast)
79 .as_ref() 80 .zip(Some(trait_))
80 .map(mod_path_to_ast) 81 });
81 .zip(Some(trait_))
82 });
83 82
84 let mut no_traits_found = true; 83 let mut no_traits_found = true;
85 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 84 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
index 27da28bc0..50b05ab0b 100644
--- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
+++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
@@ -3,7 +3,6 @@ use hir::known;
3use ide_db::helpers::FamousDefs; 3use ide_db::helpers::FamousDefs;
4use stdx::format_to; 4use stdx::format_to;
5use syntax::{ast, AstNode}; 5use syntax::{ast, AstNode};
6use test_utils::mark;
7 6
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
9 8
@@ -34,7 +33,7 @@ pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistCont
34 let pat = for_loop.pat()?; 33 let pat = for_loop.pat()?;
35 let body = for_loop.loop_body()?; 34 let body = for_loop.loop_body()?;
36 if body.syntax().text_range().start() < ctx.offset() { 35 if body.syntax().text_range().start() < ctx.offset() {
37 mark::hit!(not_available_in_body); 36 cov_mark::hit!(not_available_in_body);
38 return None; 37 return None;
39 } 38 }
40 39
@@ -187,7 +186,7 @@ fn main() {
187 186
188 #[test] 187 #[test]
189 fn not_available_in_body() { 188 fn not_available_in_body() {
190 mark::check!(not_available_in_body); 189 cov_mark::check!(not_available_in_body);
191 check_assist_not_applicable( 190 check_assist_not_applicable(
192 replace_for_loop_with_for_each, 191 replace_for_loop_with_for_each,
193 r" 192 r"
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
index f3bc6cf39..36d2e0331 100644
--- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,6 +1,5 @@
1use ide_db::helpers::insert_use::{insert_use, ImportScope}; 1use ide_db::helpers::insert_use::{insert_use, ImportScope};
2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; 2use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode};
3use test_utils::mark;
4 3
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
6 5
@@ -27,7 +26,7 @@ pub(crate) fn replace_qualified_name_with_use(
27 return None; 26 return None;
28 } 27 }
29 if path.qualifier().is_none() { 28 if path.qualifier().is_none() {
30 mark::hit!(dont_import_trivial_paths); 29 cov_mark::hit!(dont_import_trivial_paths);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -44,7 +43,7 @@ pub(crate) fn replace_qualified_name_with_use(
44 let mut rewriter = SyntaxRewriter::default(); 43 let mut rewriter = SyntaxRewriter::default();
45 shorten_paths(&mut rewriter, syntax.clone(), &path); 44 shorten_paths(&mut rewriter, syntax.clone(), &path);
46 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { 45 if let Some(ref import_scope) = ImportScope::from(syntax.clone()) {
47 rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge); 46 rewriter += insert_use(import_scope, path, ctx.config.insert_use);
48 builder.rewrite(rewriter); 47 builder.rewrite(rewriter);
49 } 48 }
50 }, 49 },
@@ -458,7 +457,7 @@ impl Debug for Foo {
458 457
459 #[test] 458 #[test]
460 fn dont_import_trivial_paths() { 459 fn dont_import_trivial_paths() {
461 mark::check!(dont_import_trivial_paths); 460 cov_mark::check!(dont_import_trivial_paths);
462 check_assist_not_applicable( 461 check_assist_not_applicable(
463 replace_qualified_name_with_use, 462 replace_qualified_name_with_use,
464 r" 463 r"
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs
index 317318c24..634b9c0b7 100644
--- a/crates/ide_assists/src/handlers/replace_string_with_char.rs
+++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs
@@ -25,13 +25,16 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext) -
25 if value.chars().take(2).count() != 1 { 25 if value.chars().take(2).count() != 1 {
26 return None; 26 return None;
27 } 27 }
28 let quote_offets = token.quote_offsets()?;
28 29
29 acc.add( 30 acc.add(
30 AssistId("replace_string_with_char", AssistKind::RefactorRewrite), 31 AssistId("replace_string_with_char", AssistKind::RefactorRewrite),
31 "Replace string with char", 32 "Replace string with char",
32 target, 33 target,
33 |edit| { 34 |edit| {
34 edit.replace(token.syntax().text_range(), format!("'{}'", value)); 35 let (left, right) = quote_offets.quotes;
36 edit.replace(left, String::from('\''));
37 edit.replace(right, String::from('\''));
35 }, 38 },
36 ) 39 )
37} 40}
@@ -47,10 +50,10 @@ mod tests {
47 check_assist_target( 50 check_assist_target(
48 replace_string_with_char, 51 replace_string_with_char,
49 r#" 52 r#"
50 fn f() { 53fn f() {
51 let s = "$0c"; 54 let s = "$0c";
52 } 55}
53 "#, 56"#,
54 r#""c""#, 57 r#""c""#,
55 ); 58 );
56 } 59 }
@@ -60,15 +63,15 @@ mod tests {
60 check_assist( 63 check_assist(
61 replace_string_with_char, 64 replace_string_with_char,
62 r#" 65 r#"
63 fn f() { 66fn f() {
64 let s = "$0c"; 67 let s = "$0c";
65 } 68}
66 "#, 69"#,
67 r##" 70 r##"
68 fn f() { 71fn f() {
69 let s = 'c'; 72 let s = 'c';
70 } 73}
71 "##, 74"##,
72 ) 75 )
73 } 76 }
74 77
@@ -77,15 +80,15 @@ mod tests {
77 check_assist( 80 check_assist(
78 replace_string_with_char, 81 replace_string_with_char,
79 r#" 82 r#"
80 fn f() { 83fn f() {
81 let s = "$0😀"; 84 let s = "$0😀";
82 } 85}
83 "#, 86"#,
84 r##" 87 r##"
85 fn f() { 88fn f() {
86 let s = '😀'; 89 let s = '😀';
87 } 90}
88 "##, 91"##,
89 ) 92 )
90 } 93 }
91 94
@@ -94,10 +97,10 @@ mod tests {
94 check_assist_not_applicable( 97 check_assist_not_applicable(
95 replace_string_with_char, 98 replace_string_with_char,
96 r#" 99 r#"
97 fn f() { 100fn f() {
98 let s = "$0test"; 101 let s = "$0test";
99 } 102}
100 "#, 103"#,
101 ) 104 )
102 } 105 }
103 106
@@ -106,15 +109,15 @@ mod tests {
106 check_assist( 109 check_assist(
107 replace_string_with_char, 110 replace_string_with_char,
108 r#" 111 r#"
109 fn f() { 112fn f() {
110 format!($0"x", 92) 113 format!($0"x", 92)
111 } 114}
112 "#, 115"#,
113 r##" 116 r##"
114 fn f() { 117fn f() {
115 format!('x', 92) 118 format!('x', 92)
116 } 119}
117 "##, 120"##,
118 ) 121 )
119 } 122 }
120 123
@@ -123,15 +126,66 @@ mod tests {
123 check_assist( 126 check_assist(
124 replace_string_with_char, 127 replace_string_with_char,
125 r#" 128 r#"
126 fn f() { 129fn f() {
127 find($0"x"); 130 find($0"x");
128 } 131}
129 "#, 132"#,
130 r##" 133 r##"
131 fn f() { 134fn f() {
132 find('x'); 135 find('x');
133 } 136}
134 "##, 137"##,
138 )
139 }
140
141 #[test]
142 fn replace_string_with_char_newline() {
143 check_assist(
144 replace_string_with_char,
145 r#"
146fn f() {
147 find($0"\n");
148}
149"#,
150 r##"
151fn f() {
152 find('\n');
153}
154"##,
155 )
156 }
157
158 #[test]
159 fn replace_string_with_char_unicode_escape() {
160 check_assist(
161 replace_string_with_char,
162 r#"
163fn f() {
164 find($0"\u{7FFF}");
165}
166"#,
167 r##"
168fn f() {
169 find('\u{7FFF}');
170}
171"##,
172 )
173 }
174
175 #[test]
176 fn replace_raw_string_with_char() {
177 check_assist(
178 replace_string_with_char,
179 r##"
180fn f() {
181 $0r#"X"#
182}
183"##,
184 r##"
185fn f() {
186 'X'
187}
188"##,
135 ) 189 )
136 } 190 }
137} 191}
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs
index 3dbef8e51..616af7c2e 100644
--- a/crates/ide_assists/src/handlers/unmerge_use.rs
+++ b/crates/ide_assists/src/handlers/unmerge_use.rs
@@ -3,7 +3,6 @@ use syntax::{
3 ast::{self, edit::AstNodeEdit, VisibilityOwner}, 3 ast::{self, edit::AstNodeEdit, VisibilityOwner},
4 AstNode, SyntaxKind, 4 AstNode, SyntaxKind,
5}; 5};
6use test_utils::mark;
7 6
8use crate::{ 7use crate::{
9 assist_context::{AssistContext, Assists}, 8 assist_context::{AssistContext, Assists},
@@ -27,7 +26,7 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
27 26
28 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; 27 let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
29 if tree_list.use_trees().count() < 2 { 28 if tree_list.use_trees().count() < 2 {
30 mark::hit!(skip_single_use_item); 29 cov_mark::hit!(skip_single_use_item);
31 return None; 30 return None;
32 } 31 }
33 32
@@ -89,7 +88,7 @@ mod tests {
89 88
90 #[test] 89 #[test]
91 fn skip_single_use_item() { 90 fn skip_single_use_item() {
92 mark::check!(skip_single_use_item); 91 cov_mark::check!(skip_single_use_item);
93 check_assist_not_applicable( 92 check_assist_not_applicable(
94 unmerge_use, 93 unmerge_use,
95 r" 94 r"
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 fec16fc49..e838630ea 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
@@ -4,7 +4,6 @@ use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, 4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode, 5 match_ast, AstNode, SyntaxNode,
6}; 6};
7use test_utils::mark;
8 7
9use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
10 9
@@ -39,7 +38,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext)
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next(); 38 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type { 39 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") { 40 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); 41 cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None; 42 return None;
44 } 43 }
45 } 44 }
@@ -367,7 +366,7 @@ fn foo() -> std::result::Result<i32$0, String> {
367 366
368 #[test] 367 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() { 368 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result); 369 cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable( 370 check_assist_not_applicable(
372 wrap_return_type_in_result, 371 wrap_return_type_in_result,
373 r#" 372 r#"
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 9c8148462..ea62d5f5d 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -127,6 +127,7 @@ mod handlers {
127 mod flip_comma; 127 mod flip_comma;
128 mod flip_trait_bound; 128 mod flip_trait_bound;
129 mod generate_default_from_enum_variant; 129 mod generate_default_from_enum_variant;
130 mod generate_default_from_new;
130 mod generate_derive; 131 mod generate_derive;
131 mod generate_enum_is_method; 132 mod generate_enum_is_method;
132 mod generate_enum_projection_method; 133 mod generate_enum_projection_method;
@@ -189,6 +190,7 @@ mod handlers {
189 flip_comma::flip_comma, 190 flip_comma::flip_comma,
190 flip_trait_bound::flip_trait_bound, 191 flip_trait_bound::flip_trait_bound,
191 generate_default_from_enum_variant::generate_default_from_enum_variant, 192 generate_default_from_enum_variant::generate_default_from_enum_variant,
193 generate_default_from_new::generate_default_from_new,
192 generate_derive::generate_derive, 194 generate_derive::generate_derive,
193 generate_enum_is_method::generate_enum_is_method, 195 generate_enum_is_method::generate_enum_is_method,
194 generate_enum_projection_method::generate_enum_as_method, 196 generate_enum_projection_method::generate_enum_as_method,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f616760..a7a923beb 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -23,6 +23,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
23 insert_use: InsertUseConfig { 23 insert_use: InsertUseConfig {
24 merge: Some(MergeBehavior::Full), 24 merge: Some(MergeBehavior::Full),
25 prefix_kind: hir::PrefixKind::Plain, 25 prefix_kind: hir::PrefixKind::Plain,
26 group: true,
26 }, 27 },
27}; 28};
28 29
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4f007aa48..304b5798f 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -440,6 +440,37 @@ impl Default for Version {
440} 440}
441 441
442#[test] 442#[test]
443fn doctest_generate_default_from_new() {
444 check_doc_test(
445 "generate_default_from_new",
446 r#####"
447struct Example { _inner: () }
448
449impl Example {
450 pub fn n$0ew() -> Self {
451 Self { _inner: () }
452 }
453}
454"#####,
455 r#####"
456struct Example { _inner: () }
457
458impl Example {
459 pub fn new() -> Self {
460 Self { _inner: () }
461 }
462}
463
464impl Default for Example {
465 fn default() -> Self {
466 Self::new()
467 }
468}
469"#####,
470 )
471}
472
473#[test]
443fn doctest_generate_derive() { 474fn doctest_generate_derive() {
444 check_doc_test( 475 check_doc_test(
445 "generate_derive", 476 "generate_derive",