1 //! Checks for if expressions that contain only an if expression.
3 //! For example, the lint would catch:
8 //! println!("Hello world");
13 //! This lint is **warn** by default
18 use utils
::{in_macro, snippet_block, span_lint_and_then, span_lint_and_sugg}
;
19 use utils
::sugg
::Sugg
;
21 /// **What it does:** Checks for nested `if` statements which can be collapsed
22 /// by `&&`-combining their conditions and for `else { if ... }` expressions
24 /// can be collapsed to `else if ...`.
26 /// **Why is this bad?** Each `if`-statement adds one level of nesting, which
27 /// makes code look more complex than it really is.
29 /// **Known problems:** None.
50 /// Should be written:
68 "`if`s that can be collapsed (e.g. `if x { if y { ... } }` and `else { if x { ... } }`)"
71 #[derive(Copy, Clone)]
72 pub struct CollapsibleIf
;
74 impl LintPass
for CollapsibleIf
{
75 fn get_lints(&self) -> LintArray
{
76 lint_array
!(COLLAPSIBLE_IF
)
80 impl EarlyLintPass
for CollapsibleIf
{
81 fn check_expr(&mut self, cx
: &EarlyContext
, expr
: &ast
::Expr
) {
82 if !in_macro(expr
.span
) {
88 fn check_if(cx
: &EarlyContext
, expr
: &ast
::Expr
) {
90 ast
::ExprKind
::If(ref check
, ref then
, ref else_
) => {
91 if let Some(ref else_
) = *else_
{
92 check_collapsible_maybe_if_let(cx
, else_
);
94 check_collapsible_no_if_let(cx
, expr
, check
, then
);
97 ast
::ExprKind
::IfLet(_
, _
, _
, Some(ref else_
)) => {
98 check_collapsible_maybe_if_let(cx
, else_
);
104 fn check_collapsible_maybe_if_let(cx
: &EarlyContext
, else_
: &ast
::Expr
) {
106 let ast
::ExprKind
::Block(ref block
) = else_
.node
,
107 let Some(else_
) = expr_block(block
),
108 !in_macro(else_
.span
),
111 ast
::ExprKind
::If(..) | ast
::ExprKind
::IfLet(..) => {
112 span_lint_and_sugg(cx
,
115 "this `else { if .. }` block can be collapsed",
117 snippet_block(cx
, else_
.span
, "..").into_owned());
124 fn check_collapsible_no_if_let(cx
: &EarlyContext
, expr
: &ast
::Expr
, check
: &ast
::Expr
, then
: &ast
::Block
) {
126 let Some(inner
) = expr_block(then
),
127 let ast
::ExprKind
::If(ref check_inner
, ref content
, None
) = inner
.node
,
129 if expr
.span
.ctxt() != inner
.span
.ctxt() {
132 span_lint_and_then(cx
, COLLAPSIBLE_IF
, expr
.span
, "this if statement can be collapsed", |db
| {
133 let lhs
= Sugg
::ast(cx
, check
, "..");
134 let rhs
= Sugg
::ast(cx
, check_inner
, "..");
135 db
.span_suggestion(expr
.span
,
139 snippet_block(cx
, content
.span
, "..")));
144 /// If the block contains only one expression, return it.
145 fn expr_block(block
: &ast
::Block
) -> Option
<&ast
::Expr
> {
146 let mut it
= block
.stmts
.iter();
148 if let (Some(stmt
), None
) = (it
.next(), it
.next()) {
150 ast
::StmtKind
::Expr(ref expr
) |
151 ast
::StmtKind
::Semi(ref expr
) => Some(expr
),