1 mod builtin_type_shadow;
4 mod mixed_case_hex_literals;
6 mod unneeded_field_pattern;
7 mod unneeded_wildcard_pattern;
8 mod zero_prefixed_literal;
10 use clippy_utils::diagnostics::span_lint;
11 use clippy_utils::source::snippet_opt;
12 use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
14 use rustc_ast::visit::FnKind;
15 use rustc_data_structures::fx::FxHashMap;
16 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
17 use rustc_middle::lint::in_external_macro;
18 use rustc_session::{declare_lint_pass, declare_tool_lint};
19 use rustc_span::source_map::Span;
21 declare_clippy_lint! {
23 /// Checks for structure field patterns bound to wildcards.
25 /// ### Why is this bad?
26 /// Using `..` instead is shorter and leaves the focus on
27 /// the fields that are actually bound.
36 /// let f = Foo { a: 0, b: 0, c: 0 };
39 /// Foo { a: _, b: 0, .. } => {},
40 /// Foo { a: _, b: _, c: _ } => {},
51 /// let f = Foo { a: 0, b: 0, c: 0 };
54 /// Foo { b: 0, .. } => {},
58 #[clippy::version = "pre 1.29.0"]
59 pub UNNEEDED_FIELD_PATTERN,
61 "struct fields bound to a wildcard instead of using `..`"
64 declare_clippy_lint! {
66 /// Checks for function arguments having the similar names
67 /// differing by an underscore.
69 /// ### Why is this bad?
70 /// It affects code readability.
74 /// fn foo(a: i32, _a: i32) {}
79 /// fn bar(a: i32, _b: i32) {}
81 #[clippy::version = "pre 1.29.0"]
82 pub DUPLICATE_UNDERSCORE_ARGUMENT,
84 "function arguments having names which only differ by an underscore"
87 declare_clippy_lint! {
89 /// Detects expressions of the form `--x`.
91 /// ### Why is this bad?
92 /// It can mislead C/C++ programmers to think `x` was
100 #[clippy::version = "pre 1.29.0"]
103 "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
106 declare_clippy_lint! {
108 /// Warns on hexadecimal literals with mixed-case letter
111 /// ### Why is this bad?
112 /// It looks confusing.
127 #[clippy::version = "pre 1.29.0"]
128 pub MIXED_CASE_HEX_LITERALS,
130 "hex literals whose letter digits are not consistently upper- or lowercased"
133 declare_clippy_lint! {
135 /// Warns if literal suffixes are not separated by an
137 /// To enforce unseparated literal suffix style,
138 /// see the `separated_literal_suffix` lint.
140 /// ### Why is this bad?
141 /// Suffix style should be consistent.
156 #[clippy::version = "pre 1.29.0"]
157 pub UNSEPARATED_LITERAL_SUFFIX,
159 "literals whose suffix is not separated by an underscore"
162 declare_clippy_lint! {
164 /// Warns if literal suffixes are separated by an underscore.
165 /// To enforce separated literal suffix style,
166 /// see the `unseparated_literal_suffix` lint.
168 /// ### Why is this bad?
169 /// Suffix style should be consistent.
184 #[clippy::version = "1.58.0"]
185 pub SEPARATED_LITERAL_SUFFIX,
187 "literals whose suffix is separated by an underscore"
190 declare_clippy_lint! {
192 /// Warns if an integral constant literal starts with `0`.
194 /// ### Why is this bad?
195 /// In some languages (including the infamous C language
197 /// family), this marks an octal constant. In Rust however, this is a decimal
198 /// constant. This could
199 /// be confusing for both the writer and a reader of the constant.
207 /// println!("{}", a);
211 /// prints `123`, while in C:
214 /// #include <stdio.h>
218 /// printf("%d\n", a);
222 /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
223 #[clippy::version = "pre 1.29.0"]
224 pub ZERO_PREFIXED_LITERAL,
226 "integer literals starting with `0`"
229 declare_clippy_lint! {
231 /// Warns if a generic shadows a built-in type.
233 /// ### Why is this bad?
234 /// This gives surprising type errors.
239 /// impl<u32> Foo<u32> {
240 /// fn impl_func(&self) -> u32 {
245 #[clippy::version = "pre 1.29.0"]
246 pub BUILTIN_TYPE_SHADOW,
248 "shadowing a builtin type"
251 declare_clippy_lint! {
253 /// Checks for patterns in the form `name @ _`.
255 /// ### Why is this bad?
256 /// It's almost always more readable to just use direct
261 /// # let v = Some("abc");
270 /// # let v = Some("abc");
276 #[clippy::version = "pre 1.29.0"]
277 pub REDUNDANT_PATTERN,
279 "using `name @ _` in a pattern"
282 declare_clippy_lint! {
284 /// Checks for tuple patterns with a wildcard
285 /// pattern (`_`) is next to a rest pattern (`..`).
287 /// _NOTE_: While `_, ..` means there is at least one element left, `..`
288 /// means there are 0 or more elements left. This can make a difference
289 /// when refactoring, but shouldn't result in errors in the refactored code,
290 /// since the wildcard pattern isn't used anyway.
292 /// ### Why is this bad?
293 /// The wildcard pattern is unneeded as the rest pattern
294 /// can match that element as well.
298 /// # struct TupleStruct(u32, u32, u32);
299 /// # let t = TupleStruct(1, 2, 3);
301 /// TupleStruct(0, .., _) => (),
308 /// # struct TupleStruct(u32, u32, u32);
309 /// # let t = TupleStruct(1, 2, 3);
311 /// TupleStruct(0, ..) => (),
315 #[clippy::version = "1.40.0"]
316 pub UNNEEDED_WILDCARD_PATTERN,
318 "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
321 declare_lint_pass!(MiscEarlyLints => [
322 UNNEEDED_FIELD_PATTERN,
323 DUPLICATE_UNDERSCORE_ARGUMENT,
325 MIXED_CASE_HEX_LITERALS,
326 UNSEPARATED_LITERAL_SUFFIX,
327 SEPARATED_LITERAL_SUFFIX,
328 ZERO_PREFIXED_LITERAL,
331 UNNEEDED_WILDCARD_PATTERN,
334 impl EarlyLintPass for MiscEarlyLints {
335 fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
336 for param in &gen.params {
337 builtin_type_shadow::check(cx, param);
341 fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
342 unneeded_field_pattern::check(cx, pat);
343 redundant_pattern::check(cx, pat);
344 unneeded_wildcard_pattern::check(cx, pat);
347 fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
348 let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
350 for arg in &fn_kind.decl().inputs {
351 if let PatKind::Ident(_, ident, None) = arg.pat.kind {
352 let arg_name = ident.to_string();
354 if let Some(arg_name) = arg_name.strip_prefix('_') {
355 if let Some(correspondence) = registered_names.get(arg_name) {
358 DUPLICATE_UNDERSCORE_ARGUMENT,
361 "`{arg_name}` already exists, having another argument having almost the same \
362 name makes code comprehension and documentation more difficult"
367 registered_names.insert(arg_name, arg.pat.span);
373 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
374 if in_external_macro(cx.sess(), expr.span) {
378 if let ExprKind::Lit(lit) = expr.kind {
379 MiscEarlyLints::check_lit(cx, lit, expr.span);
381 double_neg::check(cx, expr);
385 impl MiscEarlyLints {
386 fn check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
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, span) {
393 Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
397 let lit_kind = LitKind::from_token_lit(lit);
398 if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind {
399 let suffix = match lit_int_type {
400 LitIntType::Signed(ty) => ty.name_str(),
401 LitIntType::Unsigned(ty) => ty.name_str(),
402 LitIntType::Unsuffixed => "",
404 literal_suffix::check(cx, span, &lit_snip, suffix, "integer");
405 if lit_snip.starts_with("0x") {
406 mixed_case_hex_literals::check(cx, span, suffix, &lit_snip);
407 } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
409 } else if value != 0 && lit_snip.starts_with('0') {
410 zero_prefixed_literal::check(cx, span, &lit_snip);
412 } else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind {
413 let suffix = float_ty.name_str();
414 literal_suffix::check(cx, span, &lit_snip, suffix, "float");