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