1 //! Checks for needless boolean results of if-else expressions
3 //! This lint is **warn** by default
5 use clippy_utils
::diagnostics
::{span_lint, span_lint_and_sugg}
;
6 use clippy_utils
::source
::snippet_with_applicability
;
7 use clippy_utils
::sugg
::Sugg
;
8 use clippy_utils
::{is_else_clause, is_expn_of}
;
9 use rustc_ast
::ast
::LitKind
;
10 use rustc_errors
::Applicability
;
11 use rustc_hir
::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}
;
12 use rustc_lint
::{LateContext, LateLintPass}
;
13 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
14 use rustc_span
::source_map
::Spanned
;
17 declare_clippy_lint
! {
18 /// **What it does:** Checks for expressions of the form `if c { true } else {
19 /// false }` (or vice versa) and suggests using the condition directly.
21 /// **Why is this bad?** Redundant code.
23 /// **Known problems:** Maybe false positives: Sometimes, the two branches are
24 /// painstakingly documented (which we, of course, do not detect), so they *may*
25 /// have some value. Even then, the documentation can be rewritten to match the
36 /// Could be written as
42 "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
45 declare_clippy_lint
! {
46 /// **What it does:** Checks for expressions of the form `x == true`,
47 /// `x != true` and order comparisons such as `x < true` (or vice versa) and
48 /// suggest using the variable directly.
50 /// **Why is this bad?** Unnecessary code.
52 /// **Known problems:** None.
66 "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
69 declare_lint_pass
!(NeedlessBool
=> [NEEDLESS_BOOL
]);
71 impl<'tcx
> LateLintPass
<'tcx
> for NeedlessBool
{
72 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) {
73 use self::Expression
::{Bool, RetBool}
;
74 if e
.span
.from_expansion() {
77 if let ExprKind
::If(pred
, then_block
, Some(else_expr
)) = e
.kind
{
78 let reduce
= |ret
, not
| {
79 let mut applicability
= Applicability
::MachineApplicable
;
80 let snip
= Sugg
::hir_with_applicability(cx
, pred
, "<predicate>", &mut applicability
);
81 let mut snip
= if not { !snip }
else { snip }
;
84 snip
= snip
.make_return();
87 if is_else_clause(cx
.tcx
, e
) {
88 snip
= snip
.blockify();
95 "this if-then-else expression returns a bool literal",
96 "you can reduce it to",
101 if let ExprKind
::Block(then_block
, _
) = then_block
.kind
{
102 match (fetch_bool_block(then_block
), fetch_bool_expr(else_expr
)) {
103 (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
108 "this if-then-else expression will always return true",
111 (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
116 "this if-then-else expression will always return false",
119 (RetBool(true), RetBool(false)) => reduce(true, false),
120 (Bool(true), Bool(false)) => reduce(false, false),
121 (RetBool(false), RetBool(true)) => reduce(true, true),
122 (Bool(false), Bool(true)) => reduce(false, true),
126 panic
!("IfExpr `then` node is not an `ExprKind::Block`");
132 declare_lint_pass
!(BoolComparison
=> [BOOL_COMPARISON
]);
134 impl<'tcx
> LateLintPass
<'tcx
> for BoolComparison
{
135 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, e
: &'tcx Expr
<'_
>) {
136 if e
.span
.from_expansion() {
140 if let ExprKind
::Binary(Spanned { node, .. }
, ..) = e
.kind
{
141 let ignore_case
= None
::<(fn(_
) -> _
, &str)>;
142 let ignore_no_literal
= None
::<(fn(_
, _
) -> _
, &str)>;
145 let true_case
= Some((|h
| h
, "equality checks against true are unnecessary"));
146 let false_case
= Some((
148 "equality checks against false can be replaced by a negation",
150 check_comparison(cx
, e
, true_case
, false_case
, true_case
, false_case
, ignore_no_literal
);
153 let true_case
= Some((
155 "inequality checks against true can be replaced by a negation",
157 let false_case
= Some((|h
| h
, "inequality checks against false are unnecessary"));
158 check_comparison(cx
, e
, true_case
, false_case
, true_case
, false_case
, ignore_no_literal
);
160 BinOpKind
::Lt
=> check_comparison(
164 Some((|h
| h
, "greater than checks against false are unnecessary")),
167 "less than comparison against true can be replaced by a negation",
171 |l
: Sugg
<'_
>, r
: Sugg
<'_
>| (!l
).bit_and(&r
),
172 "order comparisons between booleans can be simplified",
175 BinOpKind
::Gt
=> check_comparison(
180 "less than comparison against true can be replaced by a negation",
184 Some((|h
| h
, "greater than checks against false are unnecessary")),
186 |l
: Sugg
<'_
>, r
: Sugg
<'_
>| l
.bit_and(&(!r
)),
187 "order comparisons between booleans can be simplified",
196 struct ExpressionInfoWithSpan
{
197 one_side_is_unary_not
: bool
,
202 fn is_unary_not(e
: &Expr
<'_
>) -> (bool
, Span
) {
203 if let ExprKind
::Unary(UnOp
::Not
, operand
) = e
.kind
{
204 return (true, operand
.span
);
209 fn one_side_is_unary_not
<'tcx
>(left_side
: &'tcx Expr
<'_
>, right_side
: &'tcx Expr
<'_
>) -> ExpressionInfoWithSpan
{
210 let left
= is_unary_not(left_side
);
211 let right
= is_unary_not(right_side
);
213 ExpressionInfoWithSpan
{
214 one_side_is_unary_not
: left
.0 != right
.0,
220 fn check_comparison
<'a
, 'tcx
>(
221 cx
: &LateContext
<'tcx
>,
223 left_true
: Option
<(impl FnOnce(Sugg
<'a
>) -> Sugg
<'a
>, &str)>,
224 left_false
: Option
<(impl FnOnce(Sugg
<'a
>) -> Sugg
<'a
>, &str)>,
225 right_true
: Option
<(impl FnOnce(Sugg
<'a
>) -> Sugg
<'a
>, &str)>,
226 right_false
: Option
<(impl FnOnce(Sugg
<'a
>) -> Sugg
<'a
>, &str)>,
227 no_literal
: Option
<(impl FnOnce(Sugg
<'a
>, Sugg
<'a
>) -> Sugg
<'a
>, &str)>,
229 use self::Expression
::{Bool, Other}
;
231 if let ExprKind
::Binary(op
, left_side
, right_side
) = e
.kind
{
233 cx
.typeck_results().expr_ty(left_side
),
234 cx
.typeck_results().expr_ty(right_side
),
236 if is_expn_of(left_side
.span
, "cfg").is_some() || is_expn_of(right_side
.span
, "cfg").is_some() {
239 if l_ty
.is_bool() && r_ty
.is_bool() {
240 let mut applicability
= Applicability
::MachineApplicable
;
242 if let BinOpKind
::Eq
= op
.node
{
243 let expression_info
= one_side_is_unary_not(left_side
, right_side
);
244 if expression_info
.one_side_is_unary_not
{
249 "this comparison might be written more concisely",
250 "try simplifying it as shown",
253 snippet_with_applicability(cx
, expression_info
.left_span
, "..", &mut applicability
),
254 snippet_with_applicability(cx
, expression_info
.right_span
, "..", &mut applicability
)
261 match (fetch_bool_expr(left_side
), fetch_bool_expr(right_side
)) {
262 (Bool(true), Other
) => left_true
.map_or((), |(h
, m
)| {
263 suggest_bool_comparison(cx
, e
, right_side
, applicability
, m
, h
);
265 (Other
, Bool(true)) => right_true
.map_or((), |(h
, m
)| {
266 suggest_bool_comparison(cx
, e
, left_side
, applicability
, m
, h
);
268 (Bool(false), Other
) => left_false
.map_or((), |(h
, m
)| {
269 suggest_bool_comparison(cx
, e
, right_side
, applicability
, m
, h
);
271 (Other
, Bool(false)) => right_false
.map_or((), |(h
, m
)| {
272 suggest_bool_comparison(cx
, e
, left_side
, applicability
, m
, h
);
274 (Other
, Other
) => no_literal
.map_or((), |(h
, m
)| {
275 let left_side
= Sugg
::hir_with_applicability(cx
, left_side
, "..", &mut applicability
);
276 let right_side
= Sugg
::hir_with_applicability(cx
, right_side
, "..", &mut applicability
);
282 "try simplifying it as shown",
283 h(left_side
, right_side
).to_string(),
293 fn suggest_bool_comparison
<'a
, 'tcx
>(
294 cx
: &LateContext
<'tcx
>,
297 mut applicability
: Applicability
,
299 conv_hint
: impl FnOnce(Sugg
<'a
>) -> Sugg
<'a
>,
301 let hint
= if expr
.span
.from_expansion() {
302 if applicability
!= Applicability
::Unspecified
{
303 applicability
= Applicability
::MaybeIncorrect
;
305 Sugg
::hir_with_macro_callsite(cx
, expr
, "..")
307 Sugg
::hir_with_applicability(cx
, expr
, "..", &mut applicability
)
314 "try simplifying it as shown",
315 conv_hint(hint
).to_string(),
326 fn fetch_bool_block(block
: &Block
<'_
>) -> Expression
{
327 match (&*block
.stmts
, block
.expr
.as_ref()) {
328 (&[], Some(e
)) => fetch_bool_expr(&**e
),
329 (&[ref e
], None
) => {
330 if let StmtKind
::Semi(e
) = e
.kind
{
331 if let ExprKind
::Ret(_
) = e
.kind
{
340 _
=> Expression
::Other
,
344 fn fetch_bool_expr(expr
: &Expr
<'_
>) -> Expression
{
346 ExprKind
::Block(block
, _
) => fetch_bool_block(block
),
347 ExprKind
::Lit(ref lit_ptr
) => {
348 if let LitKind
::Bool(value
) = lit_ptr
.node
{
349 Expression
::Bool(value
)
354 ExprKind
::Ret(Some(expr
)) => match fetch_bool_expr(expr
) {
355 Expression
::Bool(value
) => Expression
::RetBool(value
),
356 _
=> Expression
::Other
,
358 _
=> Expression
::Other
,