From 9a30707281d3a978741a549196b71a27284f7240 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 17 Nov 2020 14:22:04 +0100 Subject: Add **Ignore Test** assist --- crates/assists/src/handlers/ignore_test.rs | 34 ++++++++++++++++++++++++++++++ crates/assists/src/lib.rs | 2 ++ crates/assists/src/tests/generated.rs | 20 ++++++++++++++++++ crates/assists/src/utils.rs | 18 ++++++++++++++++ crates/ide/src/fn_references.rs | 5 +++-- crates/ide/src/runnables.rs | 19 +++-------------- 6 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 crates/assists/src/handlers/ignore_test.rs 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 @@ +use syntax::{ast, AstNode}; + +use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists}; + +// Assist: ignore_test +// +// Adds `#[ignore]` attribute to the test. +// +// ``` +// <|>#[test] +// fn arithmetics { +// assert_eq!(2 + 2, 5); +// } +// ``` +// -> +// ``` +// #[test] +// #[ignore] +// fn arithmetics { +// assert_eq!(2 + 2, 5); +// } +// ``` +pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let attr: ast::Attr = ctx.find_node_at_offset()?; + let func = attr.syntax().parent().and_then(ast::Fn::cast)?; + let attr = test_related_attribute(&func)?; + + acc.add( + AssistId("ignore_test", AssistKind::None), + "Ignore this test", + attr.syntax().text_range(), + |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")), + ) +} 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 { mod generate_function; mod generate_impl; mod generate_new; + mod ignore_test; mod infer_function_return_type; mod inline_local_variable; mod introduce_named_lifetime; @@ -189,6 +190,7 @@ mod handlers { generate_function::generate_function, generate_impl::generate_impl, generate_new::generate_new, + ignore_test::ignore_test, infer_function_return_type::infer_function_return_type, inline_local_variable::inline_local_variable, 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 @@ -473,6 +473,26 @@ impl Ctx { ) } +#[test] +fn doctest_ignore_test() { + check_doc_test( + "ignore_test", + r#####" +<|>#[test] +fn arithmetics { + assert_eq!(2 + 2, 5); +} +"#####, + r#####" +#[test] +#[ignore] +fn arithmetics { + assert_eq!(2 + 2, 5); +} +"#####, + ) +} + #[test] fn doctest_infer_function_return_type() { check_doc_test( 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; use itertools::Itertools; use syntax::{ ast::edit::AstNodeEdit, + ast::AttrsOwner, ast::NameOwner, ast::{self, edit, make, ArgListOwner}, AstNode, Direction, @@ -82,6 +83,23 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option { None } +/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as +/// `#[test_case(...)]`, `#[tokio::test]` and similar. +/// Also a regular `#[test]` annotation is supported. +/// +/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, +/// but it's better than not to have the runnables for the tests at all. +pub fn test_related_attribute(fn_def: &ast::Fn) -> Option { + fn_def.attrs().find_map(|attr| { + let path = attr.path()?; + if path.syntax().text().to_string().contains("test") { + Some(attr) + } else { + None + } + }) +} + #[derive(Copy, Clone, PartialEq)] pub enum DefaultMethods { 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 @@ //! This module implements a methods and free functions search in the specified file. //! We have to skip tests, so cannot reuse file_structure module. +use assists::utils::test_related_attribute; use hir::Semantics; use ide_db::RootDatabase; use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; -use crate::{runnables::has_test_related_attribute, FileId, FileRange}; +use crate::{FileId, FileRange}; pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); @@ -15,7 +16,7 @@ pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec Option { ast::Fn::cast(item).and_then(|fn_def| { - if has_test_related_attribute(&fn_def) { + if test_related_attribute(&fn_def).is_some() { None } else { 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 @@ use std::fmt; +use assists::utils::test_related_attribute; use cfg::CfgExpr; use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; use ide_db::RootDatabase; @@ -156,7 +157,7 @@ fn runnable_fn( None => TestId::Name(name_string), }; - if has_test_related_attribute(&fn_def) { + if test_related_attribute(&fn_def).is_some() { let attr = TestAttr::from_fn(&fn_def); RunnableKind::Test { test_id, attr } } else if fn_def.has_atom_attr("bench") { @@ -235,20 +236,6 @@ impl TestAttr { } } -/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as -/// `#[test_case(...)]`, `#[tokio::test]` and similar. -/// Also a regular `#[test]` annotation is supported. -/// -/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, -/// but it's better than not to have the runnables for the tests at all. -pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { - fn_def - .attrs() - .filter_map(|attr| attr.path()) - .map(|path| path.syntax().to_string().to_lowercase()) - .any(|attribute_text| attribute_text.contains("test")) -} - const RUSTDOC_FENCE: &str = "```"; const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = &["", "rust", "should_panic", "edition2015", "edition2018"]; @@ -307,7 +294,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { for item in item_list.items() { match item { ast::Item::Fn(f) => { - if has_test_related_attribute(&f) { + if test_related_attribute(&f).is_some() { return true; } } -- cgit v1.2.3