+
+/// Result of calling `match_assert_with_message`.
+enum AssertKind {
+ WithMessage(String, bool),
+ WithoutMessage(bool),
+}
+
+/// Check if the expression matches
+///
+/// ```rust,ignore
+/// if !c {
+/// {
+/// ::std::rt::begin_panic(message, _)
+/// }
+/// }
+/// ```
+///
+/// where `message` is any expression and `c` is a constant bool.
+fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
+ if_chain! {
+ if let ExprKind::If(ref cond, ref then, _) = expr.kind;
+ if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind;
+ // bind the first argument of the `assert!` macro
+ if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
+ // block
+ if let ExprKind::Block(ref block, _) = then.kind;
+ if block.stmts.is_empty();
+ if let Some(block_expr) = &block.expr;
+ // inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
+ if let Some(begin_panic_call) = match block_expr.kind {
+ ExprKind::Block(ref inner_block, _) => &inner_block.expr,
+ _ => &block.expr,
+ };
+ // function call
+ if let Some(args) = match_panic_call(cx, begin_panic_call);
+ if args.len() == 1;
+ // bind the second argument of the `assert!` macro if it exists
+ if let panic_message = snippet_opt(cx, args[0].span);
+ // second argument of begin_panic is irrelevant
+ // as is the second match arm
+ then {
+ // an empty message occurs when it was generated by the macro
+ // (and not passed by the user)
+ return panic_message
+ .filter(|msg| !msg.is_empty())
+ .map(|msg| AssertKind::WithMessage(msg, is_true))
+ .or(Some(AssertKind::WithoutMessage(is_true)));
+ }
+ }
+ None
+}