1 mod builtin_type_shadow; 2 mod double_neg; 3 mod literal_suffix; 4 mod mixed_case_hex_literals; 5 mod redundant_pattern; 6 mod unneeded_field_pattern; 7 mod unneeded_wildcard_pattern; 8 mod zero_prefixed_literal; 9 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}; 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; 19 20 declare_clippy_lint! { 21 /// ### What it does 22 /// Checks for structure field patterns bound to wildcards. 23 /// 24 /// ### Why is this bad? 25 /// Using `..` instead is shorter and leaves the focus on 26 /// the fields that are actually bound. 27 /// 28 /// ### Example 29 /// ```rust 30 /// # struct Foo { 31 /// # a: i32, 32 /// # b: i32, 33 /// # c: i32, 34 /// # } 35 /// let f = Foo { a: 0, b: 0, c: 0 }; 36 /// 37 /// // Bad 38 /// match f { 39 /// Foo { a: _, b: 0, .. } => {}, 40 /// Foo { a: _, b: _, c: _ } => {}, 41 /// } 42 /// 43 /// // Good 44 /// match f { 45 /// Foo { b: 0, .. } => {}, 46 /// Foo { .. } => {}, 47 /// } 48 /// ``` 49 pub UNNEEDED_FIELD_PATTERN, 50 restriction, 51 "struct fields bound to a wildcard instead of using `..`" 52 } 53 54 declare_clippy_lint! { 55 /// ### What it does 56 /// Checks for function arguments having the similar names 57 /// differing by an underscore. 58 /// 59 /// ### Why is this bad? 60 /// It affects code readability. 61 /// 62 /// ### Example 63 /// ```rust 64 /// // Bad 65 /// fn foo(a: i32, _a: i32) {} 66 /// 67 /// // Good 68 /// fn bar(a: i32, _b: i32) {} 69 /// ``` 70 pub DUPLICATE_UNDERSCORE_ARGUMENT, 71 style, 72 "function arguments having names which only differ by an underscore" 73 } 74 75 declare_clippy_lint! { 76 /// ### What it does 77 /// Detects expressions of the form `--x`. 78 /// 79 /// ### Why is this bad? 80 /// It can mislead C/C++ programmers to think `x` was 81 /// decremented. 82 /// 83 /// ### Example 84 /// ```rust 85 /// let mut x = 3; 86 /// --x; 87 /// ``` 88 pub DOUBLE_NEG, 89 style, 90 "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++" 91 } 92 93 declare_clippy_lint! { 94 /// ### What it does 95 /// Warns on hexadecimal literals with mixed-case letter 96 /// digits. 97 /// 98 /// ### Why is this bad? 99 /// It looks confusing. 100 /// 101 /// ### Example 102 /// ```rust 103 /// // Bad 104 /// let y = 0x1a9BAcD; 105 /// 106 /// // Good 107 /// let y = 0x1A9BACD; 108 /// ``` 109 pub MIXED_CASE_HEX_LITERALS, 110 style, 111 "hex literals whose letter digits are not consistently upper- or lowercased" 112 } 113 114 declare_clippy_lint! { 115 /// ### What it does 116 /// Warns if literal suffixes are not separated by an 117 /// underscore. 118 /// To enforce unseparated literal suffix style, 119 /// see the `separated_literal_suffix` lint. 120 /// 121 /// ### Why is this bad? 122 /// Suffix style should be consistent. 123 /// 124 /// ### Example 125 /// ```rust 126 /// // Bad 127 /// let y = 123832i32; 128 /// 129 /// // Good 130 /// let y = 123832_i32; 131 /// ``` 132 pub UNSEPARATED_LITERAL_SUFFIX, 133 restriction, 134 "literals whose suffix is not separated by an underscore" 135 } 136 137 declare_clippy_lint! { 138 /// ### What it does 139 /// Warns if literal suffixes are separated by an underscore. 140 /// To enforce separated literal suffix style, 141 /// see the `unseparated_literal_suffix` lint. 142 /// 143 /// ### Why is this bad? 144 /// Suffix style should be consistent. 145 /// 146 /// ### Example 147 /// ```rust 148 /// // Bad 149 /// let y = 123832_i32; 150 /// 151 /// // Good 152 /// let y = 123832i32; 153 /// ``` 154 pub SEPARATED_LITERAL_SUFFIX, 155 restriction, 156 "literals whose suffix is separated by an underscore" 157 } 158 159 declare_clippy_lint! { 160 /// ### What it does 161 /// Warns if an integral constant literal starts with `0`. 162 /// 163 /// ### Why is this bad? 164 /// In some languages (including the infamous C language 165 /// and most of its 166 /// family), this marks an octal constant. In Rust however, this is a decimal 167 /// constant. This could 168 /// be confusing for both the writer and a reader of the constant. 169 /// 170 /// ### Example 171 /// 172 /// In Rust: 173 /// ```rust 174 /// fn main() { 175 /// let a = 0123; 176 /// println!("{}", a); 177 /// } 178 /// ``` 179 /// 180 /// prints `123`, while in C: 181 /// 182 /// ```c 183 /// #include <stdio.h> 184 /// 185 /// int main() { 186 /// int a = 0123; 187 /// printf("%d\n", a); 188 /// } 189 /// ``` 190 /// 191 /// prints `83` (as `83 == 0o123` while `123 == 0o173`). 192 pub ZERO_PREFIXED_LITERAL, 193 complexity, 194 "integer literals starting with `0`" 195 } 196 197 declare_clippy_lint! { 198 /// ### What it does 199 /// Warns if a generic shadows a built-in type. 200 /// 201 /// ### Why is this bad? 202 /// This gives surprising type errors. 203 /// 204 /// ### Example 205 /// 206 /// ```ignore 207 /// impl<u32> Foo<u32> { 208 /// fn impl_func(&self) -> u32 { 209 /// 42 210 /// } 211 /// } 212 /// ``` 213 pub BUILTIN_TYPE_SHADOW, 214 style, 215 "shadowing a builtin type" 216 } 217 218 declare_clippy_lint! { 219 /// ### What it does 220 /// Checks for patterns in the form `name @ _`. 221 /// 222 /// ### Why is this bad? 223 /// It's almost always more readable to just use direct 224 /// bindings. 225 /// 226 /// ### Example 227 /// ```rust 228 /// # let v = Some("abc"); 229 /// 230 /// // Bad 231 /// match v { 232 /// Some(x) => (), 233 /// y @ _ => (), 234 /// } 235 /// 236 /// // Good 237 /// match v { 238 /// Some(x) => (), 239 /// y => (), 240 /// } 241 /// ``` 242 pub REDUNDANT_PATTERN, 243 style, 244 "using `name @ _` in a pattern" 245 } 246 247 declare_clippy_lint! { 248 /// ### What it does 249 /// Checks for tuple patterns with a wildcard 250 /// pattern (`_`) is next to a rest pattern (`..`). 251 /// 252 /// _NOTE_: While `_, ..` means there is at least one element left, `..` 253 /// means there are 0 or more elements left. This can make a difference 254 /// when refactoring, but shouldn't result in errors in the refactored code, 255 /// since the wildcard pattern isn't used anyway. 256 /// ### Why is this bad? 257 /// The wildcard pattern is unneeded as the rest pattern 258 /// can match that element as well. 259 /// 260 /// ### Example 261 /// ```rust 262 /// # struct TupleStruct(u32, u32, u32); 263 /// # let t = TupleStruct(1, 2, 3); 264 /// // Bad 265 /// match t { 266 /// TupleStruct(0, .., _) => (), 267 /// _ => (), 268 /// } 269 /// 270 /// // Good 271 /// match t { 272 /// TupleStruct(0, ..) => (), 273 /// _ => (), 274 /// } 275 /// ``` 276 pub UNNEEDED_WILDCARD_PATTERN, 277 complexity, 278 "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)" 279 } 280 281 declare_lint_pass!(MiscEarlyLints => [ 282 UNNEEDED_FIELD_PATTERN, 283 DUPLICATE_UNDERSCORE_ARGUMENT, 284 DOUBLE_NEG, 285 MIXED_CASE_HEX_LITERALS, 286 UNSEPARATED_LITERAL_SUFFIX, 287 SEPARATED_LITERAL_SUFFIX, 288 ZERO_PREFIXED_LITERAL, 289 BUILTIN_TYPE_SHADOW, 290 REDUNDANT_PATTERN, 291 UNNEEDED_WILDCARD_PATTERN, 292 ]); 293 294 impl EarlyLintPass for MiscEarlyLints { check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics)295 fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { 296 for param in &gen.params { 297 builtin_type_shadow::check(cx, param); 298 } 299 } 300 check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat)301 fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { 302 unneeded_field_pattern::check(cx, pat); 303 redundant_pattern::check(cx, pat); 304 unneeded_wildcard_pattern::check(cx, pat); 305 } 306 check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId)307 fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { 308 let mut registered_names: FxHashMap<String, Span> = FxHashMap::default(); 309 310 for arg in &fn_kind.decl().inputs { 311 if let PatKind::Ident(_, ident, None) = arg.pat.kind { 312 let arg_name = ident.to_string(); 313 314 if let Some(arg_name) = arg_name.strip_prefix('_') { 315 if let Some(correspondence) = registered_names.get(arg_name) { 316 span_lint( 317 cx, 318 DUPLICATE_UNDERSCORE_ARGUMENT, 319 *correspondence, 320 &format!( 321 "`{}` already exists, having another argument having almost the same \ 322 name makes code comprehension and documentation more difficult", 323 arg_name 324 ), 325 ); 326 } 327 } else { 328 registered_names.insert(arg_name, arg.pat.span); 329 } 330 } 331 } 332 } 333 check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr)334 fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { 335 if in_external_macro(cx.sess, expr.span) { 336 return; 337 } 338 339 if let ExprKind::Lit(ref lit) = expr.kind { 340 MiscEarlyLints::check_lit(cx, lit); 341 } 342 double_neg::check(cx, expr); 343 } 344 } 345 346 impl MiscEarlyLints { check_lit(cx: &EarlyContext<'_>, lit: &Lit)347 fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { 348 // We test if first character in snippet is a number, because the snippet could be an expansion 349 // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`. 350 // Note that this check also covers special case that `line!()` is eagerly expanded by compiler. 351 // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression. 352 // FIXME: Find a better way to detect those cases. 353 let lit_snip = match snippet_opt(cx, lit.span) { 354 Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, 355 _ => return, 356 }; 357 358 if let LitKind::Int(value, lit_int_type) = lit.kind { 359 let suffix = match lit_int_type { 360 LitIntType::Signed(ty) => ty.name_str(), 361 LitIntType::Unsigned(ty) => ty.name_str(), 362 LitIntType::Unsuffixed => "", 363 }; 364 literal_suffix::check(cx, lit, &lit_snip, suffix, "integer"); 365 if lit_snip.starts_with("0x") { 366 mixed_case_hex_literals::check(cx, lit, suffix, &lit_snip); 367 } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") { 368 // nothing to do 369 } else if value != 0 && lit_snip.starts_with('0') { 370 zero_prefixed_literal::check(cx, lit, &lit_snip); 371 } 372 } else if let LitKind::Float(_, LitFloatType::Suffixed(float_ty)) = lit.kind { 373 let suffix = float_ty.name_str(); 374 literal_suffix::check(cx, lit, &lit_snip, suffix, "float"); 375 } 376 } 377 } 378