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