aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/handlers/auto_import.rs3
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs3
-rw-r--r--crates/assists/src/handlers/ignore_test.rs103
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs71
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs28
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs613
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs20
-rw-r--r--crates/assists/src/utils.rs21
-rw-r--r--crates/assists/src/utils/import_assets.rs34
-rw-r--r--crates/assists/src/utils/insert_use.rs16
12 files changed, 594 insertions, 322 deletions
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 37dd61266..d665837a2 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
98 98
99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
100 let group = import_group_message(import_assets.import_candidate()); 100 let group = import_group_message(import_assets.import_candidate());
101 let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; 101 let scope =
102 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
102 for (import, _) in proposed_imports { 103 for (import, _) in proposed_imports {
103 acc.add_group( 104 acc.add_group(
104 &group, 105 &group,
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 067afabf2..cac77c49b 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -143,8 +143,7 @@ fn insert_import(
143 if let Some(mut mod_path) = mod_path { 143 if let Some(mut mod_path) = mod_path {
144 mod_path.segments.pop(); 144 mod_path.segments.pop();
145 mod_path.segments.push(variant_hir_name.clone()); 145 mod_path.segments.push(variant_hir_name.clone());
146 let scope = ImportScope::find_insert_use_container(scope_node, ctx)?; 146 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
147
148 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 147 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
149 } 148 }
150 Some(()) 149 Some(())
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
new file mode 100644
index 000000000..5096a0005
--- /dev/null
+++ b/crates/assists/src/handlers/ignore_test.rs
@@ -0,0 +1,103 @@
1use syntax::{
2 ast::{self, AttrsOwner},
3 AstNode, AstToken,
4};
5
6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
7
8// Assist: ignore_test
9//
10// Adds `#[ignore]` attribute to the test.
11//
12// ```
13// <|>#[test]
14// fn arithmetics {
15// assert_eq!(2 + 2, 5);
16// }
17// ```
18// ->
19// ```
20// #[test]
21// #[ignore]
22// fn arithmetics {
23// assert_eq!(2 + 2, 5);
24// }
25// ```
26pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let attr: ast::Attr = ctx.find_node_at_offset()?;
28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29 let attr = test_related_attribute(&func)?;
30
31 match has_ignore_attribute(&func) {
32 None => acc.add(
33 AssistId("ignore_test", AssistKind::None),
34 "Ignore this test",
35 attr.syntax().text_range(),
36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ),
38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None),
40 "Re-enable this test",
41 ignore_attr.syntax().text_range(),
42 |builder| {
43 builder.delete(ignore_attr.syntax().text_range());
44 let whitespace = ignore_attr
45 .syntax()
46 .next_sibling_or_token()
47 .and_then(|x| x.into_token())
48 .and_then(ast::Whitespace::cast);
49 if let Some(whitespace) = whitespace {
50 builder.delete(whitespace.syntax().text_range());
51 }
52 },
53 ),
54 }
55}
56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| {
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65}
66
67#[cfg(test)]
68mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist;
71
72 #[test]
73 fn test_base_case() {
74 check_assist(
75 ignore_test,
76 r#"
77 #[test<|>]
78 fn test() {}
79 "#,
80 r#"
81 #[test]
82 #[ignore]
83 fn test() {}
84 "#,
85 )
86 }
87
88 #[test]
89 fn test_unignore() {
90 check_assist(
91 ignore_test,
92 r#"
93 #[test<|>]
94 #[ignore]
95 fn test() {}
96 "#,
97 r#"
98 #[test]
99 fn test() {}
100 "#,
101 )
102 }
103}
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index 9731344b8..eae6367c1 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -49,12 +49,29 @@ fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
49 macro_text_with_brackets.len() - TextSize::of(')'), 49 macro_text_with_brackets.len() - TextSize::of(')'),
50 )); 50 ));
51 51
52 let is_leaf = macro_call.syntax().next_sibling().is_none(); 52 Some(
53 Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) { 53 if !is_leaf_or_control_flow_expr(macro_call)
54 format!("({})", macro_text_in_brackets) 54 && needs_parentheses_around_macro_contents(contents)
55 } else { 55 {
56 macro_text_in_brackets.to_string() 56 format!("({})", macro_text_in_brackets)
57 }) 57 } else {
58 macro_text_in_brackets.to_string()
59 },
60 )
61}
62
63fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool {
64 macro_call.syntax().next_sibling().is_none()
65 || match macro_call.syntax().parent() {
66 Some(parent) => match_ast! {
67 match parent {
68 ast::Condition(_it) => true,
69 ast::MatchExpr(_it) => true,
70 _ => false,
71 }
72 },
73 None => false,
74 }
58} 75}
59 76
60/// Verifies that the given macro_call actually matches the given name 77/// Verifies that the given macro_call actually matches the given name
@@ -361,4 +378,44 @@ fn main() {
361 r#"let res = (foo..=bar).foo();"#, 378 r#"let res = (foo..=bar).foo();"#,
362 ); 379 );
363 } 380 }
381
382 #[test]
383 fn test_remove_dbg_followed_by_block() {
384 check_assist(
385 remove_dbg,
386 r#"fn foo() {
387 if <|>dbg!(x || y) {}
388}"#,
389 r#"fn foo() {
390 if x || y {}
391}"#,
392 );
393 check_assist(
394 remove_dbg,
395 r#"fn foo() {
396 while let foo = <|>dbg!(&x) {}
397}"#,
398 r#"fn foo() {
399 while let foo = &x {}
400}"#,
401 );
402 check_assist(
403 remove_dbg,
404 r#"fn foo() {
405 if let foo = <|>dbg!(&x) {}
406}"#,
407 r#"fn foo() {
408 if let foo = &x {}
409}"#,
410 );
411 check_assist(
412 remove_dbg,
413 r#"fn foo() {
414 match <|>dbg!(&x) {}
415}"#,
416 r#"fn foo() {
417 match &x {}
418}"#,
419 );
420 }
364} 421}
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 82625516c..453a6cebf 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -62,19 +62,21 @@ pub(crate) fn replace_derive_with_manual_impl(
62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
63 let current_crate = current_module.krate(); 63 let current_crate = current_module.krate();
64 64
65 let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) 65 let found_traits =
66 .into_iter() 66 imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
67 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 67 .filter_map(
68 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 68 |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
69 _ => None, 69 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
70 }) 70 _ => None,
71 .flat_map(|trait_| { 71 },
72 current_module 72 )
73 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 73 .flat_map(|trait_| {
74 .as_ref() 74 current_module
75 .map(mod_path_to_ast) 75 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
76 .zip(Some(trait_)) 76 .as_ref()
77 }); 77 .map(mod_path_to_ast)
78 .zip(Some(trait_))
79 });
78 80
79 let mut no_traits_found = true; 81 let mut no_traits_found = true;
80 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 82 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index d7e1d9580..a66db9ae3 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
34 } 34 }
35 35
36 let target = path.syntax().text_range(); 36 let target = path.syntax().text_range();
37 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 37 let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
38 let syntax = scope.as_syntax_node(); 38 let syntax = scope.as_syntax_node();
39 acc.add( 39 acc.add(
40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), 40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
index 36ef871b9..676db7137 100644
--- a/crates/assists/src/handlers/unwrap_block.rs
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -3,7 +3,7 @@ use syntax::{
3 self, 3 self,
4 edit::{AstNodeEdit, IndentLevel}, 4 edit::{AstNodeEdit, IndentLevel},
5 }, 5 },
6 AstNode, TextRange, T, 6 AstNode, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; 9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
@@ -31,11 +31,21 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
31 31
32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; 32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; 33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
34 let target = block.syntax().text_range();
34 let mut parent = block.syntax().parent()?; 35 let mut parent = block.syntax().parent()?;
35 if ast::MatchArm::can_cast(parent.kind()) { 36 if ast::MatchArm::can_cast(parent.kind()) {
36 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
37 } 38 }
38 39
40 if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) {
41 return acc.add(assist_id, assist_label, target, |builder| {
42 builder.replace(
43 block.syntax().text_range(),
44 update_expr_string(block.to_string(), &[' ', '{', '\n']),
45 );
46 });
47 }
48
39 let parent = ast::Expr::cast(parent)?; 49 let parent = ast::Expr::cast(parent)?;
40 50
41 match parent.clone() { 51 match parent.clone() {
@@ -48,7 +58,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
48 // For `else if` blocks 58 // For `else if` blocks
49 let ancestor_then_branch = ancestor.then_branch()?; 59 let ancestor_then_branch = ancestor.then_branch()?;
50 60
51 let target = then_branch.syntax().text_range();
52 return acc.add(assist_id, assist_label, target, |edit| { 61 return acc.add(assist_id, assist_label, target, |edit| {
53 let range_to_del_else_if = TextRange::new( 62 let range_to_del_else_if = TextRange::new(
54 ancestor_then_branch.syntax().text_range().end(), 63 ancestor_then_branch.syntax().text_range().end(),
@@ -68,7 +77,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
68 }); 77 });
69 } 78 }
70 } else { 79 } else {
71 let target = block.syntax().text_range();
72 return acc.add(assist_id, assist_label, target, |edit| { 80 return acc.add(assist_id, assist_label, target, |edit| {
73 let range_to_del = TextRange::new( 81 let range_to_del = TextRange::new(
74 then_branch.syntax().text_range().end(), 82 then_branch.syntax().text_range().end(),
@@ -84,7 +92,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
84 }; 92 };
85 93
86 let unwrapped = unwrap_trivial_block(block); 94 let unwrapped = unwrap_trivial_block(block);
87 let target = unwrapped.syntax().text_range();
88 acc.add(assist_id, assist_label, target, |builder| { 95 acc.add(assist_id, assist_label, target, |builder| {
89 builder.replace( 96 builder.replace(
90 parent.syntax().text_range(), 97 parent.syntax().text_range(),
@@ -112,31 +119,89 @@ mod tests {
112 use super::*; 119 use super::*;
113 120
114 #[test] 121 #[test]
122 fn unwrap_tail_expr_block() {
123 check_assist(
124 unwrap_block,
125 r#"
126fn main() {
127 <|>{
128 92
129 }
130}
131"#,
132 r#"
133fn main() {
134 92
135}
136"#,
137 )
138 }
139
140 #[test]
141 fn unwrap_stmt_expr_block() {
142 check_assist(
143 unwrap_block,
144 r#"
145fn main() {
146 <|>{
147 92;
148 }
149 ()
150}
151"#,
152 r#"
153fn main() {
154 92;
155 ()
156}
157"#,
158 );
159 // Pedantically, we should add an `;` here...
160 check_assist(
161 unwrap_block,
162 r#"
163fn main() {
164 <|>{
165 92
166 }
167 ()
168}
169"#,
170 r#"
171fn main() {
172 92
173 ()
174}
175"#,
176 );
177 }
178
179 #[test]
115 fn simple_if() { 180 fn simple_if() {
116 check_assist( 181 check_assist(
117 unwrap_block, 182 unwrap_block,
118 r#" 183 r#"
119 fn main() { 184fn main() {
120 bar(); 185 bar();
121 if true {<|> 186 if true {<|>
122 foo(); 187 foo();
123 188
124 //comment 189 //comment
125 bar(); 190 bar();
126 } else { 191 } else {
127 println!("bar"); 192 println!("bar");
128 } 193 }
129 } 194}
130 "#, 195"#,
131 r#" 196 r#"
132 fn main() { 197fn main() {
133 bar(); 198 bar();
134 foo(); 199 foo();
135 200
136 //comment 201 //comment
137 bar(); 202 bar();
138 } 203}
139 "#, 204"#,
140 ); 205 );
141 } 206 }
142 207
@@ -145,30 +210,30 @@ mod tests {
145 check_assist( 210 check_assist(
146 unwrap_block, 211 unwrap_block,
147 r#" 212 r#"
148 fn main() { 213fn main() {
149 bar(); 214 bar();
150 if true { 215 if true {
151 foo(); 216 foo();
152 217
153 //comment 218 //comment
154 bar(); 219 bar();
155 } else {<|> 220 } else {<|>
156 println!("bar"); 221 println!("bar");
157 } 222 }
158 } 223}
159 "#, 224"#,
160 r#" 225 r#"
161 fn main() { 226fn main() {
162 bar(); 227 bar();
163 if true { 228 if true {
164 foo(); 229 foo();
165 230
166 //comment 231 //comment
167 bar(); 232 bar();
168 } 233 }
169 println!("bar"); 234 println!("bar");
170 } 235}
171 "#, 236"#,
172 ); 237 );
173 } 238 }
174 239
@@ -177,32 +242,32 @@ mod tests {
177 check_assist( 242 check_assist(
178 unwrap_block, 243 unwrap_block,
179 r#" 244 r#"
180 fn main() { 245fn main() {
181 //bar(); 246 //bar();
182 if true { 247 if true {
183 println!("true"); 248 println!("true");
184 249
185 //comment 250 //comment
186 //bar(); 251 //bar();
187 } else if false {<|> 252 } else if false {<|>
188 println!("bar"); 253 println!("bar");
189 } else { 254 } else {
190 println!("foo"); 255 println!("foo");
191 } 256 }
192 } 257}
193 "#, 258"#,
194 r#" 259 r#"
195 fn main() { 260fn main() {
196 //bar(); 261 //bar();
197 if true { 262 if true {
198 println!("true"); 263 println!("true");
199 264
200 //comment 265 //comment
201 //bar(); 266 //bar();
202 } 267 }
203 println!("bar"); 268 println!("bar");
204 } 269}
205 "#, 270"#,
206 ); 271 );
207 } 272 }
208 273
@@ -211,34 +276,34 @@ mod tests {
211 check_assist( 276 check_assist(
212 unwrap_block, 277 unwrap_block,
213 r#" 278 r#"
214 fn main() { 279fn main() {
215 //bar(); 280 //bar();
216 if true { 281 if true {
217 println!("true"); 282 println!("true");
218 283
219 //comment 284 //comment
220 //bar(); 285 //bar();
221 } else if false { 286 } else if false {
222 println!("bar"); 287 println!("bar");
223 } else if true {<|> 288 } else if true {<|>
224 println!("foo"); 289 println!("foo");
225 } 290 }
226 } 291}
227 "#, 292"#,
228 r#" 293 r#"
229 fn main() { 294fn main() {
230 //bar(); 295 //bar();
231 if true { 296 if true {
232 println!("true"); 297 println!("true");
233 298
234 //comment 299 //comment
235 //bar(); 300 //bar();
236 } else if false { 301 } else if false {
237 println!("bar"); 302 println!("bar");
238 } 303 }
239 println!("foo"); 304 println!("foo");
240 } 305}
241 "#, 306"#,
242 ); 307 );
243 } 308 }
244 309
@@ -247,38 +312,38 @@ mod tests {
247 check_assist( 312 check_assist(
248 unwrap_block, 313 unwrap_block,
249 r#" 314 r#"
250 fn main() { 315fn main() {
251 //bar(); 316 //bar();
252 if true { 317 if true {
253 println!("true"); 318 println!("true");
254 319
255 //comment 320 //comment
256 //bar(); 321 //bar();
257 } else if false { 322 } else if false {
258 println!("bar"); 323 println!("bar");
259 } else if true { 324 } else if true {
260 println!("foo"); 325 println!("foo");
261 } else {<|> 326 } else {<|>
262 println!("else"); 327 println!("else");
263 } 328 }
264 } 329}
265 "#, 330"#,
266 r#" 331 r#"
267 fn main() { 332fn main() {
268 //bar(); 333 //bar();
269 if true { 334 if true {
270 println!("true"); 335 println!("true");
271 336
272 //comment 337 //comment
273 //bar(); 338 //bar();
274 } else if false { 339 } else if false {
275 println!("bar"); 340 println!("bar");
276 } else if true { 341 } else if true {
277 println!("foo"); 342 println!("foo");
278 } 343 }
279 println!("else"); 344 println!("else");
280 } 345}
281 "#, 346"#,
282 ); 347 );
283 } 348 }
284 349
@@ -287,36 +352,36 @@ mod tests {
287 check_assist( 352 check_assist(
288 unwrap_block, 353 unwrap_block,
289 r#" 354 r#"
290 fn main() { 355fn main() {
291 //bar(); 356 //bar();
292 if true { 357 if true {
293 println!("true"); 358 println!("true");
294 359
295 //comment 360 //comment
296 //bar(); 361 //bar();
297 } else if false { 362 } else if false {
298 println!("bar"); 363 println!("bar");
299 } else if true {<|> 364 } else if true {<|>
300 println!("foo"); 365 println!("foo");
301 } else { 366 } else {
302 println!("else"); 367 println!("else");
303 } 368 }
304 } 369}
305 "#, 370"#,
306 r#" 371 r#"
307 fn main() { 372fn main() {
308 //bar(); 373 //bar();
309 if true { 374 if true {
310 println!("true"); 375 println!("true");
311 376
312 //comment 377 //comment
313 //bar(); 378 //bar();
314 } else if false { 379 } else if false {
315 println!("bar"); 380 println!("bar");
316 } 381 }
317 println!("foo"); 382 println!("foo");
318 } 383}
319 "#, 384"#,
320 ); 385 );
321 } 386 }
322 387
@@ -325,18 +390,18 @@ mod tests {
325 check_assist_not_applicable( 390 check_assist_not_applicable(
326 unwrap_block, 391 unwrap_block,
327 r#" 392 r#"
328 fn main() { 393fn main() {
329 bar();<|> 394 bar();<|>
330 if true { 395 if true {
331 foo(); 396 foo();
332 397
333 //comment 398 //comment
334 bar(); 399 bar();
335 } else { 400 } else {
336 println!("bar"); 401 println!("bar");
337 } 402 }
338 } 403}
339 "#, 404"#,
340 ); 405 );
341 } 406 }
342 407
@@ -345,31 +410,31 @@ mod tests {
345 check_assist( 410 check_assist(
346 unwrap_block, 411 unwrap_block,
347 r#" 412 r#"
348 fn main() { 413fn main() {
349 for i in 0..5 {<|> 414 for i in 0..5 {<|>
350 if true { 415 if true {
351 foo(); 416 foo();
352 417
353 //comment 418 //comment
354 bar(); 419 bar();
355 } else { 420 } else {
356 println!("bar"); 421 println!("bar");
357 } 422 }
358 } 423 }
359 } 424}
360 "#, 425"#,
361 r#" 426 r#"
362 fn main() { 427fn main() {
363 if true { 428 if true {
364 foo(); 429 foo();
365 430
366 //comment 431 //comment
367 bar(); 432 bar();
368 } else { 433 } else {
369 println!("bar"); 434 println!("bar");
370 } 435 }
371 } 436}
372 "#, 437"#,
373 ); 438 );
374 } 439 }
375 440
@@ -378,29 +443,29 @@ mod tests {
378 check_assist( 443 check_assist(
379 unwrap_block, 444 unwrap_block,
380 r#" 445 r#"
381 fn main() { 446fn main() {
382 for i in 0..5 { 447 for i in 0..5 {
383 if true {<|> 448 if true {<|>
384 foo(); 449 foo();
385 450
386 //comment 451 //comment
387 bar(); 452 bar();
388 } else { 453 } else {
389 println!("bar"); 454 println!("bar");
390 } 455 }
391 } 456 }
392 } 457}
393 "#, 458"#,
394 r#" 459 r#"
395 fn main() { 460fn main() {
396 for i in 0..5 { 461 for i in 0..5 {
397 foo(); 462 foo();
398 463
399 //comment 464 //comment
400 bar(); 465 bar();
401 } 466 }
402 } 467}
403 "#, 468"#,
404 ); 469 );
405 } 470 }
406 471
@@ -409,31 +474,31 @@ mod tests {
409 check_assist( 474 check_assist(
410 unwrap_block, 475 unwrap_block,
411 r#" 476 r#"
412 fn main() { 477fn main() {
413 loop {<|> 478 loop {<|>
414 if true { 479 if true {
415 foo(); 480 foo();
416 481
417 //comment 482 //comment
418 bar(); 483 bar();
419 } else { 484 } else {
420 println!("bar"); 485 println!("bar");
421 } 486 }
422 } 487 }
423 } 488}
424 "#, 489"#,
425 r#" 490 r#"
426 fn main() { 491fn main() {
427 if true { 492 if true {
428 foo(); 493 foo();
429 494
430 //comment 495 //comment
431 bar(); 496 bar();
432 } else { 497 } else {
433 println!("bar"); 498 println!("bar");
434 } 499 }
435 } 500}
436 "#, 501"#,
437 ); 502 );
438 } 503 }
439 504
@@ -442,31 +507,31 @@ mod tests {
442 check_assist( 507 check_assist(
443 unwrap_block, 508 unwrap_block,
444 r#" 509 r#"
445 fn main() { 510fn main() {
446 while true {<|> 511 while true {<|>
447 if true { 512 if true {
448 foo(); 513 foo();
449 514
450 //comment 515 //comment
451 bar(); 516 bar();
452 } else { 517 } else {
453 println!("bar"); 518 println!("bar");
454 } 519 }
455 } 520 }
456 } 521}
457 "#, 522"#,
458 r#" 523 r#"
459 fn main() { 524fn main() {
460 if true { 525 if true {
461 foo(); 526 foo();
462 527
463 //comment 528 //comment
464 bar(); 529 bar();
465 } else { 530 } else {
466 println!("bar"); 531 println!("bar");
467 } 532 }
468 } 533}
469 "#, 534"#,
470 ); 535 );
471 } 536 }
472 537
@@ -499,19 +564,19 @@ fn main() {
499 check_assist_not_applicable( 564 check_assist_not_applicable(
500 unwrap_block, 565 unwrap_block,
501 r#" 566 r#"
502 fn main() { 567fn main() {
503 while true { 568 while true {
504 if true { 569 if true {
505 foo();<|> 570 foo();<|>
506 571
507 //comment 572 //comment
508 bar(); 573 bar();
509 } else { 574 } else {
510 println!("bar"); 575 println!("bar");
511 } 576 }
512 } 577 }
513 } 578}
514 "#, 579"#,
515 ); 580 );
516 } 581 }
517} 582}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index e8d81b33d..17e9312db 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -141,6 +141,7 @@ mod handlers {
141 mod generate_function; 141 mod generate_function;
142 mod generate_impl; 142 mod generate_impl;
143 mod generate_new; 143 mod generate_new;
144 mod ignore_test;
144 mod infer_function_return_type; 145 mod infer_function_return_type;
145 mod inline_local_variable; 146 mod inline_local_variable;
146 mod introduce_named_lifetime; 147 mod introduce_named_lifetime;
@@ -189,6 +190,7 @@ mod handlers {
189 generate_function::generate_function, 190 generate_function::generate_function,
190 generate_impl::generate_impl, 191 generate_impl::generate_impl,
191 generate_new::generate_new, 192 generate_new::generate_new,
193 ignore_test::ignore_test,
192 infer_function_return_type::infer_function_return_type, 194 infer_function_return_type::infer_function_return_type,
193 inline_local_variable::inline_local_variable, 195 inline_local_variable::inline_local_variable,
194 introduce_named_lifetime::introduce_named_lifetime, 196 introduce_named_lifetime::introduce_named_lifetime,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index dbf4f21aa..5a9d1a01b 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -474,6 +474,26 @@ impl<T: Clone> Ctx<T> {
474} 474}
475 475
476#[test] 476#[test]
477fn doctest_ignore_test() {
478 check_doc_test(
479 "ignore_test",
480 r#####"
481<|>#[test]
482fn arithmetics {
483 assert_eq!(2 + 2, 5);
484}
485"#####,
486 r#####"
487#[test]
488#[ignore]
489fn arithmetics {
490 assert_eq!(2 + 2, 5);
491}
492"#####,
493 )
494}
495
496#[test]
477fn doctest_infer_function_return_type() { 497fn doctest_infer_function_return_type() {
478 check_doc_test( 498 check_doc_test(
479 "infer_function_return_type", 499 "infer_function_return_type",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 7bd338e99..66c0cdd5f 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -9,6 +9,7 @@ use ide_db::RootDatabase;
9use itertools::Itertools; 9use itertools::Itertools;
10use syntax::{ 10use syntax::{
11 ast::edit::AstNodeEdit, 11 ast::edit::AstNodeEdit,
12 ast::AttrsOwner,
12 ast::NameOwner, 13 ast::NameOwner,
13 ast::{self, edit, make, ArgListOwner}, 14 ast::{self, edit, make, ArgListOwner},
14 AstNode, Direction, 15 AstNode, Direction,
@@ -21,8 +22,7 @@ use crate::{
21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 22 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
22}; 23};
23 24
24pub use insert_use::MergeBehaviour; 25pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
25pub(crate) use insert_use::{insert_use, ImportScope};
26 26
27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 27pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
28 let mut segments = Vec::new(); 28 let mut segments = Vec::new();
@@ -82,6 +82,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
82 None 82 None
83} 83}
84 84
85/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
86/// `#[test_case(...)]`, `#[tokio::test]` and similar.
87/// Also a regular `#[test]` annotation is supported.
88///
89/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
90/// but it's better than not to have the runnables for the tests at all.
91pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
92 fn_def.attrs().find_map(|attr| {
93 let path = attr.path()?;
94 if path.syntax().text().to_string().contains("test") {
95 Some(attr)
96 } else {
97 None
98 }
99 })
100}
101
85#[derive(Copy, Clone, PartialEq)] 102#[derive(Copy, Clone, PartialEq)]
86pub enum DefaultMethods { 103pub enum DefaultMethods {
87 Only, 104 Only,
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index f47edbb76..ff5c0e78e 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -179,21 +179,25 @@ impl ImportAssets {
179 } 179 }
180 }; 180 };
181 181
182 let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query()) 182 let mut res =
183 .into_iter() 183 imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query())
184 .filter_map(filter) 184 .filter_map(filter)
185 .filter_map(|candidate| { 185 .filter_map(|candidate| {
186 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 186 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
187 if let Some(prefix_kind) = prefixed { 187 if let Some(prefix_kind) = prefixed {
188 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind) 188 self.module_with_name_to_import.find_use_path_prefixed(
189 } else { 189 db,
190 self.module_with_name_to_import.find_use_path(db, item) 190 item,
191 } 191 prefix_kind,
192 .map(|path| (path, item)) 192 )
193 }) 193 } else {
194 .filter(|(use_path, _)| !use_path.segments.is_empty()) 194 self.module_with_name_to_import.find_use_path(db, item)
195 .take(20) 195 }
196 .collect::<Vec<_>>(); 196 .map(|path| (path, item))
197 })
198 .filter(|(use_path, _)| use_path.len() > 1)
199 .take(20)
200 .collect::<Vec<_>>();
197 res.sort_by_key(|(path, _)| path.clone()); 201 res.sort_by_key(|(path, _)| path.clone());
198 res 202 res
199 } 203 }
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index af3fc96b6..423782a0e 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -1,6 +1,8 @@
1//! Handle syntactic aspects of inserting a new `use`. 1//! Handle syntactic aspects of inserting a new `use`.
2use std::{cmp::Ordering, iter::successors}; 2use std::{cmp::Ordering, iter::successors};
3 3
4use hir::Semantics;
5use ide_db::RootDatabase;
4use itertools::{EitherOrBoth, Itertools}; 6use itertools::{EitherOrBoth, Itertools};
5use syntax::{ 7use syntax::{
6 algo::SyntaxRewriter, 8 algo::SyntaxRewriter,
@@ -13,8 +15,8 @@ use syntax::{
13}; 15};
14use test_utils::mark; 16use test_utils::mark;
15 17
16#[derive(Debug)] 18#[derive(Debug, Clone)]
17pub(crate) enum ImportScope { 19pub enum ImportScope {
18 File(ast::SourceFile), 20 File(ast::SourceFile),
19 Module(ast::ItemList), 21 Module(ast::ItemList),
20} 22}
@@ -31,14 +33,14 @@ impl ImportScope {
31 } 33 }
32 34
33 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`. 35 /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
34 pub(crate) fn find_insert_use_container( 36 pub fn find_insert_use_container(
35 position: &SyntaxNode, 37 position: &SyntaxNode,
36 ctx: &crate::assist_context::AssistContext, 38 sema: &Semantics<'_, RootDatabase>,
37 ) -> Option<Self> { 39 ) -> Option<Self> {
38 ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from) 40 sema.ancestors_with_macros(position.clone()).find_map(Self::from)
39 } 41 }
40 42
41 pub(crate) fn as_syntax_node(&self) -> &SyntaxNode { 43 pub fn as_syntax_node(&self) -> &SyntaxNode {
42 match self { 44 match self {
43 ImportScope::File(file) => file.syntax(), 45 ImportScope::File(file) => file.syntax(),
44 ImportScope::Module(item_list) => item_list.syntax(), 46 ImportScope::Module(item_list) => item_list.syntax(),
@@ -88,7 +90,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
88} 90}
89 91
90/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. 92/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
91pub(crate) fn insert_use<'a>( 93pub fn insert_use<'a>(
92 scope: &ImportScope, 94 scope: &ImportScope,
93 path: ast::Path, 95 path: ast::Path,
94 merge: Option<MergeBehaviour>, 96 merge: Option<MergeBehaviour>,