Skip to content

Add test_without_assertions lint - #17626

Open
willwang-io wants to merge 2 commits into
rust-lang:masterfrom
willwang-io:empty-test
Open

Add test_without_assertions lint#17626
willwang-io wants to merge 2 commits into
rust-lang:masterfrom
willwang-io:empty-test

Conversation

@willwang-io

@willwang-io willwang-io commented Aug 24, 2026

Copy link
Copy Markdown

Fixes #17488

Tests without assertions or other potential failure sites can pass without verifying behavior. This adds test_without_assertions, a suspicious lint that detects these tests, including empty test bodies.

changelog: [test_without_assertions]: add a suspicious lint for tests without assertions or other potential failure sites

  • Followed lint naming conventions
  • Added passing UI tests, including the .stderr file
  • cargo test passes locally
  • Executed cargo dev update_lints
  • Added lint documentation
  • Ran cargo dev fmt

@rustbot rustbot added the S-waiting-on-community-reviews Status: This is awaiting for positive reviews from the community before a maintainer is assigned. label Aug 24, 2026
@rustbot

rustbot commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the pull request, and welcome!

You should hear from one of our reviewers after this PR gets at least 2 reviews from the community.

Please see the contribution instructions for more information.

@rustbot rustbot added needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Aug 24, 2026
@rustbot

rustbot commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@CommanderStorm CommanderStorm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Community review

From the issue:

This can be a more general lint for tests which can never fail or always fail. Empty tests aren't particularly unique other than being the degenerate case.

I think you missed this part.
the lint I guess can start out with only linting for the degenerate case, but it should have the ambition to also lint for the provably true/false cases..

🤔
unless..
Actually I think type tests (where you ensure that for example a types size always is below a cache line for perf) cannot happen.
But that sounds a bit like an edge case
CC @Jarcho if that would be something where a "false positive" (is it one?) is good or bad?

View changes since this review

Comment thread clippy_lints/src/empty_test.rs Outdated
Comment thread clippy_lints/src/empty_test.rs Outdated
Comment thread tests/ui/empty_test.stderr Outdated
Comment on lines +13 to +16
LL | / fn empty_with_comment() {
LL | | // This is still an empty body.
LL | | }
| |_^

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be very nice to get the #[test] attr into this span, but that sounds like some work, so this is also fine likely.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I agree that including #[test] in the span would make the diagnostic clearer. I looked into it, but it seems that #[test] has already been expanded by the time this late lint runs. I left the current function span as is for now. Please let me know if there is a simpler approach I have missed.

@CommanderStorm

CommanderStorm commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

and the requested scope widening from the issue?

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Aug 27, 2026
@rustbot

rustbot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@willwang-io

Copy link
Copy Markdown
Author

@CommanderStorm I checked the existing assertions_on_constants lint, and it already covers things like assert!(true) and assert!(false), and several constant-valued assertion cases.

I am not yet sure which additional always-passing or always-falling test cases this lint should cover without overlapping with it, or flagging useful type/layout regression tests. Could you point me to the intended cases for this PR? I can then extend the lint accordingly.

@CommanderStorm

CommanderStorm commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

I mean a testcase without assertions or other without panic sites would be my idea.
Do note that there are tests that have should panic active, I don't think we should lint on those, except for empty and if non-empty.

@willwang-io

willwang-io commented Aug 27, 2026

Copy link
Copy Markdown
Author

@CommanderStorm Thanks, that makes sense now. I’ll widen this to cover tests with no assertions or other visible panic sites, while treating ordinary calls as possible panic sites.

For #[should_panic] tests, I’ll skip the broader check since the panic may come from a call. Should an empty #[should_panic] test like this be skipped too?

#[test]
#[should_panic]
fn empty_should_panic() {}

It already fails because it returns normally instead of panicking.

@CommanderStorm

Copy link
Copy Markdown
Contributor

I think lining on this since empty is fair too. It is suspicious in any case

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.
Detect test functions with no assertions or potential failure sites,
including empty bodies. This catches test that can pass without
exercising behaviour while preserving expected-panic tests and calls
that may fail internally.
@willwang-io

Copy link
Copy Markdown
Author

@CommanderStorm It looks like this newly added lint is causing some existing UI and ui-toml tests to fail. I’m not sure that adding#![allow(clippy::test_without_assertions)] to unrelated fixtures is the right approach.

Is that expected for a new lint in this category, or is there a different approach you would prefer?

@willwang-io willwang-io changed the title Add empty_test lint Add test_without_assertions lint Aug 28, 2026

@CommanderStorm CommanderStorm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice progress.
I have some minor comments below, but nothing that is hard to fix

View changes since this review

Comment on lines +81 to +86
sym::assert_macro
| sym::assert_eq_macro
| sym::assert_ne_macro
| sym::debug_assert_macro
| sym::debug_assert_eq_macro
| sym::debug_assert_ne_macro

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We nowerdays also have assert_matches and it's debug variant. Could you add a case and testcase for this too?

Comment on lines +75 to +76
let is_failure_site = matches!(expr.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
|| root_macro_call_first_node(cx, expr).is_some_and(|macro_call| {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to explicitly state this false negative of not descending into the called functions in the known problems sections or fix it

}
//~^^^ test_without_assertions

#[allow(clippy::unused_unit)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use expect instead of allow, since it is a tighter fit

Comment on lines +45 to +48
#[test]
fn calls_helper() {
helper();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please annotate that this is a false positive and use black_box for another test where recursing into FN Def's is not possible and one test.


fn helper() {}

fn main() {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our usual strategy (a bit inconsistent) in this case is to move everything into an mod main.
This way, all followups can be nicely placed into issue_* modules ^^

Could you do this?

@CommanderStorm

CommanderStorm commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

I flew over the changes in assertions.
Nothing jumped out at me.
Make sure to use expect(..) instead of assert though ^^
Adding expect(..) to lines in the code that are truely violating this is a an indicator that the lint is working and being useful imo!

Also run an lintcheck run to see if I missed a case that should actually lint in your opinion upon second view.
I am in a plane for the next few hours, so can't do this, sorry 😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) S-waiting-on-community-reviews Status: This is awaiting for positive reviews from the community before a maintainer is assigned.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Empty Test Lint

3 participants