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, 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;
20 declare_clippy_lint! {
22 /// Checks for structure field patterns bound to wildcards.
24 /// ### Why is this bad?
25 /// Using `..` instead is shorter and leaves the focus on
26 /// the fields that are actually bound.
35 /// let f = Foo { a: 0, b: 0, c: 0 };
38 /// Foo { a: _, b: 0, .. } => {},
39 /// Foo { a: _, b: _, c: _ } => {},
50 /// let f = Foo { a: 0, b: 0, c: 0 };
53 /// Foo { b: 0, .. } => {},
57 #[clippy::version = "pre 1.29.0"]
58 pub UNNEEDED_FIELD_PATTERN,
60 "struct fields bound to a wildcard instead of using `..`"
63 declare_clippy_lint! {
65 /// Checks for function arguments having the similar names
66 /// differing by an underscore.
68 /// ### Why is this bad?
69 /// It affects code readability.
73 /// fn foo(a: i32, _a: i32) {}
78 /// fn bar(a: i32, _b: i32) {}
80 #[clippy::version = "pre 1.29.0"]
81 pub DUPLICATE_UNDERSCORE_ARGUMENT,
83 "function arguments having names which only differ by an underscore"
86 declare_clippy_lint! {
88 /// Detects expressions of the form `--x`.
90 /// ### Why is this bad?
91 /// It can mislead C/C++ programmers to think `x` was
99 #[clippy::version = "pre 1.29.0"]
102 "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
105 declare_clippy_lint! {
107 /// Warns on hexadecimal literals with mixed-case letter
110 /// ### Why is this bad?
111 /// It looks confusing.
126 #[clippy::version = "pre 1.29.0"]
127 pub MIXED_CASE_HEX_LITERALS,
129 "hex literals whose letter digits are not consistently upper- or lowercased"
132 declare_clippy_lint! {
134 /// Warns if literal suffixes are not separated by an
136 /// To enforce unseparated literal suffix style,
137 /// see the `separated_literal_suffix` lint.
139 /// ### Why is this bad?
140 /// Suffix style should be consistent.
155 #[clippy::version = "pre 1.29.0"]
156 pub UNSEPARATED_LITERAL_SUFFIX,
158 "literals whose suffix is not separated by an underscore"
161 declare_clippy_lint! {
163 /// Warns if literal suffixes are separated by an underscore.
164 /// To enforce separated literal suffix style,
165 /// see the `unseparated_literal_suffix` lint.
167 /// ### Why is this bad?
168 /// Suffix style should be consistent.
183 #[clippy::version = "1.58.0"]
184 pub SEPARATED_LITERAL_SUFFIX,
186 "literals whose suffix is separated by an underscore"
189 declare_clippy_lint! {
191 /// Warns if an integral constant literal starts with `0`.
193 /// ### Why is this bad?
194 /// In some languages (including the infamous C language
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.
206 /// println!("{}", a);
210 /// prints `123`, while in C:
213 /// #include <stdio.h>
217 /// printf("%d\n", a);
221 /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
222 #[clippy::version = "pre 1.29.0"]
223 pub ZERO_PREFIXED_LITERAL,
225 "integer literals starting with `0`"
228 declare_clippy_lint! {
230 /// Warns if a generic shadows a built-in type.
232 /// ### Why is this bad?
233 /// This gives surprising type errors.
238 /// impl<u32> Foo<u32> {
239 /// fn impl_func(&self) -> u32 {
244 #[clippy::version = "pre 1.29.0"]
245 pub BUILTIN_TYPE_SHADOW,
247 "shadowing a builtin type"
250 declare_clippy_lint! {
252 /// Checks for patterns in the form `name @ _`.
254 /// ### Why is this bad?
255 /// It's almost always more readable to just use direct
260 /// # let v = Some("abc");
269 /// # let v = Some("abc");
275 #[clippy::version = "pre 1.29.0"]
276 pub REDUNDANT_PATTERN,
278 "using `name @ _` in a pattern"
281 declare_clippy_lint! {
283 /// Checks for tuple patterns with a wildcard
284 /// pattern (`_`) is next to a rest pattern (`..`).
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.
291 /// ### Why is this bad?
292 /// The wildcard pattern is unneeded as the rest pattern
293 /// can match that element as well.
297 /// # struct TupleStruct(u32, u32, u32);
298 /// # let t = TupleStruct(1, 2, 3);
300 /// TupleStruct(0, .., _) => (),
307 /// # struct TupleStruct(u32, u32, u32);
308 /// # let t = TupleStruct(1, 2, 3);
310 /// TupleStruct(0, ..) => (),
314 #[clippy::version = "1.40.0"]
315 pub UNNEEDED_WILDCARD_PATTERN,
317 "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
320 declare_lint_pass!(MiscEarlyLints => [
321 UNNEEDED_FIELD_PATTERN,
322 DUPLICATE_UNDERSCORE_ARGUMENT,
324 MIXED_CASE_HEX_LITERALS,
325 UNSEPARATED_LITERAL_SUFFIX,
326 SEPARATED_LITERAL_SUFFIX,
327 ZERO_PREFIXED_LITERAL,
330 UNNEEDED_WILDCARD_PATTERN,
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);
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);
346 fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
347 let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
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();
353 if let Some(arg_name) = arg_name.strip_prefix('_') {
354 if let Some(correspondence) = registered_names.get(arg_name) {
357 DUPLICATE_UNDERSCORE_ARGUMENT,
360 "`{}` already exists, having another argument having almost the same \
361 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(ref lit) = expr.kind {
379 MiscEarlyLints::check_lit(cx, lit);
381 double_neg::check(cx, expr);
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,
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 => "",
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") {
408 } else if value != 0 && lit_snip.starts_with('0') {
409 zero_prefixed_literal::check(cx, lit, &lit_snip);
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");