Skip to content

Commit a2502da

Browse files
unexgeweihanglo
andcommitted
Update assertion macro parsing logic for Rust 1.52 changes
Co-authored-by: Weihang Lo <[email protected]>
1 parent 8b9088e commit a2502da

File tree

1 file changed

+32
-10
lines changed

1 file changed

+32
-10
lines changed

clippy_utils/src/macros.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
213213
matches!(name, sym::assert_macro | sym::debug_assert_macro)
214214
}
215215

216+
#[derive(Debug)]
216217
pub enum PanicExpn<'a> {
217218
/// No arguments - `panic!()`
218219
Empty,
@@ -226,10 +227,7 @@ pub enum PanicExpn<'a> {
226227

227228
impl<'a> PanicExpn<'a> {
228229
pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
229-
if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
230-
return None;
231-
}
232-
let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
230+
let ExprKind::Call(callee, [arg, rest @ ..]) = &expr.kind else { return None };
233231
let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
234232
let result = match path.segments.last().unwrap().ident.as_str() {
235233
"panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
@@ -239,6 +237,21 @@ impl<'a> PanicExpn<'a> {
239237
Self::Display(e)
240238
},
241239
"panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
240+
// Since Rust 1.52, `assert_{eq,ne}` macros expand to use:
241+
// `core::panicking::assert_failed(.., left_val, right_val, None | Some(format_args!(..)));`
242+
"assert_failed" => {
243+
// It should have 4 arguments in total (we already matched with the first argument,
244+
// so we're just checking for 3)
245+
if rest.len() != 3 {
246+
return None;
247+
}
248+
// `msg_arg` is either `None` (no custom message) or `Some(format_args!(..))` (custom message)
249+
let msg_arg = &rest[2];
250+
match msg_arg.kind {
251+
ExprKind::Call(_, [fmt_arg]) => Self::Format(FormatArgsExpn::parse(cx, fmt_arg)?),
252+
_ => Self::Empty,
253+
}
254+
},
242255
_ => return None,
243256
};
244257
Some(result)
@@ -251,7 +264,17 @@ pub fn find_assert_args<'a>(
251264
expr: &'a Expr<'a>,
252265
expn: ExpnId,
253266
) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
254-
find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
267+
find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| {
268+
// `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to
269+
// `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to
270+
// `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`).
271+
// So even we got `PanicExpn::Str(..)` that means there is no custom message provided
272+
if let PanicExpn::Str(_) = p {
273+
p = PanicExpn::Empty;
274+
}
275+
276+
(e, p)
277+
})
255278
}
256279

257280
/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
@@ -275,13 +298,12 @@ fn find_assert_args_inner<'a, const N: usize>(
275298
Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
276299
};
277300
let mut args = ArrayVec::new();
278-
let mut panic_expn = None;
279-
let _: Option<!> = for_each_expr(expr, |e| {
301+
let panic_expn = for_each_expr(expr, |e| {
280302
if args.is_full() {
281-
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
282-
panic_expn = PanicExpn::parse(cx, e);
303+
match PanicExpn::parse(cx, e) {
304+
Some(expn) => ControlFlow::Break(expn),
305+
None => ControlFlow::Continue(Descend::Yes),
283306
}
284-
ControlFlow::Continue(Descend::from(panic_expn.is_none()))
285307
} else if is_assert_arg(cx, e, expn) {
286308
args.push(e);
287309
ControlFlow::Continue(Descend::No)

0 commit comments

Comments
 (0)