1 use clippy_utils
::diagnostics
::{span_lint, span_lint_and_sugg}
;
2 use clippy_utils
::get_parent_expr
;
3 use clippy_utils
::higher
;
4 use clippy_utils
::source
::snippet_block_with_applicability
;
5 use clippy_utils
::ty
::implements_trait
;
6 use clippy_utils
::visitors
::{for_each_expr, Descend}
;
7 use core
::ops
::ControlFlow
;
8 use if_chain
::if_chain
;
9 use rustc_errors
::Applicability
;
10 use rustc_hir
::{BlockCheckMode, Expr, ExprKind}
;
11 use rustc_lint
::{LateContext, LateLintPass, LintContext}
;
12 use rustc_middle
::lint
::in_external_macro
;
13 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
16 declare_clippy_lint
! {
18 /// Checks for `if` conditions that use blocks containing an
19 /// expression, statements or conditions that use closures with blocks.
21 /// ### Why is this bad?
22 /// Style, using blocks in the condition makes it hard to read.
26 /// # fn somefunc() -> bool { true };
27 /// if { true } { /* ... */ }
29 /// if { let x = somefunc(); x } { /* ... */ }
34 /// # fn somefunc() -> bool { true };
35 /// if true { /* ... */ }
37 /// let res = { let x = somefunc(); x };
38 /// if res { /* ... */ }
40 #[clippy::version = "1.45.0"]
41 pub BLOCKS_IN_IF_CONDITIONS
,
43 "useless or complex blocks that can be eliminated in conditions"
46 declare_lint_pass
!(BlocksInIfConditions
=> [BLOCKS_IN_IF_CONDITIONS
]);
48 const BRACED_EXPR_MESSAGE
: &str = "omit braces around single expression condition";
49 const COMPLEX_BLOCK_MESSAGE
: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
50 instead, move the block or closure higher and bind it with a `let`";
52 impl<'tcx
> LateLintPass
<'tcx
> for BlocksInIfConditions
{
53 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
54 if in_external_macro(cx
.sess(), expr
.span
) {
57 if let Some(higher
::If { cond, .. }
) = higher
::If
::hir(expr
) {
58 if let ExprKind
::Block(block
, _
) = &cond
.kind
{
59 if block
.rules
== BlockCheckMode
::DefaultBlock
{
60 if block
.stmts
.is_empty() {
61 if let Some(ex
) = &block
.expr
{
62 // don't dig into the expression here, just suggest that they remove
64 if expr
.span
.from_expansion() || ex
.span
.from_expansion() {
67 let mut applicability
= Applicability
::MachineApplicable
;
70 BLOCKS_IN_IF_CONDITIONS
,
76 snippet_block_with_applicability(
89 block
.expr
.as_ref().map_or_else(|| block
.stmts
[0].span
, |e
| e
.span
);
90 if span
.from_expansion() || expr
.span
.from_expansion() {
94 let mut applicability
= Applicability
::MachineApplicable
;
97 BLOCKS_IN_IF_CONDITIONS
,
98 expr
.span
.with_hi(cond
.span
.hi()),
99 COMPLEX_BLOCK_MESSAGE
,
102 "let res = {}; if res",
103 snippet_block_with_applicability(
116 let _
: Option
<!> = for_each_expr(cond
, |e
| {
117 if let ExprKind
::Closure(closure
) = e
.kind
{
118 // do not lint if the closure is called using an iterator (see #1141)
120 if let Some(parent
) = get_parent_expr(cx
, e
);
121 if let ExprKind
::MethodCall(_
, self_arg
, _
, _
) = &parent
.kind
;
122 let caller
= cx
.typeck_results().expr_ty(self_arg
);
123 if let Some(iter_id
) = cx
.tcx
.get_diagnostic_item(sym
::Iterator
);
124 if implements_trait(cx
, caller
, iter_id
, &[]);
126 return ControlFlow
::Continue(Descend
::No
);
130 let body
= cx
.tcx
.hir().body(closure
.body
);
131 let ex
= &body
.value
;
132 if let ExprKind
::Block(block
, _
) = ex
.kind
{
133 if !body
.value
.span
.from_expansion() && !block
.stmts
.is_empty() {
134 span_lint(cx
, BLOCKS_IN_IF_CONDITIONS
, ex
.span
, COMPLEX_BLOCK_MESSAGE
);
135 return ControlFlow
::Continue(Descend
::No
);
139 ControlFlow
::Continue(Descend
::Yes
)