1 mod builtin_type_shadow
;
4 mod mixed_case_hex_literals
;
5 mod redundant_at_rest_pattern
;
7 mod unneeded_field_pattern
;
8 mod unneeded_wildcard_pattern
;
9 mod zero_prefixed_literal
;
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}
;
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}
;
22 declare_clippy_lint
! {
24 /// Checks for structure field patterns bound to wildcards.
26 /// ### Why is this bad?
27 /// Using `..` instead is shorter and leaves the focus on
28 /// the fields that are actually bound.
37 /// let f = Foo { a: 0, b: 0, c: 0 };
40 /// Foo { a: _, b: 0, .. } => {},
41 /// Foo { a: _, b: _, c: _ } => {},
52 /// let f = Foo { a: 0, b: 0, c: 0 };
55 /// Foo { b: 0, .. } => {},
59 #[clippy::version = "pre 1.29.0"]
60 pub UNNEEDED_FIELD_PATTERN
,
62 "struct fields bound to a wildcard instead of using `..`"
65 declare_clippy_lint
! {
67 /// Checks for function arguments having the similar names
68 /// differing by an underscore.
70 /// ### Why is this bad?
71 /// It affects code readability.
75 /// fn foo(a: i32, _a: i32) {}
80 /// fn bar(a: i32, _b: i32) {}
82 #[clippy::version = "pre 1.29.0"]
83 pub DUPLICATE_UNDERSCORE_ARGUMENT
,
85 "function arguments having names which only differ by an underscore"
88 declare_clippy_lint
! {
90 /// Detects expressions of the form `--x`.
92 /// ### Why is this bad?
93 /// It can mislead C/C++ programmers to think `x` was
101 #[clippy::version = "pre 1.29.0"]
104 "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
107 declare_clippy_lint
! {
109 /// Warns on hexadecimal literals with mixed-case letter
112 /// ### Why is this bad?
113 /// It looks confusing.
128 #[clippy::version = "pre 1.29.0"]
129 pub MIXED_CASE_HEX_LITERALS
,
131 "hex literals whose letter digits are not consistently upper- or lowercased"
134 declare_clippy_lint
! {
136 /// Warns if literal suffixes are not separated by an
138 /// To enforce unseparated literal suffix style,
139 /// see the `separated_literal_suffix` lint.
141 /// ### Why is this bad?
142 /// Suffix style should be consistent.
157 #[clippy::version = "pre 1.29.0"]
158 pub UNSEPARATED_LITERAL_SUFFIX
,
160 "literals whose suffix is not separated by an underscore"
163 declare_clippy_lint
! {
165 /// Warns if literal suffixes are separated by an underscore.
166 /// To enforce separated literal suffix style,
167 /// see the `unseparated_literal_suffix` lint.
169 /// ### Why is this bad?
170 /// Suffix style should be consistent.
185 #[clippy::version = "1.58.0"]
186 pub SEPARATED_LITERAL_SUFFIX
,
188 "literals whose suffix is separated by an underscore"
191 declare_clippy_lint
! {
193 /// Warns if an integral constant literal starts with `0`.
195 /// ### Why is this bad?
196 /// In some languages (including the infamous C language
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.
208 /// println!("{}", a);
212 /// prints `123`, while in C:
215 /// #include <stdio.h>
219 /// printf("%d\n", a);
223 /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
224 #[clippy::version = "pre 1.29.0"]
225 pub ZERO_PREFIXED_LITERAL
,
227 "integer literals starting with `0`"
230 declare_clippy_lint
! {
232 /// Warns if a generic shadows a built-in type.
234 /// ### Why is this bad?
235 /// This gives surprising type errors.
240 /// impl<u32> Foo<u32> {
241 /// fn impl_func(&self) -> u32 {
246 #[clippy::version = "pre 1.29.0"]
247 pub BUILTIN_TYPE_SHADOW
,
249 "shadowing a builtin type"
252 declare_clippy_lint
! {
254 /// Checks for patterns in the form `name @ _`.
256 /// ### Why is this bad?
257 /// It's almost always more readable to just use direct
262 /// # let v = Some("abc");
271 /// # let v = Some("abc");
277 #[clippy::version = "pre 1.29.0"]
278 pub REDUNDANT_PATTERN
,
280 "using `name @ _` in a pattern"
283 declare_clippy_lint
! {
285 /// Checks for tuple patterns with a wildcard
286 /// pattern (`_`) is next to a rest pattern (`..`).
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.
293 /// ### Why is this bad?
294 /// The wildcard pattern is unneeded as the rest pattern
295 /// can match that element as well.
299 /// # struct TupleStruct(u32, u32, u32);
300 /// # let t = TupleStruct(1, 2, 3);
302 /// TupleStruct(0, .., _) => (),
309 /// # struct TupleStruct(u32, u32, u32);
310 /// # let t = TupleStruct(1, 2, 3);
312 /// TupleStruct(0, ..) => (),
316 #[clippy::version = "1.40.0"]
317 pub UNNEEDED_WILDCARD_PATTERN
,
319 "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
322 declare_clippy_lint
! {
324 /// Checks for `[all @ ..]` patterns.
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.
332 /// if let [all @ ..] = &*v {
333 /// // NOTE: Type is a slice here
334 /// println!("all elements: {all:#?}");
340 /// // NOTE: Type is a `Vec` here
341 /// println!("all elements: {all:#?}");
344 /// println!("all elements: {v:#?}");
346 #[clippy::version = "1.72.0"]
347 pub REDUNDANT_AT_REST_PATTERN
,
349 "checks for `[all @ ..]` where `all` would suffice"
352 declare_lint_pass
!(MiscEarlyLints
=> [
353 UNNEEDED_FIELD_PATTERN
,
354 DUPLICATE_UNDERSCORE_ARGUMENT
,
356 MIXED_CASE_HEX_LITERALS
,
357 UNSEPARATED_LITERAL_SUFFIX
,
358 SEPARATED_LITERAL_SUFFIX
,
359 ZERO_PREFIXED_LITERAL
,
362 UNNEEDED_WILDCARD_PATTERN
,
363 REDUNDANT_AT_REST_PATTERN
,
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
);
373 fn check_pat(&mut self, cx
: &EarlyContext
<'_
>, pat
: &Pat
) {
374 if in_external_macro(cx
.sess(), pat
.span
) {
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
);
384 fn check_fn(&mut self, cx
: &EarlyContext
<'_
>, fn_kind
: FnKind
<'_
>, _
: Span
, _
: NodeId
) {
385 let mut registered_names
: FxHashMap
<String
, Span
> = FxHashMap
::default();
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();
391 if let Some(arg_name
) = arg_name
.strip_prefix('_'
) {
392 if let Some(correspondence
) = registered_names
.get(arg_name
) {
395 DUPLICATE_UNDERSCORE_ARGUMENT
,
398 "`{arg_name}` already exists, having another argument having almost the same \
399 name makes code comprehension and documentation more difficult"
404 registered_names
.insert(arg_name
, arg
.pat
.span
);
410 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, expr
: &Expr
) {
411 if in_external_macro(cx
.sess(), expr
.span
) {
415 if let ExprKind
::Lit(lit
) = expr
.kind
{
416 MiscEarlyLints
::check_lit(cx
, lit
, expr
.span
);
418 double_neg
::check(cx
, expr
);
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
,
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
=> "",
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") {
446 } else if value
!= 0 && lit_snip
.starts_with('
0'
) {
447 zero_prefixed_literal
::check(cx
, span
, &lit_snip
);
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");