1 use clippy_utils
::consts
::{constant, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint_and_help
;
3 use clippy_utils
::higher
;
4 use clippy_utils
::source
::snippet_opt
;
5 use clippy_utils
::{is_direct_expn_of, is_expn_of, match_panic_call}
;
6 use if_chain
::if_chain
;
7 use rustc_hir
::{Expr, ExprKind, UnOp}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
11 declare_clippy_lint
! {
13 /// Checks for `assert!(true)` and `assert!(false)` calls.
15 /// ### Why is this bad?
16 /// Will be optimized out by the compiler or should probably be replaced by a
17 /// `panic!()` or `unreachable!()`
19 /// ### Known problems
26 /// const B: bool = false;
29 pub ASSERTIONS_ON_CONSTANTS
,
31 "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
34 declare_lint_pass
!(AssertionsOnConstants
=> [ASSERTIONS_ON_CONSTANTS
]);
36 impl<'tcx
> LateLintPass
<'tcx
> for AssertionsOnConstants
{
37 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) {
38 let lint_true
= |is_debug
: bool
| {
41 ASSERTIONS_ON_CONSTANTS
,
44 "`debug_assert!(true)` will be optimized out by the compiler"
46 "`assert!(true)` will be optimized out by the compiler"
52 let lint_false_without_message
= || {
55 ASSERTIONS_ON_CONSTANTS
,
57 "`assert!(false)` should probably be replaced",
59 "use `panic!()` or `unreachable!()`",
62 let lint_false_with_message
= |panic_message
: String
| {
65 ASSERTIONS_ON_CONSTANTS
,
67 &format
!("`assert!(false, {})` should probably be replaced", panic_message
),
69 &format
!("use `panic!({})` or `unreachable!({})`", panic_message
, panic_message
),
73 if let Some(debug_assert_span
) = is_expn_of(e
.span
, "debug_assert") {
74 if debug_assert_span
.from_expansion() {
78 if let ExprKind
::Unary(_
, lit
) = e
.kind
;
79 if let Some((Constant
::Bool(is_true
), _
)) = constant(cx
, cx
.typeck_results(), lit
);
85 } else if let Some(assert_span
) = is_direct_expn_of(e
.span
, "assert") {
86 if assert_span
.from_expansion() {
89 if let Some(assert_match
) = match_assert_with_message(cx
, e
) {
91 // matched assert but not message
92 AssertKind
::WithoutMessage(false) => lint_false_without_message(),
93 AssertKind
::WithoutMessage(true) | AssertKind
::WithMessage(_
, true) => lint_true(false),
94 AssertKind
::WithMessage(panic_message
, false) => lint_false_with_message(panic_message
),
101 /// Result of calling `match_assert_with_message`.
103 WithMessage(String
, bool
),
104 WithoutMessage(bool
),
107 /// Check if the expression matches
112 /// ::std::rt::begin_panic(message, _)
117 /// where `message` is any expression and `c` is a constant bool.
118 fn match_assert_with_message
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) -> Option
<AssertKind
> {
120 if let Some(higher
::If { cond, then, .. }
) = higher
::If
::hir(expr
);
121 if let ExprKind
::Unary(UnOp
::Not
, expr
) = cond
.kind
;
122 // bind the first argument of the `assert!` macro
123 if let Some((Constant
::Bool(is_true
), _
)) = constant(cx
, cx
.typeck_results(), expr
);
125 if let ExprKind
::Block(block
, _
) = then
.kind
;
126 if block
.stmts
.is_empty();
127 if let Some(block_expr
) = &block
.expr
;
128 // inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
129 if let Some(begin_panic_call
) = match block_expr
.kind
{
130 ExprKind
::Block(inner_block
, _
) => &inner_block
.expr
,
134 if let Some(arg
) = match_panic_call(cx
, begin_panic_call
);
135 // bind the second argument of the `assert!` macro if it exists
136 if let panic_message
= snippet_opt(cx
, arg
.span
);
137 // second argument of begin_panic is irrelevant
138 // as is the second match arm
140 // an empty message occurs when it was generated by the macro
141 // (and not passed by the user)
143 .filter(|msg
| !msg
.is_empty())
144 .map(|msg
| AssertKind
::WithMessage(msg
, is_true
))
145 .or(Some(AssertKind
::WithoutMessage(is_true
)));