diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-11-17 13:22:43 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-11-17 13:22:43 +0000 |
commit | d4c970d1f21f014eddb550a2c33cacbf9546889d (patch) | |
tree | 3cf3a7bc4d7ef9a327418279354a68c2472db1af /crates | |
parent | 10e3a9879c8714320f9a0729d647da7877f0a753 (diff) | |
parent | 9a30707281d3a978741a549196b71a27284f7240 (diff) |
Merge #6573
6573: Add **Ignore Test** assist r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/ignore_test.rs | 34 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 20 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 18 | ||||
-rw-r--r-- | crates/ide/src/fn_references.rs | 5 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 19 |
6 files changed, 80 insertions, 18 deletions
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs new file mode 100644 index 000000000..d2339184f --- /dev/null +++ b/crates/assists/src/handlers/ignore_test.rs | |||
@@ -0,0 +1,34 @@ | |||
1 | use syntax::{ast, AstNode}; | ||
2 | |||
3 | use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; | ||
4 | |||
5 | // Assist: ignore_test | ||
6 | // | ||
7 | // Adds `#[ignore]` attribute to the test. | ||
8 | // | ||
9 | // ``` | ||
10 | // <|>#[test] | ||
11 | // fn arithmetics { | ||
12 | // assert_eq!(2 + 2, 5); | ||
13 | // } | ||
14 | // ``` | ||
15 | // -> | ||
16 | // ``` | ||
17 | // #[test] | ||
18 | // #[ignore] | ||
19 | // fn arithmetics { | ||
20 | // assert_eq!(2 + 2, 5); | ||
21 | // } | ||
22 | // ``` | ||
23 | pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
24 | let attr: ast::Attr = ctx.find_node_at_offset()?; | ||
25 | let func = attr.syntax().parent().and_then(ast::Fn::cast)?; | ||
26 | let attr = test_related_attribute(&func)?; | ||
27 | |||
28 | acc.add( | ||
29 | AssistId("ignore_test", AssistKind::None), | ||
30 | "Ignore this test", | ||
31 | attr.syntax().text_range(), | ||
32 | |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), | ||
33 | ) | ||
34 | } | ||
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] |
477 | fn doctest_ignore_test() { | ||
478 | check_doc_test( | ||
479 | "ignore_test", | ||
480 | r#####" | ||
481 | <|>#[test] | ||
482 | fn arithmetics { | ||
483 | assert_eq!(2 + 2, 5); | ||
484 | } | ||
485 | "#####, | ||
486 | r#####" | ||
487 | #[test] | ||
488 | #[ignore] | ||
489 | fn arithmetics { | ||
490 | assert_eq!(2 + 2, 5); | ||
491 | } | ||
492 | "#####, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | #[test] | ||
477 | fn doctest_infer_function_return_type() { | 497 | fn 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..d1a0a99b1 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -9,6 +9,7 @@ use ide_db::RootDatabase; | |||
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use syntax::{ | 10 | use 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, |
@@ -82,6 +83,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
82 | None | 83 | None |
83 | } | 84 | } |
84 | 85 | ||
86 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
87 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
88 | /// Also a regular `#[test]` annotation is supported. | ||
89 | /// | ||
90 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
91 | /// but it's better than not to have the runnables for the tests at all. | ||
92 | pub fn test_related_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> { | ||
93 | fn_def.attrs().find_map(|attr| { | ||
94 | let path = attr.path()?; | ||
95 | if path.syntax().text().to_string().contains("test") { | ||
96 | Some(attr) | ||
97 | } else { | ||
98 | None | ||
99 | } | ||
100 | }) | ||
101 | } | ||
102 | |||
85 | #[derive(Copy, Clone, PartialEq)] | 103 | #[derive(Copy, Clone, PartialEq)] |
86 | pub enum DefaultMethods { | 104 | pub enum DefaultMethods { |
87 | Only, | 105 | Only, |
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index 459f201ed..5cbbe306e 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -1,11 +1,12 @@ | |||
1 | //! This module implements a methods and free functions search in the specified file. | 1 | //! This module implements a methods and free functions search in the specified file. |
2 | //! We have to skip tests, so cannot reuse file_structure module. | 2 | //! We have to skip tests, so cannot reuse file_structure module. |
3 | 3 | ||
4 | use assists::utils::test_related_attribute; | ||
4 | use hir::Semantics; | 5 | use hir::Semantics; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
6 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; | 7 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; |
7 | 8 | ||
8 | use crate::{runnables::has_test_related_attribute, FileId, FileRange}; | 9 | use crate::{FileId, FileRange}; |
9 | 10 | ||
10 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { | 11 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { |
11 | let sema = Semantics::new(db); | 12 | let sema = Semantics::new(db); |
@@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRa | |||
15 | 16 | ||
16 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | 17 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { |
17 | ast::Fn::cast(item).and_then(|fn_def| { | 18 | ast::Fn::cast(item).and_then(|fn_def| { |
18 | if has_test_related_attribute(&fn_def) { | 19 | if test_related_attribute(&fn_def).is_some() { |
19 | None | 20 | None |
20 | } else { | 21 | } else { |
21 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) | 22 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 2bd0e86e5..e15411777 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | 2 | ||
3 | use assists::utils::test_related_attribute; | ||
3 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
4 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; | 5 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
5 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
@@ -156,7 +157,7 @@ fn runnable_fn( | |||
156 | None => TestId::Name(name_string), | 157 | None => TestId::Name(name_string), |
157 | }; | 158 | }; |
158 | 159 | ||
159 | if has_test_related_attribute(&fn_def) { | 160 | if test_related_attribute(&fn_def).is_some() { |
160 | let attr = TestAttr::from_fn(&fn_def); | 161 | let attr = TestAttr::from_fn(&fn_def); |
161 | RunnableKind::Test { test_id, attr } | 162 | RunnableKind::Test { test_id, attr } |
162 | } else if fn_def.has_atom_attr("bench") { | 163 | } else if fn_def.has_atom_attr("bench") { |
@@ -235,20 +236,6 @@ impl TestAttr { | |||
235 | } | 236 | } |
236 | } | 237 | } |
237 | 238 | ||
238 | /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as | ||
239 | /// `#[test_case(...)]`, `#[tokio::test]` and similar. | ||
240 | /// Also a regular `#[test]` annotation is supported. | ||
241 | /// | ||
242 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | ||
243 | /// but it's better than not to have the runnables for the tests at all. | ||
244 | pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | ||
245 | fn_def | ||
246 | .attrs() | ||
247 | .filter_map(|attr| attr.path()) | ||
248 | .map(|path| path.syntax().to_string().to_lowercase()) | ||
249 | .any(|attribute_text| attribute_text.contains("test")) | ||
250 | } | ||
251 | |||
252 | const RUSTDOC_FENCE: &str = "```"; | 239 | const RUSTDOC_FENCE: &str = "```"; |
253 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = | 240 | const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = |
254 | &["", "rust", "should_panic", "edition2015", "edition2018"]; | 241 | &["", "rust", "should_panic", "edition2015", "edition2018"]; |
@@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | |||
307 | for item in item_list.items() { | 294 | for item in item_list.items() { |
308 | match item { | 295 | match item { |
309 | ast::Item::Fn(f) => { | 296 | ast::Item::Fn(f) => { |
310 | if has_test_related_attribute(&f) { | 297 | if test_related_attribute(&f).is_some() { |
311 | return true; | 298 | return true; |
312 | } | 299 | } |
313 | } | 300 | } |