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