]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc_early.rs
Merge pull request #1129 from oli-obk/needless_ref
[rust.git] / clippy_lints / src / misc_early.rs
1 use rustc::lint::*;
2 use std::collections::HashMap;
3 use syntax::ast::*;
4 use syntax::codemap::Span;
5 use syntax::visit::FnKind;
6 use utils::{span_lint, span_help_and_lint, snippet, span_lint_and_then};
7 /// **What it does:** This lint checks for structure field patterns bound to wildcards.
8 ///
9 /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on the fields that are actually bound.
10 ///
11 /// **Known problems:** None.
12 ///
13 /// **Example:**
14 /// ```rust
15 /// let { a: _, b: ref b, c: _ } = ..
16 /// ```
17 declare_lint! {
18     pub UNNEEDED_FIELD_PATTERN, Warn,
19     "Struct fields are bound to a wildcard instead of using `..`"
20 }
21
22 /// **What it does:** This lint checks for function arguments having the similar names differing by an underscore
23 ///
24 /// **Why is this bad?** It affects code readability
25 ///
26 /// **Known problems:** None.
27 ///
28 /// **Example:**
29 /// ```rust
30 /// fn foo(a: i32, _a: i32) {}
31 /// ```
32 declare_lint! {
33     pub DUPLICATE_UNDERSCORE_ARGUMENT, Warn,
34     "Function arguments having names which only differ by an underscore"
35 }
36
37 /// **What it does:** This lint detects closures called in the same expression where they are defined.
38 ///
39 /// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
40 ///
41 /// **Known problems:** None.
42 ///
43 /// **Example:**
44 /// ```rust
45 /// (|| 42)()
46 /// ```
47 declare_lint! {
48     pub REDUNDANT_CLOSURE_CALL, Warn,
49     "Closures should not be called in the expression they are defined"
50 }
51
52 /// **What it does:** This lint detects expressions of the form `--x`
53 ///
54 /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was decremented.
55 ///
56 /// **Known problems:** None.
57 ///
58 /// **Example:**
59 /// ```rust
60 /// --x;
61 /// ```
62 declare_lint! {
63     pub DOUBLE_NEG, Warn,
64     "`--x` is a double negation of `x` and not a pre-decrement as in C or C++"
65 }
66
67
68 #[derive(Copy, Clone)]
69 pub struct MiscEarly;
70
71 impl LintPass for MiscEarly {
72     fn get_lints(&self) -> LintArray {
73         lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL, DOUBLE_NEG)
74     }
75 }
76
77 impl EarlyLintPass for MiscEarly {
78     fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
79         if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
80             let mut wilds = 0;
81             let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;
82
83             for field in pfields {
84                 if field.node.pat.node == PatKind::Wild {
85                     wilds += 1;
86                 }
87             }
88             if !pfields.is_empty() && wilds == pfields.len() {
89                 span_help_and_lint(cx,
90                                    UNNEEDED_FIELD_PATTERN,
91                                    pat.span,
92                                    "All the struct fields are matched to a wildcard pattern, consider using `..`.",
93                                    &format!("Try with `{} {{ .. }}` instead", type_name));
94                 return;
95             }
96             if wilds > 0 {
97                 let mut normal = vec![];
98
99                 for field in pfields {
100                     if field.node.pat.node != PatKind::Wild {
101                         if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
102                             normal.push(n);
103                         }
104                     }
105                 }
106                 for field in pfields {
107                     if field.node.pat.node == PatKind::Wild {
108                         wilds -= 1;
109                         if wilds > 0 {
110                             span_lint(cx,
111                                       UNNEEDED_FIELD_PATTERN,
112                                       field.span,
113                                       "You matched a field with a wildcard pattern. Consider using `..` instead");
114                         } else {
115                             span_help_and_lint(cx,
116                                                UNNEEDED_FIELD_PATTERN,
117                                                field.span,
118                                                "You matched a field with a wildcard pattern. Consider using `..` \
119                                                 instead",
120                                                &format!("Try with `{} {{ {}, .. }}`",
121                                                         type_name,
122                                                         normal[..].join(", ")));
123                         }
124                     }
125                 }
126             }
127         }
128     }
129
130     fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
131         let mut registered_names: HashMap<String, Span> = HashMap::new();
132
133         for arg in &decl.inputs {
134             if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
135                 let arg_name = sp_ident.node.to_string();
136
137                 if arg_name.starts_with('_') {
138                     if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
139                         span_lint(cx,
140                                   DUPLICATE_UNDERSCORE_ARGUMENT,
141                                   *correspondence,
142                                   &format!("`{}` already exists, having another argument having almost the same \
143                                             name makes code comprehension and documentation more difficult",
144                                            arg_name[1..].to_owned()));;
145                     }
146                 } else {
147                     registered_names.insert(arg_name, arg.pat.span);
148                 }
149             }
150         }
151     }
152
153     fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
154         match expr.node {
155             ExprKind::Call(ref paren, _) => {
156                 if let ExprKind::Paren(ref closure) = paren.node {
157                     if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
158                         span_lint_and_then(cx,
159                                            REDUNDANT_CLOSURE_CALL,
160                                            expr.span,
161                                            "Try not to call a closure in the expression where it is declared.",
162                                            |db| {
163                                                if decl.inputs.is_empty() {
164                                                    let hint = snippet(cx, block.span, "..").into_owned();
165                                                    db.span_suggestion(expr.span, "Try doing something like: ", hint);
166                                                }
167                                            });
168                     }
169                 }
170             }
171             ExprKind::Unary(UnOp::Neg, ref inner) => {
172                 if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
173                     span_lint(cx,
174                               DOUBLE_NEG,
175                               expr.span,
176                               "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
177     }
178             }
179             _ => ()
180         }
181     }
182
183     fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
184         for w in block.stmts.windows(2) {
185             if_let_chain! {[
186                 let StmtKind::Local(ref local) = w[0].node,
187                 let Option::Some(ref t) = local.init,
188                 let ExprKind::Closure(_, _, _, _) = t.node,
189                 let PatKind::Ident(_, sp_ident, _) = local.pat.node,
190                 let StmtKind::Semi(ref second) = w[1].node,
191                 let ExprKind::Assign(_, ref call) = second.node,
192                 let ExprKind::Call(ref closure, _) = call.node,
193                 let ExprKind::Path(_, ref path) = closure.node
194             ], {
195                 if sp_ident.node == (&path.segments[0]).identifier {
196                     span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
197                 }
198             }}
199         }
200     }
201 }