]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/misc_early.rs
Rustup to rust-lang/rust#66878
[rust.git] / clippy_lints / src / misc_early.rs
1 use crate::utils::{
2     constants, snippet_opt, snippet_with_applicability, span_help_and_lint, span_lint, span_lint_and_sugg,
3     span_lint_and_then,
4 };
5 use if_chain::if_chain;
6 use rustc::declare_lint_pass;
7 use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
8 use rustc_data_structures::fx::FxHashMap;
9 use rustc_errors::Applicability;
10 use rustc_session::declare_tool_lint;
11 use syntax::ast::*;
12 use syntax::source_map::Span;
13 use syntax::visit::{walk_expr, FnKind, Visitor};
14
15 declare_clippy_lint! {
16     /// **What it does:** Checks for structure field patterns bound to wildcards.
17     ///
18     /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
19     /// the fields that are actually bound.
20     ///
21     /// **Known problems:** None.
22     ///
23     /// **Example:**
24     /// ```ignore
25     /// let { a: _, b: ref b, c: _ } = ..
26     /// ```
27     pub UNNEEDED_FIELD_PATTERN,
28     style,
29     "struct fields bound to a wildcard instead of using `..`"
30 }
31
32 declare_clippy_lint! {
33     /// **What it does:** Checks for function arguments having the similar names
34     /// differing by an underscore.
35     ///
36     /// **Why is this bad?** It affects code readability.
37     ///
38     /// **Known problems:** None.
39     ///
40     /// **Example:**
41     /// ```rust
42     /// fn foo(a: i32, _a: i32) {}
43     /// ```
44     pub DUPLICATE_UNDERSCORE_ARGUMENT,
45     style,
46     "function arguments having names which only differ by an underscore"
47 }
48
49 declare_clippy_lint! {
50     /// **What it does:** Detects closures called in the same expression where they
51     /// are defined.
52     ///
53     /// **Why is this bad?** It is unnecessarily adding to the expression's
54     /// complexity.
55     ///
56     /// **Known problems:** None.
57     ///
58     /// **Example:**
59     /// ```rust,ignore
60     /// (|| 42)()
61     /// ```
62     pub REDUNDANT_CLOSURE_CALL,
63     complexity,
64     "throwaway closures called in the expression they are defined"
65 }
66
67 declare_clippy_lint! {
68     /// **What it does:** Detects expressions of the form `--x`.
69     ///
70     /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
71     /// decremented.
72     ///
73     /// **Known problems:** None.
74     ///
75     /// **Example:**
76     /// ```rust
77     /// let mut x = 3;
78     /// --x;
79     /// ```
80     pub DOUBLE_NEG,
81     style,
82     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
83 }
84
85 declare_clippy_lint! {
86     /// **What it does:** Warns on hexadecimal literals with mixed-case letter
87     /// digits.
88     ///
89     /// **Why is this bad?** It looks confusing.
90     ///
91     /// **Known problems:** None.
92     ///
93     /// **Example:**
94     /// ```rust
95     /// let y = 0x1a9BAcD;
96     /// ```
97     pub MIXED_CASE_HEX_LITERALS,
98     style,
99     "hex literals whose letter digits are not consistently upper- or lowercased"
100 }
101
102 declare_clippy_lint! {
103     /// **What it does:** Warns if literal suffixes are not separated by an
104     /// underscore.
105     ///
106     /// **Why is this bad?** It is much less readable.
107     ///
108     /// **Known problems:** None.
109     ///
110     /// **Example:**
111     /// ```rust
112     /// let y = 123832i32;
113     /// ```
114     pub UNSEPARATED_LITERAL_SUFFIX,
115     pedantic,
116     "literals whose suffix is not separated by an underscore"
117 }
118
119 declare_clippy_lint! {
120     /// **What it does:** Warns if an integral constant literal starts with `0`.
121     ///
122     /// **Why is this bad?** In some languages (including the infamous C language
123     /// and most of its
124     /// family), this marks an octal constant. In Rust however, this is a decimal
125     /// constant. This could
126     /// be confusing for both the writer and a reader of the constant.
127     ///
128     /// **Known problems:** None.
129     ///
130     /// **Example:**
131     ///
132     /// In Rust:
133     /// ```rust
134     /// fn main() {
135     ///     let a = 0123;
136     ///     println!("{}", a);
137     /// }
138     /// ```
139     ///
140     /// prints `123`, while in C:
141     ///
142     /// ```c
143     /// #include <stdio.h>
144     ///
145     /// int main() {
146     ///     int a = 0123;
147     ///     printf("%d\n", a);
148     /// }
149     /// ```
150     ///
151     /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
152     pub ZERO_PREFIXED_LITERAL,
153     complexity,
154     "integer literals starting with `0`"
155 }
156
157 declare_clippy_lint! {
158     /// **What it does:** Warns if a generic shadows a built-in type.
159     ///
160     /// **Why is this bad?** This gives surprising type errors.
161     ///
162     /// **Known problems:** None.
163     ///
164     /// **Example:**
165     ///
166     /// ```ignore
167     /// impl<u32> Foo<u32> {
168     ///     fn impl_func(&self) -> u32 {
169     ///         42
170     ///     }
171     /// }
172     /// ```
173     pub BUILTIN_TYPE_SHADOW,
174     style,
175     "shadowing a builtin type"
176 }
177
178 declare_clippy_lint! {
179     /// **What it does:** Checks for patterns in the form `name @ _`.
180     ///
181     /// **Why is this bad?** It's almost always more readable to just use direct
182     /// bindings.
183     ///
184     /// **Known problems:** None.
185     ///
186     /// **Example:**
187     /// ```rust
188     /// # let v = Some("abc");
189     ///
190     /// match v {
191     ///     Some(x) => (),
192     ///     y @ _ => (), // easier written as `y`,
193     /// }
194     /// ```
195     pub REDUNDANT_PATTERN,
196     style,
197     "using `name @ _` in a pattern"
198 }
199
200 declare_clippy_lint! {
201     /// **What it does:** Checks for tuple patterns with a wildcard
202     /// pattern (`_`) is next to a rest pattern (`..`).
203     ///
204     /// _NOTE_: While `_, ..` means there is at least one element left, `..`
205     /// means there are 0 or more elements left. This can make a difference
206     /// when refactoring, but shouldn't result in errors in the refactored code,
207     /// since the wildcard pattern isn't used anyway.
208     /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
209     /// can match that element as well.
210     ///
211     /// **Known problems:** None.
212     ///
213     /// **Example:**
214     /// ```rust
215     /// # struct TupleStruct(u32, u32, u32);
216     /// # let t = TupleStruct(1, 2, 3);
217     ///
218     /// match t {
219     ///     TupleStruct(0, .., _) => (),
220     ///     _ => (),
221     /// }
222     /// ```
223     /// can be written as
224     /// ```rust
225     /// # struct TupleStruct(u32, u32, u32);
226     /// # let t = TupleStruct(1, 2, 3);
227     ///
228     /// match t {
229     ///     TupleStruct(0, ..) => (),
230     ///     _ => (),
231     /// }
232     /// ```
233     pub UNNEEDED_WILDCARD_PATTERN,
234     complexity,
235     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
236 }
237
238 declare_lint_pass!(MiscEarlyLints => [
239     UNNEEDED_FIELD_PATTERN,
240     DUPLICATE_UNDERSCORE_ARGUMENT,
241     REDUNDANT_CLOSURE_CALL,
242     DOUBLE_NEG,
243     MIXED_CASE_HEX_LITERALS,
244     UNSEPARATED_LITERAL_SUFFIX,
245     ZERO_PREFIXED_LITERAL,
246     BUILTIN_TYPE_SHADOW,
247     REDUNDANT_PATTERN,
248     UNNEEDED_WILDCARD_PATTERN,
249 ]);
250
251 // Used to find `return` statements or equivalents e.g., `?`
252 struct ReturnVisitor {
253     found_return: bool,
254 }
255
256 impl ReturnVisitor {
257     #[must_use]
258     fn new() -> Self {
259         Self { found_return: false }
260     }
261 }
262
263 impl<'ast> Visitor<'ast> for ReturnVisitor {
264     fn visit_expr(&mut self, ex: &'ast Expr) {
265         if let ExprKind::Ret(_) = ex.kind {
266             self.found_return = true;
267         } else if let ExprKind::Try(_) = ex.kind {
268             self.found_return = true;
269         }
270
271         walk_expr(self, ex)
272     }
273 }
274
275 impl EarlyLintPass for MiscEarlyLints {
276     fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
277         for param in &gen.params {
278             if let GenericParamKind::Type { .. } = param.kind {
279                 let name = param.ident.as_str();
280                 if constants::BUILTIN_TYPES.contains(&&*name) {
281                     span_lint(
282                         cx,
283                         BUILTIN_TYPE_SHADOW,
284                         param.ident.span,
285                         &format!("This generic shadows the built-in type `{}`", name),
286                     );
287                 }
288             }
289         }
290     }
291
292     fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
293         if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
294             let mut wilds = 0;
295             let type_name = npat
296                 .segments
297                 .last()
298                 .expect("A path must have at least one segment")
299                 .ident
300                 .name;
301
302             for field in pfields {
303                 if let PatKind::Wild = field.pat.kind {
304                     wilds += 1;
305                 }
306             }
307             if !pfields.is_empty() && wilds == pfields.len() {
308                 span_help_and_lint(
309                     cx,
310                     UNNEEDED_FIELD_PATTERN,
311                     pat.span,
312                     "All the struct fields are matched to a wildcard pattern, consider using `..`.",
313                     &format!("Try with `{} {{ .. }}` instead", type_name),
314                 );
315                 return;
316             }
317             if wilds > 0 {
318                 let mut normal = vec![];
319
320                 for field in pfields {
321                     match field.pat.kind {
322                         PatKind::Wild => {},
323                         _ => {
324                             if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
325                                 normal.push(n);
326                             }
327                         },
328                     }
329                 }
330                 for field in pfields {
331                     if let PatKind::Wild = field.pat.kind {
332                         wilds -= 1;
333                         if wilds > 0 {
334                             span_lint(
335                                 cx,
336                                 UNNEEDED_FIELD_PATTERN,
337                                 field.span,
338                                 "You matched a field with a wildcard pattern. Consider using `..` instead",
339                             );
340                         } else {
341                             span_help_and_lint(
342                                 cx,
343                                 UNNEEDED_FIELD_PATTERN,
344                                 field.span,
345                                 "You matched a field with a wildcard pattern. Consider using `..` \
346                                  instead",
347                                 &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
348                             );
349                         }
350                     }
351                 }
352             }
353         }
354
355         if let PatKind::Ident(_, ident, Some(ref right)) = pat.kind {
356             if let PatKind::Wild = right.kind {
357                 span_lint_and_sugg(
358                     cx,
359                     REDUNDANT_PATTERN,
360                     pat.span,
361                     &format!(
362                         "the `{} @ _` pattern can be written as just `{}`",
363                         ident.name, ident.name,
364                     ),
365                     "try",
366                     format!("{}", ident.name),
367                     Applicability::MachineApplicable,
368                 );
369             }
370         }
371
372         check_unneeded_wildcard_pattern(cx, pat);
373     }
374
375     fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
376         let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
377
378         for arg in &decl.inputs {
379             if let PatKind::Ident(_, ident, None) = arg.pat.kind {
380                 let arg_name = ident.to_string();
381
382                 if arg_name.starts_with('_') {
383                     if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
384                         span_lint(
385                             cx,
386                             DUPLICATE_UNDERSCORE_ARGUMENT,
387                             *correspondence,
388                             &format!(
389                                 "`{}` already exists, having another argument having almost the same \
390                                  name makes code comprehension and documentation more difficult",
391                                 arg_name[1..].to_owned()
392                             ),
393                         );
394                     }
395                 } else {
396                     registered_names.insert(arg_name, arg.pat.span);
397                 }
398             }
399         }
400     }
401
402     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
403         if in_external_macro(cx.sess(), expr.span) {
404             return;
405         }
406         match expr.kind {
407             ExprKind::Call(ref paren, _) => {
408                 if let ExprKind::Paren(ref closure) = paren.kind {
409                     if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind {
410                         let mut visitor = ReturnVisitor::new();
411                         visitor.visit_expr(block);
412                         if !visitor.found_return {
413                             span_lint_and_then(
414                                 cx,
415                                 REDUNDANT_CLOSURE_CALL,
416                                 expr.span,
417                                 "Try not to call a closure in the expression where it is declared.",
418                                 |db| {
419                                     if decl.inputs.is_empty() {
420                                         let mut app = Applicability::MachineApplicable;
421                                         let hint =
422                                             snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
423                                         db.span_suggestion(expr.span, "Try doing something like: ", hint, app);
424                                     }
425                                 },
426                             );
427                         }
428                     }
429                 }
430             },
431             ExprKind::Unary(UnOp::Neg, ref inner) => {
432                 if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
433                     span_lint(
434                         cx,
435                         DOUBLE_NEG,
436                         expr.span,
437                         "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
438                     );
439                 }
440             },
441             ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
442             _ => (),
443         }
444     }
445
446     fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
447         for w in block.stmts.windows(2) {
448             if_chain! {
449                 if let StmtKind::Local(ref local) = w[0].kind;
450                 if let Option::Some(ref t) = local.init;
451                 if let ExprKind::Closure(..) = t.kind;
452                 if let PatKind::Ident(_, ident, _) = local.pat.kind;
453                 if let StmtKind::Semi(ref second) = w[1].kind;
454                 if let ExprKind::Assign(_, ref call) = second.kind;
455                 if let ExprKind::Call(ref closure, _) = call.kind;
456                 if let ExprKind::Path(_, ref path) = closure.kind;
457                 then {
458                     if ident == path.segments[0].ident {
459                         span_lint(
460                             cx,
461                             REDUNDANT_CLOSURE_CALL,
462                             second.span,
463                             "Closure called just once immediately after it was declared",
464                         );
465                     }
466                 }
467             }
468         }
469     }
470 }
471
472 impl MiscEarlyLints {
473     fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
474         // We test if first character in snippet is a number, because the snippet could be an expansion
475         // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
476         // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
477         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
478         // FIXME: Find a better way to detect those cases.
479         let lit_snip = match snippet_opt(cx, lit.span) {
480             Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
481             _ => return,
482         };
483
484         if let LitKind::Int(value, lit_int_type) = lit.kind {
485             let suffix = match lit_int_type {
486                 LitIntType::Signed(ty) => ty.name_str(),
487                 LitIntType::Unsigned(ty) => ty.name_str(),
488                 LitIntType::Unsuffixed => "",
489             };
490
491             let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
492             // Do not lint when literal is unsuffixed.
493             if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
494                 span_lint_and_sugg(
495                     cx,
496                     UNSEPARATED_LITERAL_SUFFIX,
497                     lit.span,
498                     "integer type suffix should be separated by an underscore",
499                     "add an underscore",
500                     format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
501                     Applicability::MachineApplicable,
502                 );
503             }
504
505             if lit_snip.starts_with("0x") {
506                 let mut seen = (false, false);
507                 for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
508                     match ch {
509                         b'a'..=b'f' => seen.0 = true,
510                         b'A'..=b'F' => seen.1 = true,
511                         _ => {},
512                     }
513                     if seen.0 && seen.1 {
514                         span_lint(
515                             cx,
516                             MIXED_CASE_HEX_LITERALS,
517                             lit.span,
518                             "inconsistent casing in hexadecimal literal",
519                         );
520                         break;
521                     }
522                 }
523             } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
524                 /* nothing to do */
525             } else if value != 0 && lit_snip.starts_with('0') {
526                 span_lint_and_then(
527                     cx,
528                     ZERO_PREFIXED_LITERAL,
529                     lit.span,
530                     "this is a decimal constant",
531                     |db| {
532                         db.span_suggestion(
533                             lit.span,
534                             "if you mean to use a decimal constant, remove the `0` to avoid confusion",
535                             lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
536                             Applicability::MaybeIncorrect,
537                         );
538                         db.span_suggestion(
539                             lit.span,
540                             "if you mean to use an octal constant, use `0o`",
541                             format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
542                             Applicability::MaybeIncorrect,
543                         );
544                     },
545                 );
546             }
547         } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
548             let suffix = float_ty.name_str();
549             let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
550             if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
551                 span_lint_and_sugg(
552                     cx,
553                     UNSEPARATED_LITERAL_SUFFIX,
554                     lit.span,
555                     "float type suffix should be separated by an underscore",
556                     "add an underscore",
557                     format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
558                     Applicability::MachineApplicable,
559                 );
560             }
561         }
562     }
563 }
564
565 fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
566     if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
567         fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
568             span_lint_and_sugg(
569                 cx,
570                 UNNEEDED_WILDCARD_PATTERN,
571                 span,
572                 if only_one {
573                     "this pattern is unneeded as the `..` pattern can match that element"
574                 } else {
575                     "these patterns are unneeded as the `..` pattern can match those elements"
576                 },
577                 if only_one { "remove it" } else { "remove them" },
578                 "".to_string(),
579                 Applicability::MachineApplicable,
580             );
581         }
582
583         #[allow(clippy::trivially_copy_pass_by_ref)]
584         fn is_wild<P: std::ops::Deref<Target = Pat>>(pat: &&P) -> bool {
585             if let PatKind::Wild = pat.kind {
586                 true
587             } else {
588                 false
589             }
590         }
591
592         if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
593             if let Some((left_index, left_pat)) = patterns[..rest_index]
594                 .iter()
595                 .rev()
596                 .take_while(is_wild)
597                 .enumerate()
598                 .last()
599             {
600                 span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
601             }
602
603             if let Some((right_index, right_pat)) =
604                 patterns[rest_index + 1..].iter().take_while(is_wild).enumerate().last()
605             {
606                 span_lint(
607                     cx,
608                     patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
609                     right_index == 0,
610                 );
611             }
612         }
613     }
614 }