• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 mod builtin_type_shadow;
2 mod double_neg;
3 mod literal_suffix;
4 mod mixed_case_hex_literals;
5 mod redundant_at_rest_pattern;
6 mod redundant_pattern;
7 mod unneeded_field_pattern;
8 mod unneeded_wildcard_pattern;
9 mod zero_prefixed_literal;
10 
11 use clippy_utils::diagnostics::span_lint;
12 use clippy_utils::source::snippet_opt;
13 use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
14 use rustc_ast::token;
15 use rustc_ast::visit::FnKind;
16 use rustc_data_structures::fx::FxHashMap;
17 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
18 use rustc_middle::lint::in_external_macro;
19 use rustc_session::{declare_lint_pass, declare_tool_lint};
20 use rustc_span::source_map::Span;
21 
22 declare_clippy_lint! {
23     /// ### What it does
24     /// Checks for structure field patterns bound to wildcards.
25     ///
26     /// ### Why is this bad?
27     /// Using `..` instead is shorter and leaves the focus on
28     /// the fields that are actually bound.
29     ///
30     /// ### Example
31     /// ```rust
32     /// # struct Foo {
33     /// #     a: i32,
34     /// #     b: i32,
35     /// #     c: i32,
36     /// # }
37     /// let f = Foo { a: 0, b: 0, c: 0 };
38     ///
39     /// match f {
40     ///     Foo { a: _, b: 0, .. } => {},
41     ///     Foo { a: _, b: _, c: _ } => {},
42     /// }
43     /// ```
44     ///
45     /// Use instead:
46     /// ```rust
47     /// # struct Foo {
48     /// #     a: i32,
49     /// #     b: i32,
50     /// #     c: i32,
51     /// # }
52     /// let f = Foo { a: 0, b: 0, c: 0 };
53     ///
54     /// match f {
55     ///     Foo { b: 0, .. } => {},
56     ///     Foo { .. } => {},
57     /// }
58     /// ```
59     #[clippy::version = "pre 1.29.0"]
60     pub UNNEEDED_FIELD_PATTERN,
61     restriction,
62     "struct fields bound to a wildcard instead of using `..`"
63 }
64 
65 declare_clippy_lint! {
66     /// ### What it does
67     /// Checks for function arguments having the similar names
68     /// differing by an underscore.
69     ///
70     /// ### Why is this bad?
71     /// It affects code readability.
72     ///
73     /// ### Example
74     /// ```rust
75     /// fn foo(a: i32, _a: i32) {}
76     /// ```
77     ///
78     /// Use instead:
79     /// ```rust
80     /// fn bar(a: i32, _b: i32) {}
81     /// ```
82     #[clippy::version = "pre 1.29.0"]
83     pub DUPLICATE_UNDERSCORE_ARGUMENT,
84     style,
85     "function arguments having names which only differ by an underscore"
86 }
87 
88 declare_clippy_lint! {
89     /// ### What it does
90     /// Detects expressions of the form `--x`.
91     ///
92     /// ### Why is this bad?
93     /// It can mislead C/C++ programmers to think `x` was
94     /// decremented.
95     ///
96     /// ### Example
97     /// ```rust
98     /// let mut x = 3;
99     /// --x;
100     /// ```
101     #[clippy::version = "pre 1.29.0"]
102     pub DOUBLE_NEG,
103     style,
104     "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
105 }
106 
107 declare_clippy_lint! {
108     /// ### What it does
109     /// Warns on hexadecimal literals with mixed-case letter
110     /// digits.
111     ///
112     /// ### Why is this bad?
113     /// It looks confusing.
114     ///
115     /// ### Example
116     /// ```rust
117     /// # let _ =
118     /// 0x1a9BAcD
119     /// # ;
120     /// ```
121     ///
122     /// Use instead:
123     /// ```rust
124     /// # let _ =
125     /// 0x1A9BACD
126     /// # ;
127     /// ```
128     #[clippy::version = "pre 1.29.0"]
129     pub MIXED_CASE_HEX_LITERALS,
130     style,
131     "hex literals whose letter digits are not consistently upper- or lowercased"
132 }
133 
134 declare_clippy_lint! {
135     /// ### What it does
136     /// Warns if literal suffixes are not separated by an
137     /// underscore.
138     /// To enforce unseparated literal suffix style,
139     /// see the `separated_literal_suffix` lint.
140     ///
141     /// ### Why is this bad?
142     /// Suffix style should be consistent.
143     ///
144     /// ### Example
145     /// ```rust
146     /// # let _ =
147     /// 123832i32
148     /// # ;
149     /// ```
150     ///
151     /// Use instead:
152     /// ```rust
153     /// # let _ =
154     /// 123832_i32
155     /// # ;
156     /// ```
157     #[clippy::version = "pre 1.29.0"]
158     pub UNSEPARATED_LITERAL_SUFFIX,
159     restriction,
160     "literals whose suffix is not separated by an underscore"
161 }
162 
163 declare_clippy_lint! {
164     /// ### What it does
165     /// Warns if literal suffixes are separated by an underscore.
166     /// To enforce separated literal suffix style,
167     /// see the `unseparated_literal_suffix` lint.
168     ///
169     /// ### Why is this bad?
170     /// Suffix style should be consistent.
171     ///
172     /// ### Example
173     /// ```rust
174     /// # let _ =
175     /// 123832_i32
176     /// # ;
177     /// ```
178     ///
179     /// Use instead:
180     /// ```rust
181     /// # let _ =
182     /// 123832i32
183     /// # ;
184     /// ```
185     #[clippy::version = "1.58.0"]
186     pub SEPARATED_LITERAL_SUFFIX,
187     restriction,
188     "literals whose suffix is separated by an underscore"
189 }
190 
191 declare_clippy_lint! {
192     /// ### What it does
193     /// Warns if an integral constant literal starts with `0`.
194     ///
195     /// ### Why is this bad?
196     /// In some languages (including the infamous C language
197     /// and most of its
198     /// family), this marks an octal constant. In Rust however, this is a decimal
199     /// constant. This could
200     /// be confusing for both the writer and a reader of the constant.
201     ///
202     /// ### Example
203     ///
204     /// In Rust:
205     /// ```rust
206     /// fn main() {
207     ///     let a = 0123;
208     ///     println!("{}", a);
209     /// }
210     /// ```
211     ///
212     /// prints `123`, while in C:
213     ///
214     /// ```c
215     /// #include <stdio.h>
216     ///
217     /// int main() {
218     ///     int a = 0123;
219     ///     printf("%d\n", a);
220     /// }
221     /// ```
222     ///
223     /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
224     #[clippy::version = "pre 1.29.0"]
225     pub ZERO_PREFIXED_LITERAL,
226     complexity,
227     "integer literals starting with `0`"
228 }
229 
230 declare_clippy_lint! {
231     /// ### What it does
232     /// Warns if a generic shadows a built-in type.
233     ///
234     /// ### Why is this bad?
235     /// This gives surprising type errors.
236     ///
237     /// ### Example
238     ///
239     /// ```ignore
240     /// impl<u32> Foo<u32> {
241     ///     fn impl_func(&self) -> u32 {
242     ///         42
243     ///     }
244     /// }
245     /// ```
246     #[clippy::version = "pre 1.29.0"]
247     pub BUILTIN_TYPE_SHADOW,
248     style,
249     "shadowing a builtin type"
250 }
251 
252 declare_clippy_lint! {
253     /// ### What it does
254     /// Checks for patterns in the form `name @ _`.
255     ///
256     /// ### Why is this bad?
257     /// It's almost always more readable to just use direct
258     /// bindings.
259     ///
260     /// ### Example
261     /// ```rust
262     /// # let v = Some("abc");
263     /// match v {
264     ///     Some(x) => (),
265     ///     y @ _ => (),
266     /// }
267     /// ```
268     ///
269     /// Use instead:
270     /// ```rust
271     /// # let v = Some("abc");
272     /// match v {
273     ///     Some(x) => (),
274     ///     y => (),
275     /// }
276     /// ```
277     #[clippy::version = "pre 1.29.0"]
278     pub REDUNDANT_PATTERN,
279     style,
280     "using `name @ _` in a pattern"
281 }
282 
283 declare_clippy_lint! {
284     /// ### What it does
285     /// Checks for tuple patterns with a wildcard
286     /// pattern (`_`) is next to a rest pattern (`..`).
287     ///
288     /// _NOTE_: While `_, ..` means there is at least one element left, `..`
289     /// means there are 0 or more elements left. This can make a difference
290     /// when refactoring, but shouldn't result in errors in the refactored code,
291     /// since the wildcard pattern isn't used anyway.
292     ///
293     /// ### Why is this bad?
294     /// The wildcard pattern is unneeded as the rest pattern
295     /// can match that element as well.
296     ///
297     /// ### Example
298     /// ```rust
299     /// # struct TupleStruct(u32, u32, u32);
300     /// # let t = TupleStruct(1, 2, 3);
301     /// match t {
302     ///     TupleStruct(0, .., _) => (),
303     ///     _ => (),
304     /// }
305     /// ```
306     ///
307     /// Use instead:
308     /// ```rust
309     /// # struct TupleStruct(u32, u32, u32);
310     /// # let t = TupleStruct(1, 2, 3);
311     /// match t {
312     ///     TupleStruct(0, ..) => (),
313     ///     _ => (),
314     /// }
315     /// ```
316     #[clippy::version = "1.40.0"]
317     pub UNNEEDED_WILDCARD_PATTERN,
318     complexity,
319     "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
320 }
321 
322 declare_clippy_lint! {
323     /// ### What it does
324     /// Checks for `[all @ ..]` patterns.
325     ///
326     /// ### Why is this bad?
327     /// In all cases, `all` works fine and can often make code simpler, as you possibly won't need
328     /// to convert from say a `Vec` to a slice by dereferencing.
329     ///
330     /// ### Example
331     /// ```rust,ignore
332     /// if let [all @ ..] = &*v {
333     ///     // NOTE: Type is a slice here
334     ///     println!("all elements: {all:#?}");
335     /// }
336     /// ```
337     /// Use instead:
338     /// ```rust,ignore
339     /// if let all = v {
340     ///     // NOTE: Type is a `Vec` here
341     ///     println!("all elements: {all:#?}");
342     /// }
343     /// // or
344     /// println!("all elements: {v:#?}");
345     /// ```
346     #[clippy::version = "1.72.0"]
347     pub REDUNDANT_AT_REST_PATTERN,
348     complexity,
349     "checks for `[all @ ..]` where `all` would suffice"
350 }
351 
352 declare_lint_pass!(MiscEarlyLints => [
353     UNNEEDED_FIELD_PATTERN,
354     DUPLICATE_UNDERSCORE_ARGUMENT,
355     DOUBLE_NEG,
356     MIXED_CASE_HEX_LITERALS,
357     UNSEPARATED_LITERAL_SUFFIX,
358     SEPARATED_LITERAL_SUFFIX,
359     ZERO_PREFIXED_LITERAL,
360     BUILTIN_TYPE_SHADOW,
361     REDUNDANT_PATTERN,
362     UNNEEDED_WILDCARD_PATTERN,
363     REDUNDANT_AT_REST_PATTERN,
364 ]);
365 
366 impl EarlyLintPass for MiscEarlyLints {
check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics)367     fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
368         for param in &gen.params {
369             builtin_type_shadow::check(cx, param);
370         }
371     }
372 
check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat)373     fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
374         if in_external_macro(cx.sess(), pat.span) {
375             return;
376         }
377 
378         unneeded_field_pattern::check(cx, pat);
379         redundant_pattern::check(cx, pat);
380         redundant_at_rest_pattern::check(cx, pat);
381         unneeded_wildcard_pattern::check(cx, pat);
382     }
383 
check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId)384     fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
385         let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
386 
387         for arg in &fn_kind.decl().inputs {
388             if let PatKind::Ident(_, ident, None) = arg.pat.kind {
389                 let arg_name = ident.to_string();
390 
391                 if let Some(arg_name) = arg_name.strip_prefix('_') {
392                     if let Some(correspondence) = registered_names.get(arg_name) {
393                         span_lint(
394                             cx,
395                             DUPLICATE_UNDERSCORE_ARGUMENT,
396                             *correspondence,
397                             &format!(
398                                 "`{arg_name}` already exists, having another argument having almost the same \
399                                  name makes code comprehension and documentation more difficult"
400                             ),
401                         );
402                     }
403                 } else {
404                     registered_names.insert(arg_name, arg.pat.span);
405                 }
406             }
407         }
408     }
409 
check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr)410     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
411         if in_external_macro(cx.sess(), expr.span) {
412             return;
413         }
414 
415         if let ExprKind::Lit(lit) = expr.kind {
416             MiscEarlyLints::check_lit(cx, lit, expr.span);
417         }
418         double_neg::check(cx, expr);
419     }
420 }
421 
422 impl MiscEarlyLints {
check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span)423     fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
424         // We test if first character in snippet is a number, because the snippet could be an expansion
425         // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
426         // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
427         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
428         // FIXME: Find a better way to detect those cases.
429         let lit_snip = match snippet_opt(cx, span) {
430             Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
431             _ => return,
432         };
433 
434         let lit_kind = LitKind::from_token_lit(lit);
435         if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind {
436             let suffix = match lit_int_type {
437                 LitIntType::Signed(ty) => ty.name_str(),
438                 LitIntType::Unsigned(ty) => ty.name_str(),
439                 LitIntType::Unsuffixed => "",
440             };
441             literal_suffix::check(cx, span, &lit_snip, suffix, "integer");
442             if lit_snip.starts_with("0x") {
443                 mixed_case_hex_literals::check(cx, span, suffix, &lit_snip);
444             } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
445                 // nothing to do
446             } else if value != 0 && lit_snip.starts_with('0') {
447                 zero_prefixed_literal::check(cx, span, &lit_snip);
448             }
449         } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind {
450             let suffix = float_ty.name_str();
451             literal_suffix::check(cx, span, &lit_snip, suffix, "float");
452         }
453     }
454 }
455