]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/misc_early/mod.rs
New upstream version 1.75.0+dfsg1
[rustc.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_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::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 /// ```no_run
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 /// ```no_run
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 /// ```no_run
75 /// fn foo(a: i32, _a: i32) {}
76 /// ```
77 ///
78 /// Use instead:
79 /// ```no_run
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 /// ```no_run
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 /// ```no_run
117 /// # let _ =
118 /// 0x1a9BAcD
119 /// # ;
120 /// ```
121 ///
122 /// Use instead:
123 /// ```no_run
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 /// ```no_run
146 /// # let _ =
147 /// 123832i32
148 /// # ;
149 /// ```
150 ///
151 /// Use instead:
152 /// ```no_run
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 /// ```no_run
174 /// # let _ =
175 /// 123832_i32
176 /// # ;
177 /// ```
178 ///
179 /// Use instead:
180 /// ```no_run
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 /// ```no_run
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 /// ```no_run
262 /// # let v = Some("abc");
263 /// match v {
264 /// Some(x) => (),
265 /// y @ _ => (),
266 /// }
267 /// ```
268 ///
269 /// Use instead:
270 /// ```no_run
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 /// ```no_run
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 /// ```no_run
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 {
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
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
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
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 {
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 }