@@ -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 ) ]
216217pub enum PanicExpn < ' a > {
217218 /// No arguments - `panic!()`
218219 Empty ,
@@ -226,10 +227,7 @@ pub enum PanicExpn<'a> {
226227
227228impl < ' 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