Skip to content

Commit cc616a4

Browse files
unexgeweihanglo
andcommitted
Use late lint pass for missing_assert_message lint
Co-authored-by: Weihang Lo <[email protected]>
1 parent 20a3874 commit cc616a4

File tree

2 files changed

+27
-81
lines changed

2 files changed

+27
-81
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
912912
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
913913
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
914914
store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters));
915-
store.register_pre_expansion_pass(|| Box::<missing_assert_message::MissingAssertMessage>::default());
915+
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
916916
// add lints here, do not remove this comment, it's used in `new_lint`
917917
}
918918

+26-80
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2-
use rustc_ast::ast;
3-
use rustc_ast::{
4-
token::{Token, TokenKind},
5-
tokenstream::TokenTree,
6-
};
7-
use rustc_lint::{EarlyContext, EarlyLintPass};
8-
use rustc_session::{declare_tool_lint, impl_lint_pass};
2+
use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
3+
use clippy_utils::{is_in_cfg_test, is_in_test_function};
4+
use rustc_hir::Expr;
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
97
use rustc_span::sym;
108

119
declare_clippy_lint! {
@@ -37,93 +35,41 @@ declare_clippy_lint! {
3735
"checks assertions without a custom panic message"
3836
}
3937

40-
#[derive(Default, Clone, Debug)]
41-
pub struct MissingAssertMessage {
42-
// This field will be greater than zero if we are inside a `#[test]` or `#[cfg(test)]`
43-
test_deepnes: usize,
44-
}
38+
declare_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
4539

46-
impl_lint_pass!(MissingAssertMessage => [MISSING_ASSERT_MESSAGE]);
40+
impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
41+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
42+
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
43+
let single_argument = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
44+
Some(sym::assert_macro | sym::debug_assert_macro) => true,
45+
Some(
46+
sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro,
47+
) => false,
48+
_ => return,
49+
};
4750

48-
impl EarlyLintPass for MissingAssertMessage {
49-
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac_call: &ast::MacCall) {
50-
if self.test_deepnes != 0 {
51+
// This lint would be very noisy in tests, so just ignore if we're in test context
52+
if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
5153
return;
5254
}
5355

54-
let Some(last_segment) = mac_call.path.segments.last() else { return; };
55-
let num_separators_needed = match last_segment.ident.as_str() {
56-
"assert" | "debug_assert" => 1,
57-
"assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne" => 2,
58-
_ => return,
56+
let panic_expn = if single_argument {
57+
let Some((_, panic_expn)) = find_assert_args(cx, expr, macro_call.expn) else { return };
58+
panic_expn
59+
} else {
60+
let Some((_, _, panic_expn)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
61+
panic_expn
5962
};
60-
let num_separators = num_commas_on_arguments(mac_call);
6163

62-
if num_separators < num_separators_needed {
64+
if let PanicExpn::Empty = panic_expn {
6365
span_lint_and_help(
6466
cx,
6567
MISSING_ASSERT_MESSAGE,
66-
mac_call.span(),
68+
macro_call.span,
6769
"assert without any message",
6870
None,
6971
"consider describing why the failing assert is problematic",
7072
);
7173
}
7274
}
73-
74-
fn check_item(&mut self, _: &EarlyContext<'_>, item: &ast::Item) {
75-
if item.attrs.iter().any(is_a_test_attribute) {
76-
self.test_deepnes += 1;
77-
}
78-
}
79-
80-
fn check_item_post(&mut self, _: &EarlyContext<'_>, item: &ast::Item) {
81-
if item.attrs.iter().any(is_a_test_attribute) {
82-
self.test_deepnes -= 1;
83-
}
84-
}
85-
}
86-
87-
// Returns number of commas (excluding trailing comma) from `MacCall`'s arguments.
88-
fn num_commas_on_arguments(mac_call: &ast::MacCall) -> usize {
89-
let mut num_separators = 0;
90-
let mut is_trailing = false;
91-
for tt in mac_call.args.tokens.trees() {
92-
match tt {
93-
TokenTree::Token(
94-
Token {
95-
kind: TokenKind::Comma,
96-
span: _,
97-
},
98-
_,
99-
) => {
100-
num_separators += 1;
101-
is_trailing = true;
102-
},
103-
_ => {
104-
is_trailing = false;
105-
},
106-
}
107-
}
108-
if is_trailing {
109-
num_separators -= 1;
110-
}
111-
num_separators
112-
}
113-
114-
// Returns true if the attribute is either a `#[test]` or a `#[cfg(test)]`.
115-
fn is_a_test_attribute(attr: &ast::Attribute) -> bool {
116-
if attr.has_name(sym::test) {
117-
return true;
118-
}
119-
120-
if attr.has_name(sym::cfg)
121-
&& let Some(items) = attr.meta_item_list()
122-
&& let [item] = &*items
123-
&& item.has_name(sym::test)
124-
{
125-
true
126-
} else {
127-
false
128-
}
12975
}

0 commit comments

Comments
 (0)