@@ -213,6 +213,7 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
213
213
matches ! ( name, sym:: assert_macro | sym:: debug_assert_macro)
214
214
}
215
215
216
+ #[ derive( Debug ) ]
216
217
pub enum PanicExpn < ' a > {
217
218
/// No arguments - `panic!()`
218
219
Empty ,
@@ -226,10 +227,7 @@ pub enum PanicExpn<'a> {
226
227
227
228
impl < ' a > PanicExpn < ' a > {
228
229
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 } ;
233
231
let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = & callee. kind else { return None } ;
234
232
let result = match path. segments . last ( ) . unwrap ( ) . ident . as_str ( ) {
235
233
"panic" if arg. span . ctxt ( ) == expr. span . ctxt ( ) => Self :: Empty ,
@@ -239,6 +237,21 @@ impl<'a> PanicExpn<'a> {
239
237
Self :: Display ( e)
240
238
} ,
241
239
"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
+ } ,
242
255
_ => return None ,
243
256
} ;
244
257
Some ( result)
@@ -251,7 +264,17 @@ pub fn find_assert_args<'a>(
251
264
expr : & ' a Expr < ' a > ,
252
265
expn : ExpnId ,
253
266
) -> 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
+ } )
255
278
}
256
279
257
280
/// 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>(
275
298
Some ( inner_name) => find_assert_within_debug_assert ( cx, expr, expn, Symbol :: intern ( inner_name) ) ?,
276
299
} ;
277
300
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| {
280
302
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 ) ,
283
306
}
284
- ControlFlow :: Continue ( Descend :: from ( panic_expn. is_none ( ) ) )
285
307
} else if is_assert_arg ( cx, e, expn) {
286
308
args. push ( e) ;
287
309
ControlFlow :: Continue ( Descend :: No )
0 commit comments