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