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::source_map::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 /// ```rust 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 /// ```rust 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 /// ```rust 75 /// fn foo(a: i32, _a: i32) {} 76 /// ``` 77 /// 78 /// Use instead: 79 /// ```rust 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 /// ```rust 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 /// ```rust 117 /// # let _ = 118 /// 0x1a9BAcD 119 /// # ; 120 /// ``` 121 /// 122 /// Use instead: 123 /// ```rust 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 /// ```rust 146 /// # let _ = 147 /// 123832i32 148 /// # ; 149 /// ``` 150 /// 151 /// Use instead: 152 /// ```rust 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 /// ```rust 174 /// # let _ = 175 /// 123832_i32 176 /// # ; 177 /// ``` 178 /// 179 /// Use instead: 180 /// ```rust 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 /// ```rust 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 /// ```rust 262 /// # let v = Some("abc"); 263 /// match v { 264 /// Some(x) => (), 265 /// y @ _ => (), 266 /// } 267 /// ``` 268 /// 269 /// Use instead: 270 /// ```rust 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 /// ```rust 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 /// ```rust 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 { check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics)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 check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat)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 check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId)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 check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr)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 { check_lit(cx: &EarlyContext<'_>, lit: token::Lit, span: Span)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 } 455