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