]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc_early.rs
Remove import of rustc
[rust.git] / clippy_lints / src / misc_early.rs
1 use rustc::lint::*;
2 use rustc::{declare_lint, lint_array};
3 use std::collections::HashMap;
4 use std::char;
5 use syntax::ast::*;
6 use syntax::codemap::Span;
7 use syntax::visit::FnKind;
8 use crate::utils::{constants, in_external_macro, snippet, snippet_opt, span_help_and_lint, span_lint, span_lint_and_then};
9
10 /// **What it does:** Checks for structure field patterns bound to wildcards.
11 ///
12 /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
13 /// the fields that are actually bound.
14 ///
15 /// **Known problems:** None.
16 ///
17 /// **Example:**
18 /// ```rust
19 /// let { a: _, b: ref b, c: _ } = ..
20 /// ```
21 declare_clippy_lint! {
22     pub UNNEEDED_FIELD_PATTERN,
23     style,
24     "struct fields bound to a wildcard instead of using `..`"
25 }
26
27 /// **What it does:** Checks for function arguments having the similar names
28 /// differing by an underscore.
29 ///
30 /// **Why is this bad?** It affects code readability.
31 ///
32 /// **Known problems:** None.
33 ///
34 /// **Example:**
35 /// ```rust
36 /// fn foo(a: i32, _a: i32) {}
37 /// ```
38 declare_clippy_lint! {
39     pub DUPLICATE_UNDERSCORE_ARGUMENT,
40     style,
41     "function arguments having names which only differ by an underscore"
42 }
43
44 /// **What it does:** Detects closures called in the same expression where they
45 /// are defined.
46 ///
47 /// **Why is this bad?** It is unnecessarily adding to the expression's
48 /// complexity.
49 ///
50 /// **Known problems:** None.
51 ///
52 /// **Example:**
53 /// ```rust
54 /// (|| 42)()
55 /// ```
56 declare_clippy_lint! {
57     pub REDUNDANT_CLOSURE_CALL,
58     complexity,
59     "throwaway closures called in the expression they are defined"
60 }
61
62 /// **What it does:** Detects expressions of the form `--x`.
63 ///
64 /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
65 /// decremented.
66 ///
67 /// **Known problems:** None.
68 ///
69 /// **Example:**
70 /// ```rust
71 /// --x;
72 /// ```
73 declare_clippy_lint! {
74     pub DOUBLE_NEG,
75     style,
76     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
77 }
78
79 /// **What it does:** Warns on hexadecimal literals with mixed-case letter
80 /// digits.
81 ///
82 /// **Why is this bad?** It looks confusing.
83 ///
84 /// **Known problems:** None.
85 ///
86 /// **Example:**
87 /// ```rust
88 /// let y = 0x1a9BAcD;
89 /// ```
90 declare_clippy_lint! {
91     pub MIXED_CASE_HEX_LITERALS,
92     style,
93     "hex literals whose letter digits are not consistently upper- or lowercased"
94 }
95
96 /// **What it does:** Warns if literal suffixes are not separated by an
97 /// underscore.
98 ///
99 /// **Why is this bad?** It is much less readable.
100 ///
101 /// **Known problems:** None.
102 ///
103 /// **Example:**
104 /// ```rust
105 /// let y = 123832i32;
106 /// ```
107 declare_clippy_lint! {
108     pub UNSEPARATED_LITERAL_SUFFIX,
109     pedantic,
110     "literals whose suffix is not separated by an underscore"
111 }
112
113 /// **What it does:** Warns if an integral constant literal starts with `0`.
114 ///
115 /// **Why is this bad?** In some languages (including the infamous C language
116 /// and most of its
117 /// family), this marks an octal constant. In Rust however, this is a decimal
118 /// constant. This could
119 /// be confusing for both the writer and a reader of the constant.
120 ///
121 /// **Known problems:** None.
122 ///
123 /// **Example:**
124 ///
125 /// In Rust:
126 /// ```rust
127 /// fn main() {
128 ///     let a = 0123;
129 ///     println!("{}", a);
130 /// }
131 /// ```
132 ///
133 /// prints `123`, while in C:
134 ///
135 /// ```c
136 /// #include <stdio.h>
137 ///
138 /// int main() {
139 ///     int a = 0123;
140 ///     printf("%d\n", a);
141 /// }
142 /// ```
143 ///
144 /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
145 declare_clippy_lint! {
146     pub ZERO_PREFIXED_LITERAL,
147     complexity,
148     "integer literals starting with `0`"
149 }
150
151 /// **What it does:** Warns if a generic shadows a built-in type.
152 ///
153 /// **Why is this bad?** This gives surprising type errors.
154 ///
155 /// **Known problems:** None.
156 ///
157 /// **Example:**
158 ///
159 /// ```rust
160 /// impl<u32> Foo<u32> {
161 ///     fn impl_func(&self) -> u32 {
162 ///         42
163 ///     }
164 /// }
165 /// ```
166 declare_clippy_lint! {
167     pub BUILTIN_TYPE_SHADOW,
168     style,
169     "shadowing a builtin type"
170 }
171
172 #[derive(Copy, Clone)]
173 pub struct MiscEarly;
174
175 impl LintPass for MiscEarly {
176     fn get_lints(&self) -> LintArray {
177         lint_array!(
178             UNNEEDED_FIELD_PATTERN,
179             DUPLICATE_UNDERSCORE_ARGUMENT,
180             REDUNDANT_CLOSURE_CALL,
181             DOUBLE_NEG,
182             MIXED_CASE_HEX_LITERALS,
183             UNSEPARATED_LITERAL_SUFFIX,
184             ZERO_PREFIXED_LITERAL,
185             BUILTIN_TYPE_SHADOW
186         )
187     }
188 }
189
190 impl EarlyLintPass for MiscEarly {
191     fn check_generics(&mut self, cx: &EarlyContext, gen: &Generics) {
192         for param in &gen.params {
193             if let GenericParamKind::Type { .. } = param.kind {
194                 let name = param.ident.as_str();
195                 if constants::BUILTIN_TYPES.contains(&&*name) {
196                     span_lint(
197                         cx,
198                         BUILTIN_TYPE_SHADOW,
199                         param.ident.span,
200                         &format!("This generic shadows the built-in type `{}`", name),
201                     );
202                 }
203             }
204         }
205     }
206
207     fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
208         if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
209             let mut wilds = 0;
210             let type_name = npat.segments
211                 .last()
212                 .expect("A path must have at least one segment")
213                 .ident
214                 .name;
215
216             for field in pfields {
217                 if let PatKind::Wild = field.node.pat.node {
218                     wilds += 1;
219                 }
220             }
221             if !pfields.is_empty() && wilds == pfields.len() {
222                 span_help_and_lint(
223                     cx,
224                     UNNEEDED_FIELD_PATTERN,
225                     pat.span,
226                     "All the struct fields are matched to a wildcard pattern, consider using `..`.",
227                     &format!("Try with `{} {{ .. }}` instead", type_name),
228                 );
229                 return;
230             }
231             if wilds > 0 {
232                 let mut normal = vec![];
233
234                 for field in pfields {
235                     match field.node.pat.node {
236                         PatKind::Wild => {},
237                         _ => if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
238                             normal.push(n);
239                         },
240                     }
241                 }
242                 for field in pfields {
243                     if let PatKind::Wild = field.node.pat.node {
244                         wilds -= 1;
245                         if wilds > 0 {
246                             span_lint(
247                                 cx,
248                                 UNNEEDED_FIELD_PATTERN,
249                                 field.span,
250                                 "You matched a field with a wildcard pattern. Consider using `..` instead",
251                             );
252                         } else {
253                             span_help_and_lint(
254                                 cx,
255                                 UNNEEDED_FIELD_PATTERN,
256                                 field.span,
257                                 "You matched a field with a wildcard pattern. Consider using `..` \
258                                  instead",
259                                 &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
260                             );
261                         }
262                     }
263                 }
264             }
265         }
266     }
267
268     fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: Span, _: NodeId) {
269         let mut registered_names: HashMap<String, Span> = HashMap::new();
270
271         for arg in &decl.inputs {
272             if let PatKind::Ident(_, ident, None) = arg.pat.node {
273                 let arg_name = ident.to_string();
274
275                 if arg_name.starts_with('_') {
276                     if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
277                         span_lint(
278                             cx,
279                             DUPLICATE_UNDERSCORE_ARGUMENT,
280                             *correspondence,
281                             &format!(
282                                 "`{}` already exists, having another argument having almost the same \
283                                  name makes code comprehension and documentation more difficult",
284                                 arg_name[1..].to_owned()
285                             ),
286                         );;
287                     }
288                 } else {
289                     registered_names.insert(arg_name, arg.pat.span);
290                 }
291             }
292         }
293     }
294
295     fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
296         if in_external_macro(cx, expr.span) {
297             return;
298         }
299         match expr.node {
300             ExprKind::Call(ref paren, _) => if let ExprKind::Paren(ref closure) = paren.node {
301                 if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.node {
302                     span_lint_and_then(
303                         cx,
304                         REDUNDANT_CLOSURE_CALL,
305                         expr.span,
306                         "Try not to call a closure in the expression where it is declared.",
307                         |db| if decl.inputs.is_empty() {
308                             let hint = snippet(cx, block.span, "..").into_owned();
309                             db.span_suggestion(expr.span, "Try doing something like: ", hint);
310                         },
311                     );
312                 }
313             },
314             ExprKind::Unary(UnOp::Neg, ref inner) => if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
315                 span_lint(
316                     cx,
317                     DOUBLE_NEG,
318                     expr.span,
319                     "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
320                 );
321             },
322             ExprKind::Lit(ref lit) => self.check_lit(cx, lit),
323             _ => (),
324         }
325     }
326
327     fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
328         for w in block.stmts.windows(2) {
329             if_chain! {
330                 if let StmtKind::Local(ref local) = w[0].node;
331                 if let Option::Some(ref t) = local.init;
332                 if let ExprKind::Closure(..) = t.node;
333                 if let PatKind::Ident(_, ident, _) = local.pat.node;
334                 if let StmtKind::Semi(ref second) = w[1].node;
335                 if let ExprKind::Assign(_, ref call) = second.node;
336                 if let ExprKind::Call(ref closure, _) = call.node;
337                 if let ExprKind::Path(_, ref path) = closure.node;
338                 then {
339                     if ident == path.segments[0].ident {
340                         span_lint(
341                             cx,
342                             REDUNDANT_CLOSURE_CALL,
343                             second.span,
344                             "Closure called just once immediately after it was declared",
345                         );
346                     }
347                 }
348             }
349         }
350     }
351 }
352
353 impl MiscEarly {
354     fn check_lit(self, cx: &EarlyContext, lit: &Lit) {
355         if_chain! {
356             if let LitKind::Int(value, ..) = lit.node;
357             if let Some(src) = snippet_opt(cx, lit.span);
358             if let Some(firstch) = src.chars().next();
359             if char::to_digit(firstch, 10).is_some();
360             then {
361                 let mut prev = '\0';
362                 for ch in src.chars() {
363                     if ch == 'i' || ch == 'u' {
364                         if prev != '_' {
365                             span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
366                                         "integer type suffix should be separated by an underscore");
367                         }
368                         break;
369                     }
370                     prev = ch;
371                 }
372                 if src.starts_with("0x") {
373                     let mut seen = (false, false);
374                     for ch in src.chars() {
375                         match ch {
376                             'a' ..= 'f' => seen.0 = true,
377                             'A' ..= 'F' => seen.1 = true,
378                             'i' | 'u'   => break,   // start of suffix already
379                             _ => ()
380                         }
381                     }
382                     if seen.0 && seen.1 {
383                         span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
384                                     "inconsistent casing in hexadecimal literal");
385                     }
386                 } else if src.starts_with("0b") || src.starts_with("0o") {
387                     /* nothing to do */
388                 } else if value != 0 && src.starts_with('0') {
389                     span_lint_and_then(cx,
390                                         ZERO_PREFIXED_LITERAL,
391                                         lit.span,
392                                         "this is a decimal constant",
393                                         |db| {
394                         db.span_suggestion(
395                             lit.span,
396                             "if you mean to use a decimal constant, remove the `0` to remove confusion",
397                             src.trim_left_matches(|c| c == '_' || c == '0').to_string(),
398                         );
399                         db.span_suggestion(
400                             lit.span,
401                             "if you mean to use an octal constant, use `0o`",
402                             format!("0o{}", src.trim_left_matches(|c| c == '_' || c == '0')),
403                         );
404                     });
405                 }
406             }
407         }
408         if_chain! {
409             if let LitKind::Float(..) = lit.node;
410             if let Some(src) = snippet_opt(cx, lit.span);
411             if let Some(firstch) = src.chars().next();
412             if char::to_digit(firstch, 10).is_some();
413             then {
414                 let mut prev = '\0';
415                 for ch in src.chars() {
416                     if ch == 'f' {
417                         if prev != '_' {
418                             span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
419                                         "float type suffix should be separated by an underscore");
420                         }
421                         break;
422                     }
423                     prev = ch;
424                 }
425             }
426         }
427     }
428 }