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