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