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