]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/misc_early.rs
Auto merge of #75014 - Amanieu:fix_asm_reg, r=nagisa
[rust.git] / src / tools / clippy / 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     ///
235     /// // Bad
236     /// match t {
237     ///     TupleStruct(0, .., _) => (),
238     ///     _ => (),
239     /// }
240     ///
241     /// // Good
242     /// match t {
243     ///     TupleStruct(0, ..) => (),
244     ///     _ => (),
245     /// }
246     /// ```
247     pub UNNEEDED_WILDCARD_PATTERN,
248     complexity,
249     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
250 }
251
252 declare_lint_pass!(MiscEarlyLints => [
253     UNNEEDED_FIELD_PATTERN,
254     DUPLICATE_UNDERSCORE_ARGUMENT,
255     DOUBLE_NEG,
256     MIXED_CASE_HEX_LITERALS,
257     UNSEPARATED_LITERAL_SUFFIX,
258     ZERO_PREFIXED_LITERAL,
259     BUILTIN_TYPE_SHADOW,
260     REDUNDANT_PATTERN,
261     UNNEEDED_WILDCARD_PATTERN,
262 ]);
263
264 impl EarlyLintPass for MiscEarlyLints {
265     fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
266         for param in &gen.params {
267             if let GenericParamKind::Type { .. } = param.kind {
268                 let name = param.ident.as_str();
269                 if constants::BUILTIN_TYPES.contains(&&*name) {
270                     span_lint(
271                         cx,
272                         BUILTIN_TYPE_SHADOW,
273                         param.ident.span,
274                         &format!("this generic shadows the built-in type `{}`", name),
275                     );
276                 }
277             }
278         }
279     }
280
281     fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
282         if let PatKind::Struct(ref npat, ref pfields, _) = pat.kind {
283             let mut wilds = 0;
284             let type_name = npat
285                 .segments
286                 .last()
287                 .expect("A path must have at least one segment")
288                 .ident
289                 .name;
290
291             for field in pfields {
292                 if let PatKind::Wild = field.pat.kind {
293                     wilds += 1;
294                 }
295             }
296             if !pfields.is_empty() && wilds == pfields.len() {
297                 span_lint_and_help(
298                     cx,
299                     UNNEEDED_FIELD_PATTERN,
300                     pat.span,
301                     "all the struct fields are matched to a wildcard pattern, consider using `..`",
302                     None,
303                     &format!("try with `{} {{ .. }}` instead", type_name),
304                 );
305                 return;
306             }
307             if wilds > 0 {
308                 for field in pfields {
309                     if let PatKind::Wild = field.pat.kind {
310                         wilds -= 1;
311                         if wilds > 0 {
312                             span_lint(
313                                 cx,
314                                 UNNEEDED_FIELD_PATTERN,
315                                 field.span,
316                                 "you matched a field with a wildcard pattern, consider using `..` instead",
317                             );
318                         } else {
319                             let mut normal = vec![];
320
321                             for field in pfields {
322                                 match field.pat.kind {
323                                     PatKind::Wild => {},
324                                     _ => {
325                                         if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
326                                             normal.push(n);
327                                         }
328                                     },
329                                 }
330                             }
331
332                             span_lint_and_help(
333                                 cx,
334                                 UNNEEDED_FIELD_PATTERN,
335                                 field.span,
336                                 "you matched a field with a wildcard pattern, consider using `..` \
337                                  instead",
338                                 None,
339                                 &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
340                             );
341                         }
342                     }
343                 }
344             }
345         }
346
347         if let PatKind::Ident(left, ident, Some(ref right)) = pat.kind {
348             let left_binding = match left {
349                 BindingMode::ByRef(Mutability::Mut) => "ref mut ",
350                 BindingMode::ByRef(Mutability::Not) => "ref ",
351                 BindingMode::ByValue(..) => "",
352             };
353
354             if let PatKind::Wild = right.kind {
355                 span_lint_and_sugg(
356                     cx,
357                     REDUNDANT_PATTERN,
358                     pat.span,
359                     &format!(
360                         "the `{} @ _` pattern can be written as just `{}`",
361                         ident.name, ident.name,
362                     ),
363                     "try",
364                     format!("{}{}", left_binding, ident.name),
365                     Applicability::MachineApplicable,
366                 );
367             }
368         }
369
370         check_unneeded_wildcard_pattern(cx, pat);
371     }
372
373     fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
374         let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
375
376         for arg in &fn_kind.decl().inputs {
377             if let PatKind::Ident(_, ident, None) = arg.pat.kind {
378                 let arg_name = ident.to_string();
379
380                 if arg_name.starts_with('_') {
381                     if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
382                         span_lint(
383                             cx,
384                             DUPLICATE_UNDERSCORE_ARGUMENT,
385                             *correspondence,
386                             &format!(
387                                 "`{}` already exists, having another argument having almost the same \
388                                  name makes code comprehension and documentation more difficult",
389                                 arg_name[1..].to_owned()
390                             ),
391                         );
392                     }
393                 } else {
394                     registered_names.insert(arg_name, arg.pat.span);
395                 }
396             }
397         }
398     }
399
400     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
401         if in_external_macro(cx.sess(), expr.span) {
402             return;
403         }
404         match expr.kind {
405             ExprKind::Unary(UnOp::Neg, ref inner) => {
406                 if let ExprKind::Unary(UnOp::Neg, _) = inner.kind {
407                     span_lint(
408                         cx,
409                         DOUBLE_NEG,
410                         expr.span,
411                         "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
412                     );
413                 }
414             },
415             ExprKind::Lit(ref lit) => Self::check_lit(cx, lit),
416             _ => (),
417         }
418     }
419 }
420
421 impl MiscEarlyLints {
422     fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
423         // We test if first character in snippet is a number, because the snippet could be an expansion
424         // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
425         // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
426         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
427         // FIXME: Find a better way to detect those cases.
428         let lit_snip = match snippet_opt(cx, lit.span) {
429             Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
430             _ => return,
431         };
432
433         if let LitKind::Int(value, lit_int_type) = lit.kind {
434             let suffix = match lit_int_type {
435                 LitIntType::Signed(ty) => ty.name_str(),
436                 LitIntType::Unsigned(ty) => ty.name_str(),
437                 LitIntType::Unsuffixed => "",
438             };
439
440             let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
441                 val
442             } else {
443                 return; // It's useless so shouldn't lint.
444             };
445             // Do not lint when literal is unsuffixed.
446             if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
447                 span_lint_and_sugg(
448                     cx,
449                     UNSEPARATED_LITERAL_SUFFIX,
450                     lit.span,
451                     "integer type suffix should be separated by an underscore",
452                     "add an underscore",
453                     format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
454                     Applicability::MachineApplicable,
455                 );
456             }
457
458             if lit_snip.starts_with("0x") {
459                 if maybe_last_sep_idx <= 2 {
460                     // It's meaningless or causes range error.
461                     return;
462                 }
463                 let mut seen = (false, false);
464                 for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
465                     match ch {
466                         b'a'..=b'f' => seen.0 = true,
467                         b'A'..=b'F' => seen.1 = true,
468                         _ => {},
469                     }
470                     if seen.0 && seen.1 {
471                         span_lint(
472                             cx,
473                             MIXED_CASE_HEX_LITERALS,
474                             lit.span,
475                             "inconsistent casing in hexadecimal literal",
476                         );
477                         break;
478                     }
479                 }
480             } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
481                 /* nothing to do */
482             } else if value != 0 && lit_snip.starts_with('0') {
483                 span_lint_and_then(
484                     cx,
485                     ZERO_PREFIXED_LITERAL,
486                     lit.span,
487                     "this is a decimal constant",
488                     |diag| {
489                         diag.span_suggestion(
490                             lit.span,
491                             "if you mean to use a decimal constant, remove the `0` to avoid confusion",
492                             lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
493                             Applicability::MaybeIncorrect,
494                         );
495                         diag.span_suggestion(
496                             lit.span,
497                             "if you mean to use an octal constant, use `0o`",
498                             format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
499                             Applicability::MaybeIncorrect,
500                         );
501                     },
502                 );
503             }
504         } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind {
505             let suffix = float_ty.name_str();
506             let maybe_last_sep_idx = if let Some(val) = lit_snip.len().checked_sub(suffix.len() + 1) {
507                 val
508             } else {
509                 return; // It's useless so shouldn't lint.
510             };
511             if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
512                 span_lint_and_sugg(
513                     cx,
514                     UNSEPARATED_LITERAL_SUFFIX,
515                     lit.span,
516                     "float type suffix should be separated by an underscore",
517                     "add an underscore",
518                     format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
519                     Applicability::MachineApplicable,
520                 );
521             }
522         }
523     }
524 }
525
526 fn check_unneeded_wildcard_pattern(cx: &EarlyContext<'_>, pat: &Pat) {
527     if let PatKind::TupleStruct(_, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
528         fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
529             span_lint_and_sugg(
530                 cx,
531                 UNNEEDED_WILDCARD_PATTERN,
532                 span,
533                 if only_one {
534                     "this pattern is unneeded as the `..` pattern can match that element"
535                 } else {
536                     "these patterns are unneeded as the `..` pattern can match those elements"
537                 },
538                 if only_one { "remove it" } else { "remove them" },
539                 "".to_string(),
540                 Applicability::MachineApplicable,
541             );
542         }
543
544         if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
545             if let Some((left_index, left_pat)) = patterns[..rest_index]
546                 .iter()
547                 .rev()
548                 .take_while(|pat| matches!(pat.kind, PatKind::Wild))
549                 .enumerate()
550                 .last()
551             {
552                 span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
553             }
554
555             if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
556                 .iter()
557                 .take_while(|pat| matches!(pat.kind, PatKind::Wild))
558                 .enumerate()
559                 .last()
560             {
561                 span_lint(
562                     cx,
563                     patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
564                     right_index == 0,
565                 );
566             }
567         }
568     }
569 }