]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/block_in_if_condition.rs
Merge pull request #3269 from rust-lang-nursery/relicense
[rust.git] / clippy_lints / src / block_in_if_condition.rs
1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10
11 use matches::matches;
12 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13 use crate::rustc::{declare_tool_lint, lint_array};
14 use crate::rustc::hir::*;
15 use crate::rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
16 use crate::utils::*;
17
18 /// **What it does:** Checks for `if` conditions that use blocks to contain an
19 /// expression.
20 ///
21 /// **Why is this bad?** It isn't really Rust style, same as using parentheses
22 /// to contain expressions.
23 ///
24 /// **Known problems:** None.
25 ///
26 /// **Example:**
27 /// ```rust
28 /// if { true } ..
29 /// ```
30 declare_clippy_lint! {
31     pub BLOCK_IN_IF_CONDITION_EXPR,
32     style,
33     "braces that can be eliminated in conditions, e.g. `if { true } ...`"
34 }
35
36 /// **What it does:** Checks for `if` conditions that use blocks containing
37 /// statements, or conditions that use closures with blocks.
38 ///
39 /// **Why is this bad?** Using blocks in the condition makes it hard to read.
40 ///
41 /// **Known problems:** None.
42 ///
43 /// **Example:**
44 /// ```rust
45 /// if { let x = somefunc(); x } ..
46 /// // or
47 /// if somefunc(|x| { x == 47 }) ..
48 /// ```
49 declare_clippy_lint! {
50     pub BLOCK_IN_IF_CONDITION_STMT,
51     style,
52     "complex blocks in conditions, e.g. `if { let x = true; x } ...`"
53 }
54
55 #[derive(Copy, Clone)]
56 pub struct BlockInIfCondition;
57
58 impl LintPass for BlockInIfCondition {
59     fn get_lints(&self) -> LintArray {
60         lint_array!(BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT)
61     }
62 }
63
64 struct ExVisitor<'a, 'tcx: 'a> {
65     found_block: Option<&'tcx Expr>,
66     cx: &'a LateContext<'a, 'tcx>,
67 }
68
69 impl<'a, 'tcx: 'a> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
70     fn visit_expr(&mut self, expr: &'tcx Expr) {
71         if let ExprKind::Closure(_, _, eid, _, _) = expr.node {
72             let body = self.cx.tcx.hir.body(eid);
73             let ex = &body.value;
74             if matches!(ex.node, ExprKind::Block(_, _)) {
75                 self.found_block = Some(ex);
76                 return;
77             }
78         }
79         walk_expr(self, expr);
80     }
81     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
82         NestedVisitorMap::None
83     }
84 }
85
86 const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
87 const COMPLEX_BLOCK_MESSAGE: &str = "in an 'if' condition, avoid complex blocks or closures with blocks; \
88                                      instead, move the block or closure higher and bind it with a 'let'";
89
90 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
91     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
92         if let ExprKind::If(ref check, ref then, _) = expr.node {
93             if let ExprKind::Block(ref block, _) = check.node {
94                 if block.rules == DefaultBlock {
95                     if block.stmts.is_empty() {
96                         if let Some(ref ex) = block.expr {
97                             // don't dig into the expression here, just suggest that they remove
98                             // the block
99                             if in_macro(expr.span) || differing_macro_contexts(expr.span, ex.span) {
100                                 return;
101                             }
102                             span_help_and_lint(
103                                 cx,
104                                 BLOCK_IN_IF_CONDITION_EXPR,
105                                 check.span,
106                                 BRACED_EXPR_MESSAGE,
107                                 &format!(
108                                     "try\nif {} {} ... ",
109                                     snippet_block(cx, ex.span, ".."),
110                                     snippet_block(cx, then.span, "..")
111                                 ),
112                             );
113                         }
114                     } else {
115                         let span = block
116                             .expr
117                             .as_ref()
118                             .map_or_else(|| block.stmts[0].span, |e| e.span);
119                         if in_macro(span) || differing_macro_contexts(expr.span, span) {
120                             return;
121                         }
122                         // move block higher
123                         span_help_and_lint(
124                             cx,
125                             BLOCK_IN_IF_CONDITION_STMT,
126                             check.span,
127                             COMPLEX_BLOCK_MESSAGE,
128                             &format!(
129                                 "try\nlet res = {};\nif res {} ... ",
130                                 snippet_block(cx, block.span, ".."),
131                                 snippet_block(cx, then.span, "..")
132                             ),
133                         );
134                     }
135                 }
136             } else {
137                 let mut visitor = ExVisitor {
138                     found_block: None,
139                     cx,
140                 };
141                 walk_expr(&mut visitor, check);
142                 if let Some(block) = visitor.found_block {
143                     span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE);
144                 }
145             }
146         }
147     }
148 }