Skip to content

Commit 47c2516

Browse files
committed
Add empty_test lint
Empty test functions execute no test code and cannot verify behaviour. Provide an opt-in restriction lint so projects can detect these no-op tests while allowing temporary placeholders where appropriate.
1 parent e8bea03 commit 47c2516

6 files changed

Lines changed: 95 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7023,6 +7023,7 @@ Released 2018-09-13
70237023
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/main/index.html#empty_line_after_outer_attr
70247024
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/main/index.html#empty_loop
70257025
[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/main/index.html#empty_structs_with_brackets
7026+
[`empty_test`]: https://rust-lang.github.io/rust-clippy/main/index.html#empty_test
70267027
[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/main/index.html#enum_clike_unportable_variant
70277028
[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/main/index.html#enum_glob_use
70287029
[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/main/index.html#enum_variant_names

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
147147
crate::empty_enums::EMPTY_ENUMS_INFO,
148148
crate::empty_line_after::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
149149
crate::empty_line_after::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
150+
crate::empty_test::EMPTY_TEST_INFO,
150151
crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO,
151152
crate::empty_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
152153
crate::endian_bytes::BIG_ENDIAN_BYTES_INFO,

clippy_lints/src/empty_test.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::is_test_function;
3+
use rustc_hir::intravisit::FnKind;
4+
use rustc_hir::{Body, ExprKind, FnDecl};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::declare_lint_pass;
7+
use rustc_span::Span;
8+
use rustc_span::def_id::LocalDefId;
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks for test functions with an empty body.
13+
///
14+
/// ### Why restrict this?
15+
/// Empty tests do not verify behavior. However, they can serve as temporary placeholders while
16+
/// a test is being developed.
17+
///
18+
/// ### Example
19+
/// ```no_run
20+
/// #[test]
21+
/// fn empty_test() {}
22+
/// ```
23+
///
24+
/// Remove the test if it is no longer needed, or add assertions that exercise the behavior it
25+
/// is intended to test.
26+
#[clippy::version = "1.100.0"]
27+
pub EMPTY_TEST,
28+
restriction,
29+
"test function with an empty body"
30+
}
31+
32+
declare_lint_pass!(EmptyTest => [EMPTY_TEST]);
33+
34+
impl LateLintPass<'_> for EmptyTest {
35+
fn check_fn(
36+
&mut self,
37+
cx: &LateContext<'_>,
38+
kind: FnKind<'_>,
39+
_: &FnDecl<'_>,
40+
body: &Body<'_>,
41+
span: Span,
42+
fn_def_id: LocalDefId,
43+
) {
44+
if matches!(kind, FnKind::ItemFn(..))
45+
&& is_test_function(cx.tcx, fn_def_id)
46+
&& matches!(body.value.kind, ExprKind::Block(block, _) if block.stmts.is_empty() && block.expr.is_none())
47+
{
48+
span_lint(cx, EMPTY_TEST, span, "empty test function");
49+
}
50+
}
51+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ mod else_if_without_else;
121121
mod empty_drop;
122122
mod empty_enums;
123123
mod empty_line_after;
124+
mod empty_test;
124125
mod empty_with_brackets;
125126
mod endian_bytes;
126127
mod entry;
@@ -870,6 +871,7 @@ rustc_lint::late_lint_methods!(
870871
BlockScrutinee: block_scrutinee::BlockScrutinee = block_scrutinee::BlockScrutinee,
871872
NonnullUncheckedOnBoxPtr: nonnull_unchecked_on_box_ptr::NonnullUncheckedOnBoxPtr = nonnull_unchecked_on_box_ptr::NonnullUncheckedOnBoxPtr::new(conf),
872873
NeedlessNonzeroGet: needless_nonzero_get::NeedlessNonzeroGet = needless_nonzero_get::NeedlessNonzeroGet::new(conf),
874+
EmptyTest: empty_test::EmptyTest = empty_test::EmptyTest,
873875
// add late passes here, used by `cargo dev new_lint`
874876
]]
875877
);

tests/ui/empty_test.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![warn(clippy::empty_test)]
2+
3+
#[test]
4+
fn empty() {}
5+
//~^ empty_test
6+
7+
#[test]
8+
fn empty_with_comment() {
9+
// This is still an empty body.
10+
}
11+
//~^^^ empty_test
12+
13+
fn ordinary_empty_function() {}
14+
15+
#[test]
16+
fn meaningful_test() {
17+
let value = String::from("Clippy");
18+
assert_eq!(value.len(), 6);
19+
}
20+
21+
fn main() {}

tests/ui/empty_test.stderr

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: empty test function
2+
--> tests/ui/empty_test.rs:4:1
3+
|
4+
LL | fn empty() {}
5+
| ^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::empty-test` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::empty_test)]`
9+
10+
error: empty test function
11+
--> tests/ui/empty_test.rs:8:1
12+
|
13+
LL | / fn empty_with_comment() {
14+
LL | | // This is still an empty body.
15+
LL | | }
16+
| |_^
17+
18+
error: aborting due to 2 previous errors
19+

0 commit comments

Comments
 (0)