]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc_early.rs
Auto merge of #3845 - euclio:unused-comments, 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_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 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 #[derive(Copy, Clone)]
176 pub struct MiscEarly;
177
178 impl LintPass for MiscEarly {
179     fn get_lints(&self) -> LintArray {
180         lint_array!(
181             UNNEEDED_FIELD_PATTERN,
182             DUPLICATE_UNDERSCORE_ARGUMENT,
183             REDUNDANT_CLOSURE_CALL,
184             DOUBLE_NEG,
185             MIXED_CASE_HEX_LITERALS,
186             UNSEPARATED_LITERAL_SUFFIX,
187             ZERO_PREFIXED_LITERAL,
188             BUILTIN_TYPE_SHADOW
189         )
190     }
191
192     fn name(&self) -> &'static str {
193         "MiscEarlyLints"
194     }
195 }
196
197 // Used to find `return` statements or equivalents e.g. `?`
198 struct ReturnVisitor {
199     found_return: bool,
200 }
201
202 impl ReturnVisitor {
203     fn new() -> Self {
204         Self { found_return: false }
205     }
206 }
207
208 impl<'ast> Visitor<'ast> for ReturnVisitor {
209     fn visit_expr(&mut self, ex: &'ast Expr) {
210         if let ExprKind::Ret(_) = ex.node {
211             self.found_return = true;
212         } else if let ExprKind::Try(_) = ex.node {
213             self.found_return = true;
214         }
215
216         walk_expr(self, ex)
217     }
218 }
219
220 impl EarlyLintPass for MiscEarly {
221     fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
222         for param in &gen.params {
223             if let GenericParamKind::Type { .. } = param.kind {
224                 let name = param.ident.as_str();
225                 if constants::BUILTIN_TYPES.contains(&&*name) {
226                     span_lint(
227                         cx,
228                         BUILTIN_TYPE_SHADOW,
229                         param.ident.span,
230                         &format!("This generic shadows the built-in type `{}`", name),
231                     );
232                 }
233             }
234         }
235     }
236
237     fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat, _: &mut bool) {
238         if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
239             let mut wilds = 0;
240             let type_name = npat
241                 .segments
242                 .last()
243                 .expect("A path must have at least one segment")
244                 .ident
245                 .name;
246
247             for field in pfields {
248                 if let PatKind::Wild = field.node.pat.node {
249                     wilds += 1;
250                 }
251             }
252             if !pfields.is_empty() && wilds == pfields.len() {
253                 span_help_and_lint(
254                     cx,
255                     UNNEEDED_FIELD_PATTERN,
256                     pat.span,
257                     "All the struct fields are matched to a wildcard pattern, consider using `..`.",
258                     &format!("Try with `{} {{ .. }}` instead", type_name),
259                 );
260                 return;
261             }
262             if wilds > 0 {
263                 let mut normal = vec![];
264
265                 for field in pfields {
266                     match field.node.pat.node {
267                         PatKind::Wild => {},
268                         _ => {
269                             if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
270                                 normal.push(n);
271                             }
272                         },
273                     }
274                 }
275                 for field in pfields {
276                     if let PatKind::Wild = field.node.pat.node {
277                         wilds -= 1;
278                         if wilds > 0 {
279                             span_lint(
280                                 cx,
281                                 UNNEEDED_FIELD_PATTERN,
282                                 field.span,
283                                 "You matched a field with a wildcard pattern. Consider using `..` instead",
284                             );
285                         } else {
286                             span_help_and_lint(
287                                 cx,
288                                 UNNEEDED_FIELD_PATTERN,
289                                 field.span,
290                                 "You matched a field with a wildcard pattern. Consider using `..` \
291                                  instead",
292                                 &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
293                             );
294                         }
295                     }
296                 }
297             }
298         }
299     }
300
301     fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
302         let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
303
304         for arg in &decl.inputs {
305             if let PatKind::Ident(_, ident, None) = arg.pat.node {
306                 let arg_name = ident.to_string();
307
308                 if arg_name.starts_with('_') {
309                     if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
310                         span_lint(
311                             cx,
312                             DUPLICATE_UNDERSCORE_ARGUMENT,
313                             *correspondence,
314                             &format!(
315                                 "`{}` already exists, having another argument having almost the same \
316                                  name makes code comprehension and documentation more difficult",
317                                 arg_name[1..].to_owned()
318                             ),
319                         );;
320                     }
321                 } else {
322                     registered_names.insert(arg_name, arg.pat.span);
323                 }
324             }
325         }
326     }
327
328     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
329         if in_external_macro(cx.sess(), expr.span) {
330             return;
331         }
332         match expr.node {
333             ExprKind::Call(ref paren, _) => {
334                 if let ExprKind::Paren(ref closure) = paren.node {
335                     if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.node {
336                         let mut visitor = ReturnVisitor::new();
337                         visitor.visit_expr(block);
338                         if !visitor.found_return {
339                             span_lint_and_then(
340                                 cx,
341                                 REDUNDANT_CLOSURE_CALL,
342                                 expr.span,
343                                 "Try not to call a closure in the expression where it is declared.",
344                                 |db| {
345                                     if decl.inputs.is_empty() {
346                                         let hint = snippet(cx, block.span, "..").into_owned();
347                                         db.span_suggestion(
348                                             expr.span,
349                                             "Try doing something like: ",
350                                             hint,
351                                             Applicability::MachineApplicable, // snippet
352                                         );
353                                     }
354                                 },
355                             );
356                         }
357                     }
358                 }
359             },
360             ExprKind::Unary(UnOp::Neg, ref inner) => {
361                 if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
362                     span_lint(
363                         cx,
364                         DOUBLE_NEG,
365                         expr.span,
366                         "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
367                     );
368                 }
369             },
370             ExprKind::Lit(ref lit) => self.check_lit(cx, lit),
371             _ => (),
372         }
373     }
374
375     fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
376         for w in block.stmts.windows(2) {
377             if_chain! {
378                 if let StmtKind::Local(ref local) = w[0].node;
379                 if let Option::Some(ref t) = local.init;
380                 if let ExprKind::Closure(..) = t.node;
381                 if let PatKind::Ident(_, ident, _) = local.pat.node;
382                 if let StmtKind::Semi(ref second) = w[1].node;
383                 if let ExprKind::Assign(_, ref call) = second.node;
384                 if let ExprKind::Call(ref closure, _) = call.node;
385                 if let ExprKind::Path(_, ref path) = closure.node;
386                 then {
387                     if ident == path.segments[0].ident {
388                         span_lint(
389                             cx,
390                             REDUNDANT_CLOSURE_CALL,
391                             second.span,
392                             "Closure called just once immediately after it was declared",
393                         );
394                     }
395                 }
396             }
397         }
398     }
399 }
400
401 impl MiscEarly {
402     fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
403         if_chain! {
404             if let LitKind::Int(value, ..) = lit.node;
405             if let Some(src) = snippet_opt(cx, lit.span);
406             if let Some(firstch) = src.chars().next();
407             if char::to_digit(firstch, 10).is_some();
408             then {
409                 let mut prev = '\0';
410                 for ch in src.chars() {
411                     if ch == 'i' || ch == 'u' {
412                         if prev != '_' {
413                             span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
414                                         "integer type suffix should be separated by an underscore");
415                         }
416                         break;
417                     }
418                     prev = ch;
419                 }
420                 if src.starts_with("0x") {
421                     let mut seen = (false, false);
422                     for ch in src.chars() {
423                         match ch {
424                             'a' ..= 'f' => seen.0 = true,
425                             'A' ..= 'F' => seen.1 = true,
426                             'i' | 'u'   => break,   // start of suffix already
427                             _ => ()
428                         }
429                     }
430                     if seen.0 && seen.1 {
431                         span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
432                                     "inconsistent casing in hexadecimal literal");
433                     }
434                 } else if src.starts_with("0b") || src.starts_with("0o") {
435                     /* nothing to do */
436                 } else if value != 0 && src.starts_with('0') {
437                     span_lint_and_then(cx,
438                                         ZERO_PREFIXED_LITERAL,
439                                         lit.span,
440                                         "this is a decimal constant",
441                                         |db| {
442                         db.span_suggestion(
443                             lit.span,
444                             "if you mean to use a decimal constant, remove the `0` to remove confusion",
445                             src.trim_start_matches(|c| c == '_' || c == '0').to_string(),
446                             Applicability::MaybeIncorrect,
447                         );
448                         db.span_suggestion(
449                             lit.span,
450                             "if you mean to use an octal constant, use `0o`",
451                             format!("0o{}", src.trim_start_matches(|c| c == '_' || c == '0')),
452                             Applicability::MaybeIncorrect,
453                         );
454                     });
455                 }
456             }
457         }
458         if_chain! {
459             if let LitKind::Float(..) = lit.node;
460             if let Some(src) = snippet_opt(cx, lit.span);
461             if let Some(firstch) = src.chars().next();
462             if char::to_digit(firstch, 10).is_some();
463             then {
464                 let mut prev = '\0';
465                 for ch in src.chars() {
466                     if ch == 'f' {
467                         if prev != '_' {
468                             span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
469                                         "float type suffix should be separated by an underscore");
470                         }
471                         break;
472                     }
473                     prev = ch;
474                 }
475             }
476         }
477     }
478 }