diff options
| author | Philipp Krones <hello@philkrones.com> | 2023-03-10 10:53:50 +0100 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2023-03-10 10:53:50 +0100 |
| commit | cf8a67d9ad81547895ec986f8bcb17e912037c38 (patch) | |
| tree | edc3b5da6b9b1cd68b680506a91ee7f59897a66a /clippy_lints/src/missing_assert_message.rs | |
| parent | eceedd9c8b2d42695cf45d1c94e85819feba64bc (diff) | |
| download | rust-cf8a67d9ad81547895ec986f8bcb17e912037c38.tar.gz rust-cf8a67d9ad81547895ec986f8bcb17e912037c38.zip | |
Merge commit '3c06e0b1ce003912f8fe0536d3a7fe22558e38cf' into clippyup
Diffstat (limited to 'clippy_lints/src/missing_assert_message.rs')
| -rw-r--r-- | clippy_lints/src/missing_assert_message.rs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs new file mode 100644 index 00000000000..2214a568d9c --- /dev/null +++ b/clippy_lints/src/missing_assert_message.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks assertions without a custom panic message. + /// + /// ### Why is this bad? + /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails. + /// A good custom message should be more about why the failure of the assertion is problematic + /// and not what is failed because the assertion already conveys that. + /// + /// ### Known problems + /// This lint cannot check the quality of the custom panic messages. + /// Hence, you can suppress this lint simply by adding placeholder messages + /// like "assertion failed". However, we recommend coming up with good messages + /// that provide useful information instead of placeholder messages that + /// don't provide any extra information. + /// + /// ### Example + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Service { ready: bool } + /// fn call(service: Service) { + /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub MISSING_ASSERT_MESSAGE, + restriction, + "checks assertions without a custom panic message" +} + +declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]); + +impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::assert_macro | sym::debug_assert_macro) => true, + Some( + sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro, + ) => false, + _ => return, + }; + + // This lint would be very noisy in tests, so just ignore if we're in test context + if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + return; + } + + let panic_expn = if single_argument { + let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return }; + panic_expn + } else { + let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + panic_expn + }; + + if let PanicExpn::Empty = panic_expn { + span_lint_and_help( + cx, + MISSING_ASSERT_MESSAGE, + macro_call.span, + "assert without any message", + None, + "consider describing why the failing assert is problematic", + ); + } + } +} |
