]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | mod bind_instead_of_map; |
2 | mod bytes_nth; | |
cdc7bbd5 XL |
3 | mod chars_cmp; |
4 | mod chars_cmp_with_unwrap; | |
5 | mod chars_last_cmp; | |
6 | mod chars_last_cmp_with_unwrap; | |
7 | mod chars_next_cmp; | |
8 | mod chars_next_cmp_with_unwrap; | |
f20569fa XL |
9 | mod clone_on_copy; |
10 | mod clone_on_ref_ptr; | |
cdc7bbd5 | 11 | mod cloned_instead_of_copied; |
f20569fa XL |
12 | mod expect_fun_call; |
13 | mod expect_used; | |
136023e0 | 14 | mod extend_with_drain; |
f20569fa | 15 | mod filetype_is_file; |
f20569fa | 16 | mod filter_map; |
f20569fa | 17 | mod filter_map_identity; |
f20569fa XL |
18 | mod filter_map_next; |
19 | mod filter_next; | |
20 | mod flat_map_identity; | |
cdc7bbd5 | 21 | mod flat_map_option; |
f20569fa XL |
22 | mod from_iter_instead_of_collect; |
23 | mod get_unwrap; | |
24 | mod implicit_clone; | |
25 | mod inefficient_to_string; | |
26 | mod inspect_for_each; | |
27 | mod into_iter_on_ref; | |
28 | mod iter_cloned_collect; | |
29 | mod iter_count; | |
30 | mod iter_next_slice; | |
31 | mod iter_nth; | |
32 | mod iter_nth_zero; | |
33 | mod iter_skip_next; | |
34 | mod iterator_step_by_zero; | |
35 | mod manual_saturating_arithmetic; | |
17df50a5 | 36 | mod manual_str_repeat; |
f20569fa XL |
37 | mod map_collect_result_unit; |
38 | mod map_flatten; | |
136023e0 | 39 | mod map_identity; |
f20569fa XL |
40 | mod map_unwrap_or; |
41 | mod ok_expect; | |
42 | mod option_as_ref_deref; | |
43 | mod option_map_or_none; | |
44 | mod option_map_unwrap_or; | |
45 | mod or_fun_call; | |
46 | mod search_is_some; | |
cdc7bbd5 | 47 | mod single_char_add_str; |
f20569fa XL |
48 | mod single_char_insert_string; |
49 | mod single_char_pattern; | |
50 | mod single_char_push_string; | |
51 | mod skip_while_next; | |
a2a8927a | 52 | mod str_splitn; |
f20569fa XL |
53 | mod string_extend_chars; |
54 | mod suspicious_map; | |
17df50a5 | 55 | mod suspicious_splitn; |
f20569fa XL |
56 | mod uninit_assumed_init; |
57 | mod unnecessary_filter_map; | |
58 | mod unnecessary_fold; | |
a2a8927a | 59 | mod unnecessary_iter_cloned; |
f20569fa | 60 | mod unnecessary_lazy_eval; |
a2a8927a | 61 | mod unnecessary_to_owned; |
94222f64 | 62 | mod unwrap_or_else_default; |
f20569fa XL |
63 | mod unwrap_used; |
64 | mod useless_asref; | |
cdc7bbd5 | 65 | mod utils; |
f20569fa XL |
66 | mod wrong_self_convention; |
67 | mod zst_offset; | |
68 | ||
69 | use bind_instead_of_map::BindInsteadOfMap; | |
c295e0f8 | 70 | use clippy_utils::consts::{constant, Constant}; |
cdc7bbd5 XL |
71 | use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; |
72 | use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; | |
a2a8927a | 73 | use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; |
f20569fa | 74 | use if_chain::if_chain; |
f20569fa | 75 | use rustc_hir as hir; |
cdc7bbd5 XL |
76 | use rustc_hir::def::Res; |
77 | use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; | |
78 | use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
f20569fa XL |
79 | use rustc_middle::lint::in_external_macro; |
80 | use rustc_middle::ty::{self, TraitRef, Ty, TyS}; | |
81 | use rustc_semver::RustcVersion; | |
82 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
a2a8927a | 83 | use rustc_span::symbol::Symbol; |
cdc7bbd5 | 84 | use rustc_span::{sym, Span}; |
f20569fa XL |
85 | use rustc_typeck::hir_ty_to_ty; |
86 | ||
cdc7bbd5 | 87 | declare_clippy_lint! { |
94222f64 XL |
88 | /// ### What it does |
89 | /// Checks for usages of `cloned()` on an `Iterator` or `Option` where | |
cdc7bbd5 XL |
90 | /// `copied()` could be used instead. |
91 | /// | |
94222f64 XL |
92 | /// ### Why is this bad? |
93 | /// `copied()` is better because it guarantees that the type being cloned | |
cdc7bbd5 XL |
94 | /// implements `Copy`. |
95 | /// | |
94222f64 | 96 | /// ### Example |
cdc7bbd5 XL |
97 | /// ```rust |
98 | /// [1, 2, 3].iter().cloned(); | |
99 | /// ``` | |
100 | /// Use instead: | |
101 | /// ```rust | |
102 | /// [1, 2, 3].iter().copied(); | |
103 | /// ``` | |
a2a8927a | 104 | #[clippy::version = "1.53.0"] |
cdc7bbd5 XL |
105 | pub CLONED_INSTEAD_OF_COPIED, |
106 | pedantic, | |
107 | "used `cloned` where `copied` could be used instead" | |
108 | } | |
109 | ||
110 | declare_clippy_lint! { | |
94222f64 XL |
111 | /// ### What it does |
112 | /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be | |
cdc7bbd5 XL |
113 | /// used instead. |
114 | /// | |
94222f64 XL |
115 | /// ### Why is this bad? |
116 | /// When applicable, `filter_map()` is more clear since it shows that | |
cdc7bbd5 XL |
117 | /// `Option` is used to produce 0 or 1 items. |
118 | /// | |
94222f64 | 119 | /// ### Example |
cdc7bbd5 XL |
120 | /// ```rust |
121 | /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); | |
122 | /// ``` | |
123 | /// Use instead: | |
124 | /// ```rust | |
125 | /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); | |
126 | /// ``` | |
a2a8927a | 127 | #[clippy::version = "1.53.0"] |
cdc7bbd5 XL |
128 | pub FLAT_MAP_OPTION, |
129 | pedantic, | |
130 | "used `flat_map` where `filter_map` could be used instead" | |
131 | } | |
f20569fa XL |
132 | |
133 | declare_clippy_lint! { | |
94222f64 XL |
134 | /// ### What it does |
135 | /// Checks for `.unwrap()` calls on `Option`s and on `Result`s. | |
f20569fa | 136 | /// |
94222f64 XL |
137 | /// ### Why is this bad? |
138 | /// It is better to handle the `None` or `Err` case, | |
f20569fa XL |
139 | /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of |
140 | /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is | |
141 | /// `Allow` by default. | |
142 | /// | |
143 | /// `result.unwrap()` will let the thread panic on `Err` values. | |
144 | /// Normally, you want to implement more sophisticated error handling, | |
145 | /// and propagate errors upwards with `?` operator. | |
146 | /// | |
147 | /// Even if you want to panic on errors, not all `Error`s implement good | |
148 | /// messages on display. Therefore, it may be beneficial to look at the places | |
149 | /// where they may get displayed. Activate this lint to do just that. | |
150 | /// | |
94222f64 | 151 | /// ### Examples |
f20569fa XL |
152 | /// ```rust |
153 | /// # let opt = Some(1); | |
154 | /// | |
155 | /// // Bad | |
156 | /// opt.unwrap(); | |
157 | /// | |
158 | /// // Good | |
159 | /// opt.expect("more helpful message"); | |
160 | /// ``` | |
161 | /// | |
162 | /// // or | |
163 | /// | |
164 | /// ```rust | |
165 | /// # let res: Result<usize, ()> = Ok(1); | |
166 | /// | |
167 | /// // Bad | |
168 | /// res.unwrap(); | |
169 | /// | |
170 | /// // Good | |
171 | /// res.expect("more helpful message"); | |
172 | /// ``` | |
a2a8927a | 173 | #[clippy::version = "1.45.0"] |
f20569fa XL |
174 | pub UNWRAP_USED, |
175 | restriction, | |
176 | "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`" | |
177 | } | |
178 | ||
179 | declare_clippy_lint! { | |
94222f64 XL |
180 | /// ### What it does |
181 | /// Checks for `.expect()` calls on `Option`s and `Result`s. | |
f20569fa | 182 | /// |
94222f64 XL |
183 | /// ### Why is this bad? |
184 | /// Usually it is better to handle the `None` or `Err` case. | |
f20569fa XL |
185 | /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why |
186 | /// this lint is `Allow` by default. | |
187 | /// | |
188 | /// `result.expect()` will let the thread panic on `Err` | |
189 | /// values. Normally, you want to implement more sophisticated error handling, | |
190 | /// and propagate errors upwards with `?` operator. | |
191 | /// | |
94222f64 | 192 | /// ### Examples |
f20569fa XL |
193 | /// ```rust,ignore |
194 | /// # let opt = Some(1); | |
195 | /// | |
196 | /// // Bad | |
197 | /// opt.expect("one"); | |
198 | /// | |
199 | /// // Good | |
200 | /// let opt = Some(1); | |
201 | /// opt?; | |
202 | /// ``` | |
203 | /// | |
204 | /// // or | |
205 | /// | |
206 | /// ```rust | |
207 | /// # let res: Result<usize, ()> = Ok(1); | |
208 | /// | |
209 | /// // Bad | |
210 | /// res.expect("one"); | |
211 | /// | |
212 | /// // Good | |
213 | /// res?; | |
214 | /// # Ok::<(), ()>(()) | |
215 | /// ``` | |
a2a8927a | 216 | #[clippy::version = "1.45.0"] |
f20569fa XL |
217 | pub EXPECT_USED, |
218 | restriction, | |
219 | "using `.expect()` on `Result` or `Option`, which might be better handled" | |
220 | } | |
221 | ||
222 | declare_clippy_lint! { | |
94222f64 XL |
223 | /// ### What it does |
224 | /// Checks for methods that should live in a trait | |
f20569fa XL |
225 | /// implementation of a `std` trait (see [llogiq's blog |
226 | /// post](http://llogiq.github.io/2015/07/30/traits.html) for further | |
227 | /// information) instead of an inherent implementation. | |
228 | /// | |
94222f64 XL |
229 | /// ### Why is this bad? |
230 | /// Implementing the traits improve ergonomics for users of | |
f20569fa XL |
231 | /// the code, often with very little cost. Also people seeing a `mul(...)` |
232 | /// method | |
233 | /// may expect `*` to work equally, so you should have good reason to disappoint | |
234 | /// them. | |
235 | /// | |
94222f64 | 236 | /// ### Example |
f20569fa XL |
237 | /// ```rust |
238 | /// struct X; | |
239 | /// impl X { | |
240 | /// fn add(&self, other: &X) -> X { | |
241 | /// // .. | |
242 | /// # X | |
243 | /// } | |
244 | /// } | |
245 | /// ``` | |
a2a8927a | 246 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
247 | pub SHOULD_IMPLEMENT_TRAIT, |
248 | style, | |
249 | "defining a method that should be implementing a std trait" | |
250 | } | |
251 | ||
252 | declare_clippy_lint! { | |
94222f64 XL |
253 | /// ### What it does |
254 | /// Checks for methods with certain name prefixes and which | |
f20569fa XL |
255 | /// doesn't match how self is taken. The actual rules are: |
256 | /// | |
cdc7bbd5 XL |
257 | /// |Prefix |Postfix |`self` taken | `self` type | |
258 | /// |-------|------------|-----------------------|--------------| | |
259 | /// |`as_` | none |`&self` or `&mut self` | any | | |
260 | /// |`from_`| none | none | any | | |
261 | /// |`into_`| none |`self` | any | | |
262 | /// |`is_` | none |`&self` or none | any | | |
263 | /// |`to_` | `_mut` |`&mut self` | any | | |
264 | /// |`to_` | not `_mut` |`self` | `Copy` | | |
265 | /// |`to_` | not `_mut` |`&self` | not `Copy` | | |
266 | /// | |
267 | /// Note: Clippy doesn't trigger methods with `to_` prefix in: | |
268 | /// - Traits definition. | |
269 | /// Clippy can not tell if a type that implements a trait is `Copy` or not. | |
270 | /// - Traits implementation, when `&self` is taken. | |
271 | /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait | |
272 | /// (see e.g. the `std::string::ToString` trait). | |
273 | /// | |
c295e0f8 XL |
274 | /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required. |
275 | /// | |
cdc7bbd5 XL |
276 | /// Please find more info here: |
277 | /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv | |
f20569fa | 278 | /// |
94222f64 XL |
279 | /// ### Why is this bad? |
280 | /// Consistency breeds readability. If you follow the | |
f20569fa XL |
281 | /// conventions, your users won't be surprised that they, e.g., need to supply a |
282 | /// mutable reference to a `as_..` function. | |
283 | /// | |
94222f64 | 284 | /// ### Example |
f20569fa XL |
285 | /// ```rust |
286 | /// # struct X; | |
287 | /// impl X { | |
288 | /// fn as_str(self) -> &'static str { | |
289 | /// // .. | |
290 | /// # "" | |
291 | /// } | |
292 | /// } | |
293 | /// ``` | |
a2a8927a | 294 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
295 | pub WRONG_SELF_CONVENTION, |
296 | style, | |
297 | "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" | |
298 | } | |
299 | ||
f20569fa | 300 | declare_clippy_lint! { |
94222f64 XL |
301 | /// ### What it does |
302 | /// Checks for usage of `ok().expect(..)`. | |
f20569fa | 303 | /// |
94222f64 XL |
304 | /// ### Why is this bad? |
305 | /// Because you usually call `expect()` on the `Result` | |
f20569fa XL |
306 | /// directly to get a better error message. |
307 | /// | |
94222f64 XL |
308 | /// ### Known problems |
309 | /// The error type needs to implement `Debug` | |
f20569fa | 310 | /// |
94222f64 | 311 | /// ### Example |
f20569fa XL |
312 | /// ```rust |
313 | /// # let x = Ok::<_, ()>(()); | |
314 | /// | |
315 | /// // Bad | |
316 | /// x.ok().expect("why did I do this again?"); | |
317 | /// | |
318 | /// // Good | |
319 | /// x.expect("why did I do this again?"); | |
320 | /// ``` | |
a2a8927a | 321 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
322 | pub OK_EXPECT, |
323 | style, | |
324 | "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result" | |
325 | } | |
326 | ||
327 | declare_clippy_lint! { | |
94222f64 XL |
328 | /// ### What it does |
329 | /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and | |
330 | /// `Result` values. | |
331 | /// | |
332 | /// ### Why is this bad? | |
333 | /// Readability, these can be written as `_.unwrap_or_default`, which is | |
334 | /// simpler and more concise. | |
335 | /// | |
336 | /// ### Examples | |
337 | /// ```rust | |
338 | /// # let x = Some(1); | |
339 | /// | |
340 | /// // Bad | |
341 | /// x.unwrap_or_else(Default::default); | |
342 | /// x.unwrap_or_else(u32::default); | |
343 | /// | |
344 | /// // Good | |
345 | /// x.unwrap_or_default(); | |
346 | /// ``` | |
a2a8927a | 347 | #[clippy::version = "1.56.0"] |
94222f64 XL |
348 | pub UNWRAP_OR_ELSE_DEFAULT, |
349 | style, | |
350 | "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`" | |
351 | } | |
352 | ||
353 | declare_clippy_lint! { | |
354 | /// ### What it does | |
355 | /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or | |
f20569fa XL |
356 | /// `result.map(_).unwrap_or_else(_)`. |
357 | /// | |
94222f64 XL |
358 | /// ### Why is this bad? |
359 | /// Readability, these can be written more concisely (resp.) as | |
f20569fa XL |
360 | /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. |
361 | /// | |
94222f64 XL |
362 | /// ### Known problems |
363 | /// The order of the arguments is not in execution order | |
f20569fa | 364 | /// |
94222f64 | 365 | /// ### Examples |
f20569fa XL |
366 | /// ```rust |
367 | /// # let x = Some(1); | |
368 | /// | |
369 | /// // Bad | |
370 | /// x.map(|a| a + 1).unwrap_or(0); | |
371 | /// | |
372 | /// // Good | |
373 | /// x.map_or(0, |a| a + 1); | |
374 | /// ``` | |
375 | /// | |
376 | /// // or | |
377 | /// | |
378 | /// ```rust | |
379 | /// # let x: Result<usize, ()> = Ok(1); | |
380 | /// # fn some_function(foo: ()) -> usize { 1 } | |
381 | /// | |
382 | /// // Bad | |
383 | /// x.map(|a| a + 1).unwrap_or_else(some_function); | |
384 | /// | |
385 | /// // Good | |
386 | /// x.map_or_else(some_function, |a| a + 1); | |
387 | /// ``` | |
a2a8927a | 388 | #[clippy::version = "1.45.0"] |
f20569fa XL |
389 | pub MAP_UNWRAP_OR, |
390 | pedantic, | |
391 | "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" | |
392 | } | |
393 | ||
394 | declare_clippy_lint! { | |
94222f64 XL |
395 | /// ### What it does |
396 | /// Checks for usage of `_.map_or(None, _)`. | |
f20569fa | 397 | /// |
94222f64 XL |
398 | /// ### Why is this bad? |
399 | /// Readability, this can be written more concisely as | |
f20569fa XL |
400 | /// `_.and_then(_)`. |
401 | /// | |
94222f64 XL |
402 | /// ### Known problems |
403 | /// The order of the arguments is not in execution order. | |
f20569fa | 404 | /// |
94222f64 | 405 | /// ### Example |
f20569fa XL |
406 | /// ```rust |
407 | /// # let opt = Some(1); | |
408 | /// | |
409 | /// // Bad | |
410 | /// opt.map_or(None, |a| Some(a + 1)); | |
411 | /// | |
412 | /// // Good | |
413 | /// opt.and_then(|a| Some(a + 1)); | |
414 | /// ``` | |
a2a8927a | 415 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
416 | pub OPTION_MAP_OR_NONE, |
417 | style, | |
418 | "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`" | |
419 | } | |
420 | ||
421 | declare_clippy_lint! { | |
94222f64 XL |
422 | /// ### What it does |
423 | /// Checks for usage of `_.map_or(None, Some)`. | |
f20569fa | 424 | /// |
94222f64 XL |
425 | /// ### Why is this bad? |
426 | /// Readability, this can be written more concisely as | |
f20569fa XL |
427 | /// `_.ok()`. |
428 | /// | |
94222f64 | 429 | /// ### Example |
f20569fa XL |
430 | /// Bad: |
431 | /// ```rust | |
432 | /// # let r: Result<u32, &str> = Ok(1); | |
433 | /// assert_eq!(Some(1), r.map_or(None, Some)); | |
434 | /// ``` | |
435 | /// | |
436 | /// Good: | |
437 | /// ```rust | |
438 | /// # let r: Result<u32, &str> = Ok(1); | |
439 | /// assert_eq!(Some(1), r.ok()); | |
440 | /// ``` | |
a2a8927a | 441 | #[clippy::version = "1.44.0"] |
f20569fa XL |
442 | pub RESULT_MAP_OR_INTO_OPTION, |
443 | style, | |
444 | "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`" | |
445 | } | |
446 | ||
447 | declare_clippy_lint! { | |
94222f64 XL |
448 | /// ### What it does |
449 | /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or | |
f20569fa XL |
450 | /// `_.or_else(|x| Err(y))`. |
451 | /// | |
94222f64 XL |
452 | /// ### Why is this bad? |
453 | /// Readability, this can be written more concisely as | |
f20569fa XL |
454 | /// `_.map(|x| y)` or `_.map_err(|x| y)`. |
455 | /// | |
94222f64 | 456 | /// ### Example |
f20569fa XL |
457 | /// ```rust |
458 | /// # fn opt() -> Option<&'static str> { Some("42") } | |
459 | /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } | |
460 | /// let _ = opt().and_then(|s| Some(s.len())); | |
461 | /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); | |
462 | /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); | |
463 | /// ``` | |
464 | /// | |
465 | /// The correct use would be: | |
466 | /// | |
467 | /// ```rust | |
468 | /// # fn opt() -> Option<&'static str> { Some("42") } | |
469 | /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } | |
470 | /// let _ = opt().map(|s| s.len()); | |
471 | /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); | |
472 | /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); | |
473 | /// ``` | |
a2a8927a | 474 | #[clippy::version = "1.45.0"] |
f20569fa XL |
475 | pub BIND_INSTEAD_OF_MAP, |
476 | complexity, | |
477 | "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" | |
478 | } | |
479 | ||
480 | declare_clippy_lint! { | |
94222f64 XL |
481 | /// ### What it does |
482 | /// Checks for usage of `_.filter(_).next()`. | |
f20569fa | 483 | /// |
94222f64 XL |
484 | /// ### Why is this bad? |
485 | /// Readability, this can be written more concisely as | |
f20569fa XL |
486 | /// `_.find(_)`. |
487 | /// | |
94222f64 | 488 | /// ### Example |
f20569fa XL |
489 | /// ```rust |
490 | /// # let vec = vec![1]; | |
491 | /// vec.iter().filter(|x| **x == 0).next(); | |
492 | /// ``` | |
493 | /// Could be written as | |
494 | /// ```rust | |
495 | /// # let vec = vec![1]; | |
496 | /// vec.iter().find(|x| **x == 0); | |
497 | /// ``` | |
a2a8927a | 498 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
499 | pub FILTER_NEXT, |
500 | complexity, | |
501 | "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`" | |
502 | } | |
503 | ||
504 | declare_clippy_lint! { | |
94222f64 XL |
505 | /// ### What it does |
506 | /// Checks for usage of `_.skip_while(condition).next()`. | |
f20569fa | 507 | /// |
94222f64 XL |
508 | /// ### Why is this bad? |
509 | /// Readability, this can be written more concisely as | |
f20569fa XL |
510 | /// `_.find(!condition)`. |
511 | /// | |
94222f64 | 512 | /// ### Example |
f20569fa XL |
513 | /// ```rust |
514 | /// # let vec = vec![1]; | |
515 | /// vec.iter().skip_while(|x| **x == 0).next(); | |
516 | /// ``` | |
517 | /// Could be written as | |
518 | /// ```rust | |
519 | /// # let vec = vec![1]; | |
520 | /// vec.iter().find(|x| **x != 0); | |
521 | /// ``` | |
a2a8927a | 522 | #[clippy::version = "1.42.0"] |
f20569fa XL |
523 | pub SKIP_WHILE_NEXT, |
524 | complexity, | |
525 | "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`" | |
526 | } | |
527 | ||
528 | declare_clippy_lint! { | |
94222f64 XL |
529 | /// ### What it does |
530 | /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option` | |
f20569fa | 531 | /// |
94222f64 XL |
532 | /// ### Why is this bad? |
533 | /// Readability, this can be written more concisely as | |
f20569fa XL |
534 | /// `_.flat_map(_)` |
535 | /// | |
94222f64 | 536 | /// ### Example |
f20569fa XL |
537 | /// ```rust |
538 | /// let vec = vec![vec![1]]; | |
539 | /// | |
540 | /// // Bad | |
541 | /// vec.iter().map(|x| x.iter()).flatten(); | |
542 | /// | |
543 | /// // Good | |
544 | /// vec.iter().flat_map(|x| x.iter()); | |
545 | /// ``` | |
a2a8927a | 546 | #[clippy::version = "1.31.0"] |
f20569fa | 547 | pub MAP_FLATTEN, |
a2a8927a | 548 | complexity, |
f20569fa XL |
549 | "using combinations of `flatten` and `map` which can usually be written as a single method call" |
550 | } | |
551 | ||
f20569fa | 552 | declare_clippy_lint! { |
94222f64 XL |
553 | /// ### What it does |
554 | /// Checks for usage of `_.filter(_).map(_)` that can be written more simply | |
f20569fa XL |
555 | /// as `filter_map(_)`. |
556 | /// | |
94222f64 XL |
557 | /// ### Why is this bad? |
558 | /// Redundant code in the `filter` and `map` operations is poor style and | |
f20569fa XL |
559 | /// less performant. |
560 | /// | |
94222f64 | 561 | /// ### Example |
f20569fa XL |
562 | /// Bad: |
563 | /// ```rust | |
564 | /// (0_i32..10) | |
565 | /// .filter(|n| n.checked_add(1).is_some()) | |
566 | /// .map(|n| n.checked_add(1).unwrap()); | |
567 | /// ``` | |
568 | /// | |
569 | /// Good: | |
570 | /// ```rust | |
571 | /// (0_i32..10).filter_map(|n| n.checked_add(1)); | |
572 | /// ``` | |
a2a8927a | 573 | #[clippy::version = "1.51.0"] |
f20569fa XL |
574 | pub MANUAL_FILTER_MAP, |
575 | complexity, | |
576 | "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`" | |
577 | } | |
578 | ||
579 | declare_clippy_lint! { | |
94222f64 XL |
580 | /// ### What it does |
581 | /// Checks for usage of `_.find(_).map(_)` that can be written more simply | |
f20569fa XL |
582 | /// as `find_map(_)`. |
583 | /// | |
94222f64 XL |
584 | /// ### Why is this bad? |
585 | /// Redundant code in the `find` and `map` operations is poor style and | |
f20569fa XL |
586 | /// less performant. |
587 | /// | |
94222f64 | 588 | /// ### Example |
f20569fa XL |
589 | /// Bad: |
590 | /// ```rust | |
591 | /// (0_i32..10) | |
592 | /// .find(|n| n.checked_add(1).is_some()) | |
593 | /// .map(|n| n.checked_add(1).unwrap()); | |
594 | /// ``` | |
595 | /// | |
596 | /// Good: | |
597 | /// ```rust | |
598 | /// (0_i32..10).find_map(|n| n.checked_add(1)); | |
599 | /// ``` | |
a2a8927a | 600 | #[clippy::version = "1.51.0"] |
f20569fa XL |
601 | pub MANUAL_FIND_MAP, |
602 | complexity, | |
603 | "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`" | |
604 | } | |
605 | ||
606 | declare_clippy_lint! { | |
94222f64 XL |
607 | /// ### What it does |
608 | /// Checks for usage of `_.filter_map(_).next()`. | |
f20569fa | 609 | /// |
94222f64 XL |
610 | /// ### Why is this bad? |
611 | /// Readability, this can be written more concisely as | |
f20569fa XL |
612 | /// `_.find_map(_)`. |
613 | /// | |
94222f64 | 614 | /// ### Example |
f20569fa XL |
615 | /// ```rust |
616 | /// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next(); | |
617 | /// ``` | |
618 | /// Can be written as | |
619 | /// | |
620 | /// ```rust | |
621 | /// (0..3).find_map(|x| if x == 2 { Some(x) } else { None }); | |
622 | /// ``` | |
a2a8927a | 623 | #[clippy::version = "1.36.0"] |
f20569fa XL |
624 | pub FILTER_MAP_NEXT, |
625 | pedantic, | |
626 | "using combination of `filter_map` and `next` which can usually be written as a single method call" | |
627 | } | |
628 | ||
629 | declare_clippy_lint! { | |
94222f64 XL |
630 | /// ### What it does |
631 | /// Checks for usage of `flat_map(|x| x)`. | |
f20569fa | 632 | /// |
94222f64 XL |
633 | /// ### Why is this bad? |
634 | /// Readability, this can be written more concisely by using `flatten`. | |
f20569fa | 635 | /// |
94222f64 | 636 | /// ### Example |
f20569fa XL |
637 | /// ```rust |
638 | /// # let iter = vec![vec![0]].into_iter(); | |
639 | /// iter.flat_map(|x| x); | |
640 | /// ``` | |
641 | /// Can be written as | |
642 | /// ```rust | |
643 | /// # let iter = vec![vec![0]].into_iter(); | |
644 | /// iter.flatten(); | |
645 | /// ``` | |
a2a8927a | 646 | #[clippy::version = "1.39.0"] |
f20569fa XL |
647 | pub FLAT_MAP_IDENTITY, |
648 | complexity, | |
649 | "call to `flat_map` where `flatten` is sufficient" | |
650 | } | |
651 | ||
652 | declare_clippy_lint! { | |
94222f64 XL |
653 | /// ### What it does |
654 | /// Checks for an iterator or string search (such as `find()`, | |
cdc7bbd5 | 655 | /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. |
f20569fa | 656 | /// |
94222f64 XL |
657 | /// ### Why is this bad? |
658 | /// Readability, this can be written more concisely as: | |
cdc7bbd5 XL |
659 | /// * `_.any(_)`, or `_.contains(_)` for `is_some()`, |
660 | /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`. | |
f20569fa | 661 | /// |
94222f64 | 662 | /// ### Example |
f20569fa | 663 | /// ```rust |
cdc7bbd5 | 664 | /// let vec = vec![1]; |
f20569fa | 665 | /// vec.iter().find(|x| **x == 0).is_some(); |
cdc7bbd5 XL |
666 | /// |
667 | /// let _ = "hello world".find("world").is_none(); | |
f20569fa XL |
668 | /// ``` |
669 | /// Could be written as | |
670 | /// ```rust | |
cdc7bbd5 | 671 | /// let vec = vec![1]; |
f20569fa | 672 | /// vec.iter().any(|x| *x == 0); |
cdc7bbd5 XL |
673 | /// |
674 | /// let _ = !"hello world".contains("world"); | |
f20569fa | 675 | /// ``` |
a2a8927a | 676 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
677 | pub SEARCH_IS_SOME, |
678 | complexity, | |
cdc7bbd5 | 679 | "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)" |
f20569fa XL |
680 | } |
681 | ||
682 | declare_clippy_lint! { | |
94222f64 XL |
683 | /// ### What it does |
684 | /// Checks for usage of `.chars().next()` on a `str` to check | |
f20569fa XL |
685 | /// if it starts with a given char. |
686 | /// | |
94222f64 XL |
687 | /// ### Why is this bad? |
688 | /// Readability, this can be written more concisely as | |
f20569fa XL |
689 | /// `_.starts_with(_)`. |
690 | /// | |
94222f64 | 691 | /// ### Example |
f20569fa XL |
692 | /// ```rust |
693 | /// let name = "foo"; | |
694 | /// if name.chars().next() == Some('_') {}; | |
695 | /// ``` | |
696 | /// Could be written as | |
697 | /// ```rust | |
698 | /// let name = "foo"; | |
699 | /// if name.starts_with('_') {}; | |
700 | /// ``` | |
a2a8927a | 701 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
702 | pub CHARS_NEXT_CMP, |
703 | style, | |
704 | "using `.chars().next()` to check if a string starts with a char" | |
705 | } | |
706 | ||
707 | declare_clippy_lint! { | |
94222f64 XL |
708 | /// ### What it does |
709 | /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, | |
f20569fa XL |
710 | /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or |
711 | /// `unwrap_or_default` instead. | |
712 | /// | |
94222f64 XL |
713 | /// ### Why is this bad? |
714 | /// The function will always be called and potentially | |
f20569fa XL |
715 | /// allocate an object acting as the default. |
716 | /// | |
94222f64 XL |
717 | /// ### Known problems |
718 | /// If the function has side-effects, not calling it will | |
f20569fa XL |
719 | /// change the semantic of the program, but you shouldn't rely on that anyway. |
720 | /// | |
94222f64 | 721 | /// ### Example |
f20569fa XL |
722 | /// ```rust |
723 | /// # let foo = Some(String::new()); | |
724 | /// foo.unwrap_or(String::new()); | |
725 | /// ``` | |
726 | /// this can instead be written: | |
727 | /// ```rust | |
728 | /// # let foo = Some(String::new()); | |
729 | /// foo.unwrap_or_else(String::new); | |
730 | /// ``` | |
731 | /// or | |
732 | /// ```rust | |
733 | /// # let foo = Some(String::new()); | |
734 | /// foo.unwrap_or_default(); | |
735 | /// ``` | |
a2a8927a | 736 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
737 | pub OR_FUN_CALL, |
738 | perf, | |
739 | "using any `*or` method with a function call, which suggests `*or_else`" | |
740 | } | |
741 | ||
742 | declare_clippy_lint! { | |
94222f64 XL |
743 | /// ### What it does |
744 | /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, | |
f20569fa XL |
745 | /// etc., and suggests to use `unwrap_or_else` instead |
746 | /// | |
94222f64 XL |
747 | /// ### Why is this bad? |
748 | /// The function will always be called. | |
f20569fa | 749 | /// |
94222f64 XL |
750 | /// ### Known problems |
751 | /// If the function has side-effects, not calling it will | |
f20569fa XL |
752 | /// change the semantics of the program, but you shouldn't rely on that anyway. |
753 | /// | |
94222f64 | 754 | /// ### Example |
f20569fa XL |
755 | /// ```rust |
756 | /// # let foo = Some(String::new()); | |
757 | /// # let err_code = "418"; | |
758 | /// # let err_msg = "I'm a teapot"; | |
759 | /// foo.expect(&format!("Err {}: {}", err_code, err_msg)); | |
760 | /// ``` | |
761 | /// or | |
762 | /// ```rust | |
763 | /// # let foo = Some(String::new()); | |
764 | /// # let err_code = "418"; | |
765 | /// # let err_msg = "I'm a teapot"; | |
766 | /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); | |
767 | /// ``` | |
768 | /// this can instead be written: | |
769 | /// ```rust | |
770 | /// # let foo = Some(String::new()); | |
771 | /// # let err_code = "418"; | |
772 | /// # let err_msg = "I'm a teapot"; | |
773 | /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg)); | |
774 | /// ``` | |
a2a8927a | 775 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
776 | pub EXPECT_FUN_CALL, |
777 | perf, | |
778 | "using any `expect` method with a function call" | |
779 | } | |
780 | ||
781 | declare_clippy_lint! { | |
94222f64 XL |
782 | /// ### What it does |
783 | /// Checks for usage of `.clone()` on a `Copy` type. | |
f20569fa | 784 | /// |
94222f64 XL |
785 | /// ### Why is this bad? |
786 | /// The only reason `Copy` types implement `Clone` is for | |
f20569fa XL |
787 | /// generics, not for using the `clone` method on a concrete type. |
788 | /// | |
94222f64 | 789 | /// ### Example |
f20569fa XL |
790 | /// ```rust |
791 | /// 42u64.clone(); | |
792 | /// ``` | |
a2a8927a | 793 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
794 | pub CLONE_ON_COPY, |
795 | complexity, | |
796 | "using `clone` on a `Copy` type" | |
797 | } | |
798 | ||
799 | declare_clippy_lint! { | |
94222f64 XL |
800 | /// ### What it does |
801 | /// Checks for usage of `.clone()` on a ref-counted pointer, | |
f20569fa XL |
802 | /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified |
803 | /// function syntax instead (e.g., `Rc::clone(foo)`). | |
804 | /// | |
94222f64 XL |
805 | /// ### Why is this bad? |
806 | /// Calling '.clone()' on an Rc, Arc, or Weak | |
f20569fa XL |
807 | /// can obscure the fact that only the pointer is being cloned, not the underlying |
808 | /// data. | |
809 | /// | |
94222f64 | 810 | /// ### Example |
f20569fa XL |
811 | /// ```rust |
812 | /// # use std::rc::Rc; | |
813 | /// let x = Rc::new(1); | |
814 | /// | |
815 | /// // Bad | |
816 | /// x.clone(); | |
817 | /// | |
818 | /// // Good | |
819 | /// Rc::clone(&x); | |
820 | /// ``` | |
a2a8927a | 821 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
822 | pub CLONE_ON_REF_PTR, |
823 | restriction, | |
824 | "using 'clone' on a ref-counted pointer" | |
825 | } | |
826 | ||
827 | declare_clippy_lint! { | |
94222f64 XL |
828 | /// ### What it does |
829 | /// Checks for usage of `.clone()` on an `&&T`. | |
f20569fa | 830 | /// |
94222f64 XL |
831 | /// ### Why is this bad? |
832 | /// Cloning an `&&T` copies the inner `&T`, instead of | |
f20569fa XL |
833 | /// cloning the underlying `T`. |
834 | /// | |
94222f64 | 835 | /// ### Example |
f20569fa XL |
836 | /// ```rust |
837 | /// fn main() { | |
838 | /// let x = vec![1]; | |
839 | /// let y = &&x; | |
840 | /// let z = y.clone(); | |
841 | /// println!("{:p} {:p}", *y, z); // prints out the same pointer | |
842 | /// } | |
843 | /// ``` | |
a2a8927a | 844 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
845 | pub CLONE_DOUBLE_REF, |
846 | correctness, | |
847 | "using `clone` on `&&T`" | |
848 | } | |
849 | ||
850 | declare_clippy_lint! { | |
94222f64 XL |
851 | /// ### What it does |
852 | /// Checks for usage of `.to_string()` on an `&&T` where | |
f20569fa XL |
853 | /// `T` implements `ToString` directly (like `&&str` or `&&String`). |
854 | /// | |
94222f64 XL |
855 | /// ### Why is this bad? |
856 | /// This bypasses the specialized implementation of | |
f20569fa XL |
857 | /// `ToString` and instead goes through the more expensive string formatting |
858 | /// facilities. | |
859 | /// | |
94222f64 | 860 | /// ### Example |
f20569fa XL |
861 | /// ```rust |
862 | /// // Generic implementation for `T: Display` is used (slow) | |
863 | /// ["foo", "bar"].iter().map(|s| s.to_string()); | |
864 | /// | |
865 | /// // OK, the specialized impl is used | |
866 | /// ["foo", "bar"].iter().map(|&s| s.to_string()); | |
867 | /// ``` | |
a2a8927a | 868 | #[clippy::version = "1.40.0"] |
f20569fa XL |
869 | pub INEFFICIENT_TO_STRING, |
870 | pedantic, | |
871 | "using `to_string` on `&&T` where `T: ToString`" | |
872 | } | |
873 | ||
874 | declare_clippy_lint! { | |
94222f64 XL |
875 | /// ### What it does |
876 | /// Checks for `new` not returning a type that contains `Self`. | |
f20569fa | 877 | /// |
94222f64 XL |
878 | /// ### Why is this bad? |
879 | /// As a convention, `new` methods are used to make a new | |
f20569fa XL |
880 | /// instance of a type. |
881 | /// | |
94222f64 | 882 | /// ### Example |
f20569fa XL |
883 | /// In an impl block: |
884 | /// ```rust | |
885 | /// # struct Foo; | |
886 | /// # struct NotAFoo; | |
887 | /// impl Foo { | |
888 | /// fn new() -> NotAFoo { | |
889 | /// # NotAFoo | |
890 | /// } | |
891 | /// } | |
892 | /// ``` | |
893 | /// | |
894 | /// ```rust | |
895 | /// # struct Foo; | |
896 | /// struct Bar(Foo); | |
897 | /// impl Foo { | |
898 | /// // Bad. The type name must contain `Self` | |
899 | /// fn new() -> Bar { | |
900 | /// # Bar(Foo) | |
901 | /// } | |
902 | /// } | |
903 | /// ``` | |
904 | /// | |
905 | /// ```rust | |
906 | /// # struct Foo; | |
907 | /// # struct FooError; | |
908 | /// impl Foo { | |
909 | /// // Good. Return type contains `Self` | |
910 | /// fn new() -> Result<Foo, FooError> { | |
911 | /// # Ok(Foo) | |
912 | /// } | |
913 | /// } | |
914 | /// ``` | |
915 | /// | |
916 | /// Or in a trait definition: | |
917 | /// ```rust | |
918 | /// pub trait Trait { | |
919 | /// // Bad. The type name must contain `Self` | |
920 | /// fn new(); | |
921 | /// } | |
922 | /// ``` | |
923 | /// | |
924 | /// ```rust | |
925 | /// pub trait Trait { | |
926 | /// // Good. Return type contains `Self` | |
927 | /// fn new() -> Self; | |
928 | /// } | |
929 | /// ``` | |
a2a8927a | 930 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
931 | pub NEW_RET_NO_SELF, |
932 | style, | |
933 | "not returning type containing `Self` in a `new` method" | |
934 | } | |
935 | ||
936 | declare_clippy_lint! { | |
94222f64 XL |
937 | /// ### What it does |
938 | /// Checks for string methods that receive a single-character | |
f20569fa XL |
939 | /// `str` as an argument, e.g., `_.split("x")`. |
940 | /// | |
94222f64 XL |
941 | /// ### Why is this bad? |
942 | /// Performing these methods using a `char` is faster than | |
f20569fa XL |
943 | /// using a `str`. |
944 | /// | |
94222f64 XL |
945 | /// ### Known problems |
946 | /// Does not catch multi-byte unicode characters. | |
f20569fa | 947 | /// |
94222f64 | 948 | /// ### Example |
f20569fa XL |
949 | /// ```rust,ignore |
950 | /// // Bad | |
951 | /// _.split("x"); | |
952 | /// | |
953 | /// // Good | |
954 | /// _.split('x'); | |
a2a8927a | 955 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
956 | pub SINGLE_CHAR_PATTERN, |
957 | perf, | |
958 | "using a single-character str where a char could be used, e.g., `_.split(\"x\")`" | |
959 | } | |
960 | ||
961 | declare_clippy_lint! { | |
94222f64 XL |
962 | /// ### What it does |
963 | /// Checks for calling `.step_by(0)` on iterators which panics. | |
f20569fa | 964 | /// |
94222f64 XL |
965 | /// ### Why is this bad? |
966 | /// This very much looks like an oversight. Use `panic!()` instead if you | |
f20569fa XL |
967 | /// actually intend to panic. |
968 | /// | |
94222f64 | 969 | /// ### Example |
f20569fa XL |
970 | /// ```rust,should_panic |
971 | /// for x in (0..100).step_by(0) { | |
972 | /// //.. | |
973 | /// } | |
974 | /// ``` | |
a2a8927a | 975 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
976 | pub ITERATOR_STEP_BY_ZERO, |
977 | correctness, | |
978 | "using `Iterator::step_by(0)`, which will panic at runtime" | |
979 | } | |
980 | ||
cdc7bbd5 | 981 | declare_clippy_lint! { |
94222f64 XL |
982 | /// ### What it does |
983 | /// Checks for indirect collection of populated `Option` | |
cdc7bbd5 | 984 | /// |
94222f64 XL |
985 | /// ### Why is this bad? |
986 | /// `Option` is like a collection of 0-1 things, so `flatten` | |
cdc7bbd5 XL |
987 | /// automatically does this without suspicious-looking `unwrap` calls. |
988 | /// | |
94222f64 | 989 | /// ### Example |
cdc7bbd5 XL |
990 | /// ```rust |
991 | /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap); | |
992 | /// ``` | |
993 | /// Use instead: | |
994 | /// ```rust | |
995 | /// let _ = std::iter::empty::<Option<i32>>().flatten(); | |
996 | /// ``` | |
a2a8927a | 997 | #[clippy::version = "1.53.0"] |
cdc7bbd5 XL |
998 | pub OPTION_FILTER_MAP, |
999 | complexity, | |
1000 | "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation" | |
1001 | } | |
1002 | ||
f20569fa | 1003 | declare_clippy_lint! { |
94222f64 XL |
1004 | /// ### What it does |
1005 | /// Checks for the use of `iter.nth(0)`. | |
f20569fa | 1006 | /// |
94222f64 XL |
1007 | /// ### Why is this bad? |
1008 | /// `iter.next()` is equivalent to | |
f20569fa XL |
1009 | /// `iter.nth(0)`, as they both consume the next element, |
1010 | /// but is more readable. | |
1011 | /// | |
94222f64 | 1012 | /// ### Example |
f20569fa XL |
1013 | /// ```rust |
1014 | /// # use std::collections::HashSet; | |
1015 | /// // Bad | |
1016 | /// # let mut s = HashSet::new(); | |
1017 | /// # s.insert(1); | |
1018 | /// let x = s.iter().nth(0); | |
1019 | /// | |
1020 | /// // Good | |
1021 | /// # let mut s = HashSet::new(); | |
1022 | /// # s.insert(1); | |
1023 | /// let x = s.iter().next(); | |
1024 | /// ``` | |
a2a8927a | 1025 | #[clippy::version = "1.42.0"] |
f20569fa XL |
1026 | pub ITER_NTH_ZERO, |
1027 | style, | |
1028 | "replace `iter.nth(0)` with `iter.next()`" | |
1029 | } | |
1030 | ||
1031 | declare_clippy_lint! { | |
94222f64 XL |
1032 | /// ### What it does |
1033 | /// Checks for use of `.iter().nth()` (and the related | |
c295e0f8 | 1034 | /// `.iter_mut().nth()`) on standard library types with *O*(1) element access. |
f20569fa | 1035 | /// |
94222f64 XL |
1036 | /// ### Why is this bad? |
1037 | /// `.get()` and `.get_mut()` are more efficient and more | |
f20569fa XL |
1038 | /// readable. |
1039 | /// | |
94222f64 | 1040 | /// ### Example |
f20569fa XL |
1041 | /// ```rust |
1042 | /// let some_vec = vec![0, 1, 2, 3]; | |
1043 | /// let bad_vec = some_vec.iter().nth(3); | |
1044 | /// let bad_slice = &some_vec[..].iter().nth(3); | |
1045 | /// ``` | |
1046 | /// The correct use would be: | |
1047 | /// ```rust | |
1048 | /// let some_vec = vec![0, 1, 2, 3]; | |
1049 | /// let bad_vec = some_vec.get(3); | |
1050 | /// let bad_slice = &some_vec[..].get(3); | |
1051 | /// ``` | |
a2a8927a | 1052 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1053 | pub ITER_NTH, |
1054 | perf, | |
1055 | "using `.iter().nth()` on a standard library type with O(1) element access" | |
1056 | } | |
1057 | ||
1058 | declare_clippy_lint! { | |
94222f64 XL |
1059 | /// ### What it does |
1060 | /// Checks for use of `.skip(x).next()` on iterators. | |
f20569fa | 1061 | /// |
94222f64 XL |
1062 | /// ### Why is this bad? |
1063 | /// `.nth(x)` is cleaner | |
f20569fa | 1064 | /// |
94222f64 | 1065 | /// ### Example |
f20569fa XL |
1066 | /// ```rust |
1067 | /// let some_vec = vec![0, 1, 2, 3]; | |
1068 | /// let bad_vec = some_vec.iter().skip(3).next(); | |
1069 | /// let bad_slice = &some_vec[..].iter().skip(3).next(); | |
1070 | /// ``` | |
1071 | /// The correct use would be: | |
1072 | /// ```rust | |
1073 | /// let some_vec = vec![0, 1, 2, 3]; | |
1074 | /// let bad_vec = some_vec.iter().nth(3); | |
1075 | /// let bad_slice = &some_vec[..].iter().nth(3); | |
1076 | /// ``` | |
a2a8927a | 1077 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1078 | pub ITER_SKIP_NEXT, |
1079 | style, | |
1080 | "using `.skip(x).next()` on an iterator" | |
1081 | } | |
1082 | ||
1083 | declare_clippy_lint! { | |
94222f64 XL |
1084 | /// ### What it does |
1085 | /// Checks for use of `.get().unwrap()` (or | |
f20569fa XL |
1086 | /// `.get_mut().unwrap`) on a standard library type which implements `Index` |
1087 | /// | |
94222f64 XL |
1088 | /// ### Why is this bad? |
1089 | /// Using the Index trait (`[]`) is more clear and more | |
f20569fa XL |
1090 | /// concise. |
1091 | /// | |
94222f64 XL |
1092 | /// ### Known problems |
1093 | /// Not a replacement for error handling: Using either | |
f20569fa XL |
1094 | /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic` |
1095 | /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a | |
1096 | /// temporary placeholder for dealing with the `Option` type, then this does | |
1097 | /// not mitigate the need for error handling. If there is a chance that `.get()` | |
1098 | /// will be `None` in your program, then it is advisable that the `None` case | |
1099 | /// is handled in a future refactor instead of using `.unwrap()` or the Index | |
1100 | /// trait. | |
1101 | /// | |
94222f64 | 1102 | /// ### Example |
f20569fa XL |
1103 | /// ```rust |
1104 | /// let mut some_vec = vec![0, 1, 2, 3]; | |
1105 | /// let last = some_vec.get(3).unwrap(); | |
1106 | /// *some_vec.get_mut(0).unwrap() = 1; | |
1107 | /// ``` | |
1108 | /// The correct use would be: | |
1109 | /// ```rust | |
1110 | /// let mut some_vec = vec![0, 1, 2, 3]; | |
1111 | /// let last = some_vec[3]; | |
1112 | /// some_vec[0] = 1; | |
1113 | /// ``` | |
a2a8927a | 1114 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1115 | pub GET_UNWRAP, |
1116 | restriction, | |
1117 | "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead" | |
1118 | } | |
1119 | ||
136023e0 | 1120 | declare_clippy_lint! { |
94222f64 XL |
1121 | /// ### What it does |
1122 | /// Checks for occurrences where one vector gets extended instead of append | |
136023e0 | 1123 | /// |
94222f64 XL |
1124 | /// ### Why is this bad? |
1125 | /// Using `append` instead of `extend` is more concise and faster | |
136023e0 | 1126 | /// |
94222f64 | 1127 | /// ### Example |
136023e0 XL |
1128 | /// ```rust |
1129 | /// let mut a = vec![1, 2, 3]; | |
1130 | /// let mut b = vec![4, 5, 6]; | |
1131 | /// | |
1132 | /// // Bad | |
1133 | /// a.extend(b.drain(..)); | |
1134 | /// | |
1135 | /// // Good | |
1136 | /// a.append(&mut b); | |
1137 | /// ``` | |
a2a8927a | 1138 | #[clippy::version = "1.55.0"] |
136023e0 XL |
1139 | pub EXTEND_WITH_DRAIN, |
1140 | perf, | |
1141 | "using vec.append(&mut vec) to move the full range of a vecor to another" | |
1142 | } | |
1143 | ||
f20569fa | 1144 | declare_clippy_lint! { |
94222f64 XL |
1145 | /// ### What it does |
1146 | /// Checks for the use of `.extend(s.chars())` where s is a | |
f20569fa XL |
1147 | /// `&str` or `String`. |
1148 | /// | |
94222f64 XL |
1149 | /// ### Why is this bad? |
1150 | /// `.push_str(s)` is clearer | |
f20569fa | 1151 | /// |
94222f64 | 1152 | /// ### Example |
f20569fa XL |
1153 | /// ```rust |
1154 | /// let abc = "abc"; | |
1155 | /// let def = String::from("def"); | |
1156 | /// let mut s = String::new(); | |
1157 | /// s.extend(abc.chars()); | |
1158 | /// s.extend(def.chars()); | |
1159 | /// ``` | |
1160 | /// The correct use would be: | |
1161 | /// ```rust | |
1162 | /// let abc = "abc"; | |
1163 | /// let def = String::from("def"); | |
1164 | /// let mut s = String::new(); | |
1165 | /// s.push_str(abc); | |
1166 | /// s.push_str(&def); | |
1167 | /// ``` | |
a2a8927a | 1168 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1169 | pub STRING_EXTEND_CHARS, |
1170 | style, | |
1171 | "using `x.extend(s.chars())` where s is a `&str` or `String`" | |
1172 | } | |
1173 | ||
1174 | declare_clippy_lint! { | |
94222f64 XL |
1175 | /// ### What it does |
1176 | /// Checks for the use of `.cloned().collect()` on slice to | |
f20569fa XL |
1177 | /// create a `Vec`. |
1178 | /// | |
94222f64 XL |
1179 | /// ### Why is this bad? |
1180 | /// `.to_vec()` is clearer | |
f20569fa | 1181 | /// |
94222f64 | 1182 | /// ### Example |
f20569fa XL |
1183 | /// ```rust |
1184 | /// let s = [1, 2, 3, 4, 5]; | |
1185 | /// let s2: Vec<isize> = s[..].iter().cloned().collect(); | |
1186 | /// ``` | |
1187 | /// The better use would be: | |
1188 | /// ```rust | |
1189 | /// let s = [1, 2, 3, 4, 5]; | |
1190 | /// let s2: Vec<isize> = s.to_vec(); | |
1191 | /// ``` | |
a2a8927a | 1192 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1193 | pub ITER_CLONED_COLLECT, |
1194 | style, | |
1195 | "using `.cloned().collect()` on slice to create a `Vec`" | |
1196 | } | |
1197 | ||
1198 | declare_clippy_lint! { | |
94222f64 XL |
1199 | /// ### What it does |
1200 | /// Checks for usage of `_.chars().last()` or | |
f20569fa XL |
1201 | /// `_.chars().next_back()` on a `str` to check if it ends with a given char. |
1202 | /// | |
94222f64 XL |
1203 | /// ### Why is this bad? |
1204 | /// Readability, this can be written more concisely as | |
f20569fa XL |
1205 | /// `_.ends_with(_)`. |
1206 | /// | |
94222f64 | 1207 | /// ### Example |
f20569fa XL |
1208 | /// ```rust |
1209 | /// # let name = "_"; | |
1210 | /// | |
1211 | /// // Bad | |
1212 | /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); | |
1213 | /// | |
1214 | /// // Good | |
1215 | /// name.ends_with('_') || name.ends_with('-'); | |
1216 | /// ``` | |
a2a8927a | 1217 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1218 | pub CHARS_LAST_CMP, |
1219 | style, | |
1220 | "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char" | |
1221 | } | |
1222 | ||
1223 | declare_clippy_lint! { | |
94222f64 XL |
1224 | /// ### What it does |
1225 | /// Checks for usage of `.as_ref()` or `.as_mut()` where the | |
f20569fa XL |
1226 | /// types before and after the call are the same. |
1227 | /// | |
94222f64 XL |
1228 | /// ### Why is this bad? |
1229 | /// The call is unnecessary. | |
f20569fa | 1230 | /// |
94222f64 | 1231 | /// ### Example |
f20569fa XL |
1232 | /// ```rust |
1233 | /// # fn do_stuff(x: &[i32]) {} | |
1234 | /// let x: &[i32] = &[1, 2, 3, 4, 5]; | |
1235 | /// do_stuff(x.as_ref()); | |
1236 | /// ``` | |
1237 | /// The correct use would be: | |
1238 | /// ```rust | |
1239 | /// # fn do_stuff(x: &[i32]) {} | |
1240 | /// let x: &[i32] = &[1, 2, 3, 4, 5]; | |
1241 | /// do_stuff(x); | |
1242 | /// ``` | |
a2a8927a | 1243 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1244 | pub USELESS_ASREF, |
1245 | complexity, | |
1246 | "using `as_ref` where the types before and after the call are the same" | |
1247 | } | |
1248 | ||
1249 | declare_clippy_lint! { | |
94222f64 XL |
1250 | /// ### What it does |
1251 | /// Checks for using `fold` when a more succinct alternative exists. | |
f20569fa XL |
1252 | /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`, |
1253 | /// `sum` or `product`. | |
1254 | /// | |
94222f64 XL |
1255 | /// ### Why is this bad? |
1256 | /// Readability. | |
f20569fa | 1257 | /// |
94222f64 | 1258 | /// ### Example |
f20569fa XL |
1259 | /// ```rust |
1260 | /// let _ = (0..3).fold(false, |acc, x| acc || x > 2); | |
1261 | /// ``` | |
1262 | /// This could be written as: | |
1263 | /// ```rust | |
1264 | /// let _ = (0..3).any(|x| x > 2); | |
1265 | /// ``` | |
a2a8927a | 1266 | #[clippy::version = "pre 1.29.0"] |
f20569fa XL |
1267 | pub UNNECESSARY_FOLD, |
1268 | style, | |
1269 | "using `fold` when a more succinct alternative exists" | |
1270 | } | |
1271 | ||
1272 | declare_clippy_lint! { | |
94222f64 XL |
1273 | /// ### What it does |
1274 | /// Checks for `filter_map` calls which could be replaced by `filter` or `map`. | |
f20569fa XL |
1275 | /// More specifically it checks if the closure provided is only performing one of the |
1276 | /// filter or map operations and suggests the appropriate option. | |
1277 | /// | |
94222f64 XL |
1278 | /// ### Why is this bad? |
1279 | /// Complexity. The intent is also clearer if only a single | |
f20569fa XL |
1280 | /// operation is being performed. |
1281 | /// | |
94222f64 | 1282 | /// ### Example |
f20569fa XL |
1283 | /// ```rust |
1284 | /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None }); | |
1285 | /// | |
1286 | /// // As there is no transformation of the argument this could be written as: | |
1287 | /// let _ = (0..3).filter(|&x| x > 2); | |
1288 | /// ``` | |
1289 | /// | |
1290 | /// ```rust | |
1291 | /// let _ = (0..4).filter_map(|x| Some(x + 1)); | |
1292 | /// | |
1293 | /// // As there is no conditional check on the argument this could be written as: | |
1294 | /// let _ = (0..4).map(|x| x + 1); | |
1295 | /// ``` | |
a2a8927a | 1296 | #[clippy::version = "1.31.0"] |
f20569fa XL |
1297 | pub UNNECESSARY_FILTER_MAP, |
1298 | complexity, | |
1299 | "using `filter_map` when a more succinct alternative exists" | |
1300 | } | |
1301 | ||
1302 | declare_clippy_lint! { | |
94222f64 XL |
1303 | /// ### What it does |
1304 | /// Checks for `into_iter` calls on references which should be replaced by `iter` | |
f20569fa XL |
1305 | /// or `iter_mut`. |
1306 | /// | |
94222f64 XL |
1307 | /// ### Why is this bad? |
1308 | /// Readability. Calling `into_iter` on a reference will not move out its | |
f20569fa XL |
1309 | /// content into the resulting iterator, which is confusing. It is better just call `iter` or |
1310 | /// `iter_mut` directly. | |
1311 | /// | |
94222f64 | 1312 | /// ### Example |
f20569fa XL |
1313 | /// ```rust |
1314 | /// // Bad | |
1315 | /// let _ = (&vec![3, 4, 5]).into_iter(); | |
1316 | /// | |
1317 | /// // Good | |
1318 | /// let _ = (&vec![3, 4, 5]).iter(); | |
1319 | /// ``` | |
a2a8927a | 1320 | #[clippy::version = "1.32.0"] |
f20569fa XL |
1321 | pub INTO_ITER_ON_REF, |
1322 | style, | |
1323 | "using `.into_iter()` on a reference" | |
1324 | } | |
1325 | ||
1326 | declare_clippy_lint! { | |
94222f64 XL |
1327 | /// ### What it does |
1328 | /// Checks for calls to `map` followed by a `count`. | |
f20569fa | 1329 | /// |
94222f64 XL |
1330 | /// ### Why is this bad? |
1331 | /// It looks suspicious. Maybe `map` was confused with `filter`. | |
c295e0f8 XL |
1332 | /// If the `map` call is intentional, this should be rewritten |
1333 | /// using `inspect`. Or, if you intend to drive the iterator to | |
1334 | /// completion, you can just use `for_each` instead. | |
f20569fa | 1335 | /// |
94222f64 | 1336 | /// ### Example |
f20569fa XL |
1337 | /// ```rust |
1338 | /// let _ = (0..3).map(|x| x + 2).count(); | |
1339 | /// ``` | |
a2a8927a | 1340 | #[clippy::version = "1.39.0"] |
f20569fa | 1341 | pub SUSPICIOUS_MAP, |
136023e0 | 1342 | suspicious, |
f20569fa XL |
1343 | "suspicious usage of map" |
1344 | } | |
1345 | ||
1346 | declare_clippy_lint! { | |
94222f64 XL |
1347 | /// ### What it does |
1348 | /// Checks for `MaybeUninit::uninit().assume_init()`. | |
f20569fa | 1349 | /// |
94222f64 XL |
1350 | /// ### Why is this bad? |
1351 | /// For most types, this is undefined behavior. | |
f20569fa | 1352 | /// |
94222f64 XL |
1353 | /// ### Known problems |
1354 | /// For now, we accept empty tuples and tuples / arrays | |
f20569fa XL |
1355 | /// of `MaybeUninit`. There may be other types that allow uninitialized |
1356 | /// data, but those are not yet rigorously defined. | |
1357 | /// | |
94222f64 | 1358 | /// ### Example |
f20569fa XL |
1359 | /// ```rust |
1360 | /// // Beware the UB | |
1361 | /// use std::mem::MaybeUninit; | |
1362 | /// | |
1363 | /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | |
1364 | /// ``` | |
1365 | /// | |
1366 | /// Note that the following is OK: | |
1367 | /// | |
1368 | /// ```rust | |
1369 | /// use std::mem::MaybeUninit; | |
1370 | /// | |
1371 | /// let _: [MaybeUninit<bool>; 5] = unsafe { | |
1372 | /// MaybeUninit::uninit().assume_init() | |
1373 | /// }; | |
1374 | /// ``` | |
a2a8927a | 1375 | #[clippy::version = "1.39.0"] |
f20569fa XL |
1376 | pub UNINIT_ASSUMED_INIT, |
1377 | correctness, | |
1378 | "`MaybeUninit::uninit().assume_init()`" | |
1379 | } | |
1380 | ||
1381 | declare_clippy_lint! { | |
94222f64 XL |
1382 | /// ### What it does |
1383 | /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`. | |
f20569fa | 1384 | /// |
94222f64 XL |
1385 | /// ### Why is this bad? |
1386 | /// These can be written simply with `saturating_add/sub` methods. | |
f20569fa | 1387 | /// |
94222f64 | 1388 | /// ### Example |
f20569fa XL |
1389 | /// ```rust |
1390 | /// # let y: u32 = 0; | |
1391 | /// # let x: u32 = 100; | |
1392 | /// let add = x.checked_add(y).unwrap_or(u32::MAX); | |
1393 | /// let sub = x.checked_sub(y).unwrap_or(u32::MIN); | |
1394 | /// ``` | |
1395 | /// | |
1396 | /// can be written using dedicated methods for saturating addition/subtraction as: | |
1397 | /// | |
1398 | /// ```rust | |
1399 | /// # let y: u32 = 0; | |
1400 | /// # let x: u32 = 100; | |
1401 | /// let add = x.saturating_add(y); | |
1402 | /// let sub = x.saturating_sub(y); | |
1403 | /// ``` | |
a2a8927a | 1404 | #[clippy::version = "1.39.0"] |
f20569fa XL |
1405 | pub MANUAL_SATURATING_ARITHMETIC, |
1406 | style, | |
1407 | "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" | |
1408 | } | |
1409 | ||
1410 | declare_clippy_lint! { | |
94222f64 XL |
1411 | /// ### What it does |
1412 | /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to | |
f20569fa XL |
1413 | /// zero-sized types |
1414 | /// | |
94222f64 XL |
1415 | /// ### Why is this bad? |
1416 | /// This is a no-op, and likely unintended | |
f20569fa | 1417 | /// |
94222f64 | 1418 | /// ### Example |
f20569fa XL |
1419 | /// ```rust |
1420 | /// unsafe { (&() as *const ()).offset(1) }; | |
1421 | /// ``` | |
a2a8927a | 1422 | #[clippy::version = "1.41.0"] |
f20569fa XL |
1423 | pub ZST_OFFSET, |
1424 | correctness, | |
1425 | "Check for offset calculations on raw pointers to zero-sized types" | |
1426 | } | |
1427 | ||
1428 | declare_clippy_lint! { | |
94222f64 XL |
1429 | /// ### What it does |
1430 | /// Checks for `FileType::is_file()`. | |
f20569fa | 1431 | /// |
94222f64 XL |
1432 | /// ### Why is this bad? |
1433 | /// When people testing a file type with `FileType::is_file` | |
f20569fa XL |
1434 | /// they are testing whether a path is something they can get bytes from. But |
1435 | /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover | |
1436 | /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention. | |
1437 | /// | |
94222f64 | 1438 | /// ### Example |
f20569fa XL |
1439 | /// ```rust |
1440 | /// # || { | |
1441 | /// let metadata = std::fs::metadata("foo.txt")?; | |
1442 | /// let filetype = metadata.file_type(); | |
1443 | /// | |
1444 | /// if filetype.is_file() { | |
1445 | /// // read file | |
1446 | /// } | |
1447 | /// # Ok::<_, std::io::Error>(()) | |
1448 | /// # }; | |
1449 | /// ``` | |
1450 | /// | |
1451 | /// should be written as: | |
1452 | /// | |
1453 | /// ```rust | |
1454 | /// # || { | |
1455 | /// let metadata = std::fs::metadata("foo.txt")?; | |
1456 | /// let filetype = metadata.file_type(); | |
1457 | /// | |
1458 | /// if !filetype.is_dir() { | |
1459 | /// // read file | |
1460 | /// } | |
1461 | /// # Ok::<_, std::io::Error>(()) | |
1462 | /// # }; | |
1463 | /// ``` | |
a2a8927a | 1464 | #[clippy::version = "1.42.0"] |
f20569fa XL |
1465 | pub FILETYPE_IS_FILE, |
1466 | restriction, | |
1467 | "`FileType::is_file` is not recommended to test for readable file type" | |
1468 | } | |
1469 | ||
1470 | declare_clippy_lint! { | |
94222f64 XL |
1471 | /// ### What it does |
1472 | /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). | |
f20569fa | 1473 | /// |
94222f64 XL |
1474 | /// ### Why is this bad? |
1475 | /// Readability, this can be written more concisely as | |
f20569fa XL |
1476 | /// `_.as_deref()`. |
1477 | /// | |
94222f64 | 1478 | /// ### Example |
f20569fa XL |
1479 | /// ```rust |
1480 | /// # let opt = Some("".to_string()); | |
1481 | /// opt.as_ref().map(String::as_str) | |
1482 | /// # ; | |
1483 | /// ``` | |
1484 | /// Can be written as | |
1485 | /// ```rust | |
1486 | /// # let opt = Some("".to_string()); | |
1487 | /// opt.as_deref() | |
1488 | /// # ; | |
1489 | /// ``` | |
a2a8927a | 1490 | #[clippy::version = "1.42.0"] |
f20569fa XL |
1491 | pub OPTION_AS_REF_DEREF, |
1492 | complexity, | |
1493 | "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`" | |
1494 | } | |
1495 | ||
1496 | declare_clippy_lint! { | |
94222f64 XL |
1497 | /// ### What it does |
1498 | /// Checks for usage of `iter().next()` on a Slice or an Array | |
f20569fa | 1499 | /// |
94222f64 XL |
1500 | /// ### Why is this bad? |
1501 | /// These can be shortened into `.get()` | |
f20569fa | 1502 | /// |
94222f64 | 1503 | /// ### Example |
f20569fa XL |
1504 | /// ```rust |
1505 | /// # let a = [1, 2, 3]; | |
1506 | /// # let b = vec![1, 2, 3]; | |
1507 | /// a[2..].iter().next(); | |
1508 | /// b.iter().next(); | |
1509 | /// ``` | |
1510 | /// should be written as: | |
1511 | /// ```rust | |
1512 | /// # let a = [1, 2, 3]; | |
1513 | /// # let b = vec![1, 2, 3]; | |
1514 | /// a.get(2); | |
1515 | /// b.get(0); | |
1516 | /// ``` | |
a2a8927a | 1517 | #[clippy::version = "1.46.0"] |
f20569fa XL |
1518 | pub ITER_NEXT_SLICE, |
1519 | style, | |
1520 | "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" | |
1521 | } | |
1522 | ||
1523 | declare_clippy_lint! { | |
94222f64 XL |
1524 | /// ### What it does |
1525 | /// Warns when using `push_str`/`insert_str` with a single-character string literal | |
f20569fa XL |
1526 | /// where `push`/`insert` with a `char` would work fine. |
1527 | /// | |
94222f64 XL |
1528 | /// ### Why is this bad? |
1529 | /// It's less clear that we are pushing a single character. | |
f20569fa | 1530 | /// |
94222f64 | 1531 | /// ### Example |
f20569fa XL |
1532 | /// ```rust |
1533 | /// let mut string = String::new(); | |
1534 | /// string.insert_str(0, "R"); | |
1535 | /// string.push_str("R"); | |
1536 | /// ``` | |
1537 | /// Could be written as | |
1538 | /// ```rust | |
1539 | /// let mut string = String::new(); | |
1540 | /// string.insert(0, 'R'); | |
1541 | /// string.push('R'); | |
1542 | /// ``` | |
a2a8927a | 1543 | #[clippy::version = "1.49.0"] |
f20569fa XL |
1544 | pub SINGLE_CHAR_ADD_STR, |
1545 | style, | |
1546 | "`push_str()` or `insert_str()` used with a single-character string literal as parameter" | |
1547 | } | |
1548 | ||
1549 | declare_clippy_lint! { | |
94222f64 XL |
1550 | /// ### What it does |
1551 | /// As the counterpart to `or_fun_call`, this lint looks for unnecessary | |
f20569fa XL |
1552 | /// lazily evaluated closures on `Option` and `Result`. |
1553 | /// | |
1554 | /// This lint suggests changing the following functions, when eager evaluation results in | |
1555 | /// simpler code: | |
1556 | /// - `unwrap_or_else` to `unwrap_or` | |
1557 | /// - `and_then` to `and` | |
1558 | /// - `or_else` to `or` | |
1559 | /// - `get_or_insert_with` to `get_or_insert` | |
1560 | /// - `ok_or_else` to `ok_or` | |
1561 | /// | |
94222f64 XL |
1562 | /// ### Why is this bad? |
1563 | /// Using eager evaluation is shorter and simpler in some cases. | |
f20569fa | 1564 | /// |
94222f64 XL |
1565 | /// ### Known problems |
1566 | /// It is possible, but not recommended for `Deref` and `Index` to have | |
f20569fa XL |
1567 | /// side effects. Eagerly evaluating them can change the semantics of the program. |
1568 | /// | |
94222f64 | 1569 | /// ### Example |
f20569fa XL |
1570 | /// ```rust |
1571 | /// // example code where clippy issues a warning | |
1572 | /// let opt: Option<u32> = None; | |
1573 | /// | |
1574 | /// opt.unwrap_or_else(|| 42); | |
1575 | /// ``` | |
1576 | /// Use instead: | |
1577 | /// ```rust | |
1578 | /// let opt: Option<u32> = None; | |
1579 | /// | |
1580 | /// opt.unwrap_or(42); | |
1581 | /// ``` | |
a2a8927a | 1582 | #[clippy::version = "1.48.0"] |
f20569fa XL |
1583 | pub UNNECESSARY_LAZY_EVALUATIONS, |
1584 | style, | |
1585 | "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" | |
1586 | } | |
1587 | ||
1588 | declare_clippy_lint! { | |
94222f64 XL |
1589 | /// ### What it does |
1590 | /// Checks for usage of `_.map(_).collect::<Result<(), _>()`. | |
f20569fa | 1591 | /// |
94222f64 XL |
1592 | /// ### Why is this bad? |
1593 | /// Using `try_for_each` instead is more readable and idiomatic. | |
f20569fa | 1594 | /// |
94222f64 | 1595 | /// ### Example |
f20569fa XL |
1596 | /// ```rust |
1597 | /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>(); | |
1598 | /// ``` | |
1599 | /// Use instead: | |
1600 | /// ```rust | |
1601 | /// (0..3).try_for_each(|t| Err(t)); | |
1602 | /// ``` | |
a2a8927a | 1603 | #[clippy::version = "1.49.0"] |
f20569fa XL |
1604 | pub MAP_COLLECT_RESULT_UNIT, |
1605 | style, | |
1606 | "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`" | |
1607 | } | |
1608 | ||
1609 | declare_clippy_lint! { | |
94222f64 XL |
1610 | /// ### What it does |
1611 | /// Checks for `from_iter()` function calls on types that implement the `FromIterator` | |
f20569fa XL |
1612 | /// trait. |
1613 | /// | |
94222f64 XL |
1614 | /// ### Why is this bad? |
1615 | /// It is recommended style to use collect. See | |
f20569fa XL |
1616 | /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) |
1617 | /// | |
94222f64 | 1618 | /// ### Example |
f20569fa XL |
1619 | /// ```rust |
1620 | /// use std::iter::FromIterator; | |
1621 | /// | |
1622 | /// let five_fives = std::iter::repeat(5).take(5); | |
1623 | /// | |
1624 | /// let v = Vec::from_iter(five_fives); | |
1625 | /// | |
1626 | /// assert_eq!(v, vec![5, 5, 5, 5, 5]); | |
1627 | /// ``` | |
1628 | /// Use instead: | |
1629 | /// ```rust | |
1630 | /// let five_fives = std::iter::repeat(5).take(5); | |
1631 | /// | |
1632 | /// let v: Vec<i32> = five_fives.collect(); | |
1633 | /// | |
1634 | /// assert_eq!(v, vec![5, 5, 5, 5, 5]); | |
1635 | /// ``` | |
a2a8927a | 1636 | #[clippy::version = "1.49.0"] |
f20569fa | 1637 | pub FROM_ITER_INSTEAD_OF_COLLECT, |
136023e0 | 1638 | pedantic, |
f20569fa XL |
1639 | "use `.collect()` instead of `::from_iter()`" |
1640 | } | |
1641 | ||
1642 | declare_clippy_lint! { | |
94222f64 XL |
1643 | /// ### What it does |
1644 | /// Checks for usage of `inspect().for_each()`. | |
f20569fa | 1645 | /// |
94222f64 XL |
1646 | /// ### Why is this bad? |
1647 | /// It is the same as performing the computation | |
f20569fa XL |
1648 | /// inside `inspect` at the beginning of the closure in `for_each`. |
1649 | /// | |
94222f64 | 1650 | /// ### Example |
f20569fa XL |
1651 | /// ```rust |
1652 | /// [1,2,3,4,5].iter() | |
1653 | /// .inspect(|&x| println!("inspect the number: {}", x)) | |
1654 | /// .for_each(|&x| { | |
1655 | /// assert!(x >= 0); | |
1656 | /// }); | |
1657 | /// ``` | |
1658 | /// Can be written as | |
1659 | /// ```rust | |
1660 | /// [1,2,3,4,5].iter() | |
1661 | /// .for_each(|&x| { | |
1662 | /// println!("inspect the number: {}", x); | |
1663 | /// assert!(x >= 0); | |
1664 | /// }); | |
1665 | /// ``` | |
a2a8927a | 1666 | #[clippy::version = "1.51.0"] |
f20569fa XL |
1667 | pub INSPECT_FOR_EACH, |
1668 | complexity, | |
1669 | "using `.inspect().for_each()`, which can be replaced with `.for_each()`" | |
1670 | } | |
1671 | ||
1672 | declare_clippy_lint! { | |
94222f64 XL |
1673 | /// ### What it does |
1674 | /// Checks for usage of `filter_map(|x| x)`. | |
f20569fa | 1675 | /// |
94222f64 XL |
1676 | /// ### Why is this bad? |
1677 | /// Readability, this can be written more concisely by using `flatten`. | |
f20569fa | 1678 | /// |
94222f64 | 1679 | /// ### Example |
f20569fa XL |
1680 | /// ```rust |
1681 | /// # let iter = vec![Some(1)].into_iter(); | |
1682 | /// iter.filter_map(|x| x); | |
1683 | /// ``` | |
1684 | /// Use instead: | |
1685 | /// ```rust | |
1686 | /// # let iter = vec![Some(1)].into_iter(); | |
1687 | /// iter.flatten(); | |
1688 | /// ``` | |
a2a8927a | 1689 | #[clippy::version = "1.52.0"] |
f20569fa XL |
1690 | pub FILTER_MAP_IDENTITY, |
1691 | complexity, | |
1692 | "call to `filter_map` where `flatten` is sufficient" | |
1693 | } | |
1694 | ||
136023e0 | 1695 | declare_clippy_lint! { |
94222f64 XL |
1696 | /// ### What it does |
1697 | /// Checks for instances of `map(f)` where `f` is the identity function. | |
136023e0 | 1698 | /// |
94222f64 XL |
1699 | /// ### Why is this bad? |
1700 | /// It can be written more concisely without the call to `map`. | |
136023e0 | 1701 | /// |
94222f64 | 1702 | /// ### Example |
136023e0 XL |
1703 | /// ```rust |
1704 | /// let x = [1, 2, 3]; | |
1705 | /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect(); | |
1706 | /// ``` | |
1707 | /// Use instead: | |
1708 | /// ```rust | |
1709 | /// let x = [1, 2, 3]; | |
1710 | /// let y: Vec<_> = x.iter().map(|x| 2*x).collect(); | |
1711 | /// ``` | |
a2a8927a | 1712 | #[clippy::version = "1.52.0"] |
136023e0 XL |
1713 | pub MAP_IDENTITY, |
1714 | complexity, | |
1715 | "using iterator.map(|x| x)" | |
1716 | } | |
1717 | ||
f20569fa | 1718 | declare_clippy_lint! { |
94222f64 XL |
1719 | /// ### What it does |
1720 | /// Checks for the use of `.bytes().nth()`. | |
f20569fa | 1721 | /// |
94222f64 XL |
1722 | /// ### Why is this bad? |
1723 | /// `.as_bytes().get()` is more efficient and more | |
f20569fa XL |
1724 | /// readable. |
1725 | /// | |
94222f64 | 1726 | /// ### Example |
f20569fa XL |
1727 | /// ```rust |
1728 | /// // Bad | |
1729 | /// let _ = "Hello".bytes().nth(3); | |
1730 | /// | |
1731 | /// // Good | |
1732 | /// let _ = "Hello".as_bytes().get(3); | |
1733 | /// ``` | |
a2a8927a | 1734 | #[clippy::version = "1.52.0"] |
f20569fa XL |
1735 | pub BYTES_NTH, |
1736 | style, | |
1737 | "replace `.bytes().nth()` with `.as_bytes().get()`" | |
1738 | } | |
1739 | ||
1740 | declare_clippy_lint! { | |
94222f64 XL |
1741 | /// ### What it does |
1742 | /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer. | |
f20569fa | 1743 | /// |
94222f64 XL |
1744 | /// ### Why is this bad? |
1745 | /// These methods do the same thing as `_.clone()` but may be confusing as | |
f20569fa XL |
1746 | /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned. |
1747 | /// | |
94222f64 | 1748 | /// ### Example |
f20569fa XL |
1749 | /// ```rust |
1750 | /// let a = vec![1, 2, 3]; | |
1751 | /// let b = a.to_vec(); | |
1752 | /// let c = a.to_owned(); | |
1753 | /// ``` | |
1754 | /// Use instead: | |
1755 | /// ```rust | |
1756 | /// let a = vec![1, 2, 3]; | |
1757 | /// let b = a.clone(); | |
1758 | /// let c = a.clone(); | |
1759 | /// ``` | |
a2a8927a | 1760 | #[clippy::version = "1.52.0"] |
f20569fa XL |
1761 | pub IMPLICIT_CLONE, |
1762 | pedantic, | |
1763 | "implicitly cloning a value by invoking a function on its dereferenced type" | |
1764 | } | |
1765 | ||
1766 | declare_clippy_lint! { | |
94222f64 XL |
1767 | /// ### What it does |
1768 | /// Checks for the use of `.iter().count()`. | |
f20569fa | 1769 | /// |
94222f64 XL |
1770 | /// ### Why is this bad? |
1771 | /// `.len()` is more efficient and more | |
f20569fa XL |
1772 | /// readable. |
1773 | /// | |
94222f64 | 1774 | /// ### Example |
f20569fa XL |
1775 | /// ```rust |
1776 | /// // Bad | |
1777 | /// let some_vec = vec![0, 1, 2, 3]; | |
1778 | /// let _ = some_vec.iter().count(); | |
1779 | /// let _ = &some_vec[..].iter().count(); | |
1780 | /// | |
1781 | /// // Good | |
1782 | /// let some_vec = vec![0, 1, 2, 3]; | |
1783 | /// let _ = some_vec.len(); | |
1784 | /// let _ = &some_vec[..].len(); | |
1785 | /// ``` | |
a2a8927a | 1786 | #[clippy::version = "1.52.0"] |
f20569fa XL |
1787 | pub ITER_COUNT, |
1788 | complexity, | |
1789 | "replace `.iter().count()` with `.len()`" | |
1790 | } | |
1791 | ||
17df50a5 | 1792 | declare_clippy_lint! { |
94222f64 XL |
1793 | /// ### What it does |
1794 | /// Checks for calls to [`splitn`] | |
17df50a5 XL |
1795 | /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and |
1796 | /// related functions with either zero or one splits. | |
1797 | /// | |
94222f64 XL |
1798 | /// ### Why is this bad? |
1799 | /// These calls don't actually split the value and are | |
17df50a5 XL |
1800 | /// likely to be intended as a different number. |
1801 | /// | |
94222f64 | 1802 | /// ### Example |
17df50a5 XL |
1803 | /// ```rust |
1804 | /// // Bad | |
1805 | /// let s = ""; | |
1806 | /// for x in s.splitn(1, ":") { | |
1807 | /// // use x | |
1808 | /// } | |
1809 | /// | |
1810 | /// // Good | |
1811 | /// let s = ""; | |
1812 | /// for x in s.splitn(2, ":") { | |
1813 | /// // use x | |
1814 | /// } | |
1815 | /// ``` | |
a2a8927a | 1816 | #[clippy::version = "1.54.0"] |
17df50a5 XL |
1817 | pub SUSPICIOUS_SPLITN, |
1818 | correctness, | |
1819 | "checks for `.splitn(0, ..)` and `.splitn(1, ..)`" | |
1820 | } | |
1821 | ||
1822 | declare_clippy_lint! { | |
94222f64 XL |
1823 | /// ### What it does |
1824 | /// Checks for manual implementations of `str::repeat` | |
17df50a5 | 1825 | /// |
94222f64 XL |
1826 | /// ### Why is this bad? |
1827 | /// These are both harder to read, as well as less performant. | |
17df50a5 | 1828 | /// |
94222f64 | 1829 | /// ### Example |
17df50a5 XL |
1830 | /// ```rust |
1831 | /// // Bad | |
1832 | /// let x: String = std::iter::repeat('x').take(10).collect(); | |
1833 | /// | |
1834 | /// // Good | |
1835 | /// let x: String = "x".repeat(10); | |
1836 | /// ``` | |
a2a8927a | 1837 | #[clippy::version = "1.54.0"] |
17df50a5 XL |
1838 | pub MANUAL_STR_REPEAT, |
1839 | perf, | |
1840 | "manual implementation of `str::repeat`" | |
1841 | } | |
1842 | ||
c295e0f8 | 1843 | declare_clippy_lint! { |
3c0e092e XL |
1844 | /// ### What it does |
1845 | /// Checks for usages of `str::splitn(2, _)` | |
c295e0f8 | 1846 | /// |
3c0e092e XL |
1847 | /// ### Why is this bad? |
1848 | /// `split_once` is both clearer in intent and slightly more efficient. | |
c295e0f8 | 1849 | /// |
3c0e092e | 1850 | /// ### Example |
c295e0f8 XL |
1851 | /// ```rust,ignore |
1852 | /// // Bad | |
1853 | /// let (key, value) = _.splitn(2, '=').next_tuple()?; | |
1854 | /// let value = _.splitn(2, '=').nth(1)?; | |
1855 | /// | |
1856 | /// // Good | |
1857 | /// let (key, value) = _.split_once('=')?; | |
1858 | /// let value = _.split_once('=')?.1; | |
1859 | /// ``` | |
a2a8927a | 1860 | #[clippy::version = "1.57.0"] |
c295e0f8 XL |
1861 | pub MANUAL_SPLIT_ONCE, |
1862 | complexity, | |
1863 | "replace `.splitn(2, pat)` with `.split_once(pat)`" | |
1864 | } | |
1865 | ||
a2a8927a XL |
1866 | declare_clippy_lint! { |
1867 | /// ### What it does | |
1868 | /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same. | |
1869 | /// ### Why is this bad? | |
1870 | /// The function `split` is simpler and there is no performance difference in these cases, considering | |
1871 | /// that both functions return a lazy iterator. | |
1872 | /// ### Example | |
1873 | /// ```rust | |
1874 | /// // Bad | |
1875 | /// let str = "key=value=add"; | |
1876 | /// let _ = str.splitn(3, '=').next().unwrap(); | |
1877 | /// ``` | |
1878 | /// Use instead: | |
1879 | /// ```rust | |
1880 | /// // Good | |
1881 | /// let str = "key=value=add"; | |
1882 | /// let _ = str.split('=').next().unwrap(); | |
1883 | /// ``` | |
1884 | #[clippy::version = "1.58.0"] | |
1885 | pub NEEDLESS_SPLITN, | |
1886 | complexity, | |
1887 | "usages of `str::splitn` that can be replaced with `str::split`" | |
1888 | } | |
1889 | ||
1890 | declare_clippy_lint! { | |
1891 | /// ### What it does | |
1892 | /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) | |
1893 | /// and other `to_owned`-like functions. | |
1894 | /// | |
1895 | /// ### Why is this bad? | |
1896 | /// The unnecessary calls result in useless allocations. | |
1897 | /// | |
1898 | /// ### Example | |
1899 | /// ```rust | |
1900 | /// let path = std::path::Path::new("x"); | |
1901 | /// foo(&path.to_string_lossy().to_string()); | |
1902 | /// fn foo(s: &str) {} | |
1903 | /// ``` | |
1904 | /// Use instead: | |
1905 | /// ```rust | |
1906 | /// let path = std::path::Path::new("x"); | |
1907 | /// foo(&path.to_string_lossy()); | |
1908 | /// fn foo(s: &str) {} | |
1909 | /// ``` | |
1910 | #[clippy::version = "1.58.0"] | |
1911 | pub UNNECESSARY_TO_OWNED, | |
1912 | perf, | |
1913 | "unnecessary calls to `to_owned`-like functions" | |
1914 | } | |
1915 | ||
f20569fa | 1916 | pub struct Methods { |
17df50a5 | 1917 | avoid_breaking_exported_api: bool, |
f20569fa XL |
1918 | msrv: Option<RustcVersion>, |
1919 | } | |
1920 | ||
1921 | impl Methods { | |
1922 | #[must_use] | |
17df50a5 XL |
1923 | pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self { |
1924 | Self { | |
1925 | avoid_breaking_exported_api, | |
1926 | msrv, | |
1927 | } | |
f20569fa XL |
1928 | } |
1929 | } | |
1930 | ||
1931 | impl_lint_pass!(Methods => [ | |
1932 | UNWRAP_USED, | |
1933 | EXPECT_USED, | |
1934 | SHOULD_IMPLEMENT_TRAIT, | |
1935 | WRONG_SELF_CONVENTION, | |
f20569fa | 1936 | OK_EXPECT, |
94222f64 | 1937 | UNWRAP_OR_ELSE_DEFAULT, |
f20569fa XL |
1938 | MAP_UNWRAP_OR, |
1939 | RESULT_MAP_OR_INTO_OPTION, | |
1940 | OPTION_MAP_OR_NONE, | |
1941 | BIND_INSTEAD_OF_MAP, | |
1942 | OR_FUN_CALL, | |
1943 | EXPECT_FUN_CALL, | |
1944 | CHARS_NEXT_CMP, | |
1945 | CHARS_LAST_CMP, | |
1946 | CLONE_ON_COPY, | |
1947 | CLONE_ON_REF_PTR, | |
1948 | CLONE_DOUBLE_REF, | |
cdc7bbd5 XL |
1949 | CLONED_INSTEAD_OF_COPIED, |
1950 | FLAT_MAP_OPTION, | |
f20569fa XL |
1951 | INEFFICIENT_TO_STRING, |
1952 | NEW_RET_NO_SELF, | |
1953 | SINGLE_CHAR_PATTERN, | |
1954 | SINGLE_CHAR_ADD_STR, | |
1955 | SEARCH_IS_SOME, | |
1956 | FILTER_NEXT, | |
1957 | SKIP_WHILE_NEXT, | |
f20569fa | 1958 | FILTER_MAP_IDENTITY, |
136023e0 | 1959 | MAP_IDENTITY, |
f20569fa XL |
1960 | MANUAL_FILTER_MAP, |
1961 | MANUAL_FIND_MAP, | |
cdc7bbd5 | 1962 | OPTION_FILTER_MAP, |
f20569fa XL |
1963 | FILTER_MAP_NEXT, |
1964 | FLAT_MAP_IDENTITY, | |
1965 | MAP_FLATTEN, | |
1966 | ITERATOR_STEP_BY_ZERO, | |
1967 | ITER_NEXT_SLICE, | |
1968 | ITER_COUNT, | |
1969 | ITER_NTH, | |
1970 | ITER_NTH_ZERO, | |
1971 | BYTES_NTH, | |
1972 | ITER_SKIP_NEXT, | |
1973 | GET_UNWRAP, | |
1974 | STRING_EXTEND_CHARS, | |
1975 | ITER_CLONED_COLLECT, | |
1976 | USELESS_ASREF, | |
1977 | UNNECESSARY_FOLD, | |
1978 | UNNECESSARY_FILTER_MAP, | |
1979 | INTO_ITER_ON_REF, | |
1980 | SUSPICIOUS_MAP, | |
1981 | UNINIT_ASSUMED_INIT, | |
1982 | MANUAL_SATURATING_ARITHMETIC, | |
1983 | ZST_OFFSET, | |
1984 | FILETYPE_IS_FILE, | |
1985 | OPTION_AS_REF_DEREF, | |
1986 | UNNECESSARY_LAZY_EVALUATIONS, | |
1987 | MAP_COLLECT_RESULT_UNIT, | |
1988 | FROM_ITER_INSTEAD_OF_COLLECT, | |
1989 | INSPECT_FOR_EACH, | |
17df50a5 XL |
1990 | IMPLICIT_CLONE, |
1991 | SUSPICIOUS_SPLITN, | |
136023e0 | 1992 | MANUAL_STR_REPEAT, |
c295e0f8 | 1993 | EXTEND_WITH_DRAIN, |
a2a8927a XL |
1994 | MANUAL_SPLIT_ONCE, |
1995 | NEEDLESS_SPLITN, | |
1996 | UNNECESSARY_TO_OWNED, | |
f20569fa XL |
1997 | ]); |
1998 | ||
cdc7bbd5 | 1999 | /// Extracts a method call name, args, and `Span` of the method name. |
a2a8927a | 2000 | fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> { |
cdc7bbd5 XL |
2001 | if let ExprKind::MethodCall(path, span, args, _) = recv.kind { |
2002 | if !args.iter().any(|e| e.span.from_expansion()) { | |
a2a8927a | 2003 | return Some((path.ident.name, args, span)); |
cdc7bbd5 XL |
2004 | } |
2005 | } | |
2006 | None | |
2007 | } | |
2008 | ||
a2a8927a | 2009 | /// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str` |
cdc7bbd5 XL |
2010 | macro_rules! method_call { |
2011 | ($expr:expr) => { | |
2012 | method_call($expr) | |
2013 | .as_ref() | |
a2a8927a | 2014 | .map(|&(ref name, args, span)| (name.as_str(), args, span)) |
cdc7bbd5 XL |
2015 | }; |
2016 | } | |
2017 | ||
f20569fa | 2018 | impl<'tcx> LateLintPass<'tcx> for Methods { |
f20569fa | 2019 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { |
a2a8927a | 2020 | if expr.span.from_expansion() { |
f20569fa XL |
2021 | return; |
2022 | } | |
2023 | ||
cdc7bbd5 | 2024 | check_methods(cx, expr, self.msrv.as_ref()); |
f20569fa XL |
2025 | |
2026 | match expr.kind { | |
cdc7bbd5 XL |
2027 | hir::ExprKind::Call(func, args) => { |
2028 | from_iter_instead_of_collect::check(cx, expr, args, func); | |
f20569fa | 2029 | }, |
cdc7bbd5 | 2030 | hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { |
a2a8927a XL |
2031 | or_fun_call::check(cx, expr, *method_span, method_call.ident.as_str(), args); |
2032 | expect_fun_call::check(cx, expr, *method_span, method_call.ident.as_str(), args); | |
cdc7bbd5 XL |
2033 | clone_on_copy::check(cx, expr, method_call.ident.name, args); |
2034 | clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args); | |
2035 | inefficient_to_string::check(cx, expr, method_call.ident.name, args); | |
2036 | single_char_add_str::check(cx, expr, args); | |
2037 | into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); | |
2038 | single_char_pattern::check(cx, expr, method_call.ident.name, args); | |
a2a8927a | 2039 | unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); |
f20569fa | 2040 | }, |
cdc7bbd5 | 2041 | hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { |
f20569fa XL |
2042 | let mut info = BinaryExprInfo { |
2043 | expr, | |
2044 | chain: lhs, | |
2045 | other: rhs, | |
2046 | eq: op.node == hir::BinOpKind::Eq, | |
2047 | }; | |
2048 | lint_binary_expr_with_method_call(cx, &mut info); | |
cdc7bbd5 | 2049 | }, |
f20569fa XL |
2050 | _ => (), |
2051 | } | |
2052 | } | |
2053 | ||
2054 | #[allow(clippy::too_many_lines)] | |
2055 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { | |
2056 | if in_external_macro(cx.sess(), impl_item.span) { | |
2057 | return; | |
2058 | } | |
2059 | let name = impl_item.ident.name.as_str(); | |
a2a8927a | 2060 | let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id()); |
f20569fa XL |
2061 | let item = cx.tcx.hir().expect_item(parent); |
2062 | let self_ty = cx.tcx.type_of(item.def_id); | |
2063 | ||
cdc7bbd5 | 2064 | let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); |
f20569fa XL |
2065 | if_chain! { |
2066 | if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; | |
cdc7bbd5 | 2067 | if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); |
f20569fa XL |
2068 | |
2069 | let method_sig = cx.tcx.fn_sig(impl_item.def_id); | |
2070 | let method_sig = cx.tcx.erase_late_bound_regions(method_sig); | |
2071 | ||
2072 | let first_arg_ty = &method_sig.inputs().iter().next(); | |
2073 | ||
2074 | // check conventions w.r.t. conversion method names and predicates | |
2075 | if let Some(first_arg_ty) = first_arg_ty; | |
2076 | ||
2077 | then { | |
cdc7bbd5 | 2078 | // if this impl block implements a trait, lint in trait definition instead |
94222f64 | 2079 | if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) { |
f20569fa XL |
2080 | // check missing trait implementations |
2081 | for method_config in &TRAIT_METHODS { | |
2082 | if name == method_config.method_name && | |
2083 | sig.decl.inputs.len() == method_config.param_count && | |
cdc7bbd5 | 2084 | method_config.output_type.matches(&sig.decl.output) && |
f20569fa XL |
2085 | method_config.self_kind.matches(cx, self_ty, first_arg_ty) && |
2086 | fn_header_equals(method_config.fn_header, sig.header) && | |
cdc7bbd5 | 2087 | method_config.lifetime_param_cond(impl_item) |
f20569fa XL |
2088 | { |
2089 | span_lint_and_help( | |
2090 | cx, | |
2091 | SHOULD_IMPLEMENT_TRAIT, | |
2092 | impl_item.span, | |
2093 | &format!( | |
2094 | "method `{}` can be confused for the standard trait method `{}::{}`", | |
2095 | method_config.method_name, | |
2096 | method_config.trait_name, | |
2097 | method_config.method_name | |
2098 | ), | |
2099 | None, | |
2100 | &format!( | |
2101 | "consider implementing the trait `{}` or choosing a less ambiguous method name", | |
2102 | method_config.trait_name | |
2103 | ) | |
2104 | ); | |
2105 | } | |
2106 | } | |
2107 | } | |
2108 | ||
17df50a5 XL |
2109 | if sig.decl.implicit_self.has_implicit_self() |
2110 | && !(self.avoid_breaking_exported_api | |
94222f64 | 2111 | && cx.access_levels.is_exported(impl_item.def_id)) |
17df50a5 XL |
2112 | { |
2113 | wrong_self_convention::check( | |
2114 | cx, | |
a2a8927a | 2115 | name, |
17df50a5 XL |
2116 | self_ty, |
2117 | first_arg_ty, | |
2118 | first_arg.pat.span, | |
2119 | implements_trait, | |
2120 | false | |
2121 | ); | |
2122 | } | |
f20569fa XL |
2123 | } |
2124 | } | |
2125 | ||
cdc7bbd5 XL |
2126 | // if this impl block implements a trait, lint in trait definition instead |
2127 | if implements_trait { | |
2128 | return; | |
2129 | } | |
2130 | ||
f20569fa XL |
2131 | if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { |
2132 | let ret_ty = return_ty(cx, impl_item.hir_id()); | |
2133 | ||
2134 | // walk the return type and check for Self (this does not check associated types) | |
cdc7bbd5 | 2135 | if let Some(self_adt) = self_ty.ty_adt_def() { |
94222f64 | 2136 | if contains_adt_constructor(cx.tcx, ret_ty, self_adt) { |
cdc7bbd5 XL |
2137 | return; |
2138 | } | |
94222f64 | 2139 | } else if contains_ty(cx.tcx, ret_ty, self_ty) { |
f20569fa XL |
2140 | return; |
2141 | } | |
2142 | ||
2143 | // if return type is impl trait, check the associated types | |
2144 | if let ty::Opaque(def_id, _) = *ret_ty.kind() { | |
2145 | // one of the associated types must be Self | |
2146 | for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { | |
2147 | if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { | |
2148 | // walk the associated type and check for Self | |
cdc7bbd5 | 2149 | if let Some(self_adt) = self_ty.ty_adt_def() { |
94222f64 | 2150 | if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) { |
cdc7bbd5 XL |
2151 | return; |
2152 | } | |
94222f64 | 2153 | } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) { |
f20569fa XL |
2154 | return; |
2155 | } | |
2156 | } | |
2157 | } | |
2158 | } | |
2159 | ||
2160 | if name == "new" && !TyS::same_type(ret_ty, self_ty) { | |
2161 | span_lint( | |
2162 | cx, | |
2163 | NEW_RET_NO_SELF, | |
2164 | impl_item.span, | |
2165 | "methods called `new` usually return `Self`", | |
2166 | ); | |
2167 | } | |
2168 | } | |
2169 | } | |
2170 | ||
2171 | fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { | |
2172 | if in_external_macro(cx.tcx.sess, item.span) { | |
2173 | return; | |
2174 | } | |
2175 | ||
2176 | if_chain! { | |
2177 | if let TraitItemKind::Fn(ref sig, _) = item.kind; | |
17df50a5 | 2178 | if sig.decl.implicit_self.has_implicit_self(); |
f20569fa | 2179 | if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); |
17df50a5 | 2180 | |
f20569fa | 2181 | then { |
cdc7bbd5 XL |
2182 | let first_arg_span = first_arg_ty.span; |
2183 | let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); | |
c295e0f8 | 2184 | let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); |
f20569fa XL |
2185 | wrong_self_convention::check( |
2186 | cx, | |
a2a8927a | 2187 | item.ident.name.as_str(), |
f20569fa XL |
2188 | self_ty, |
2189 | first_arg_ty, | |
cdc7bbd5 XL |
2190 | first_arg_span, |
2191 | false, | |
2192 | true | |
f20569fa XL |
2193 | ); |
2194 | } | |
2195 | } | |
2196 | ||
2197 | if_chain! { | |
2198 | if item.ident.name == sym::new; | |
2199 | if let TraitItemKind::Fn(_, _) = item.kind; | |
2200 | let ret_ty = return_ty(cx, item.hir_id()); | |
c295e0f8 | 2201 | let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); |
94222f64 | 2202 | if !contains_ty(cx.tcx, ret_ty, self_ty); |
f20569fa XL |
2203 | |
2204 | then { | |
2205 | span_lint( | |
2206 | cx, | |
2207 | NEW_RET_NO_SELF, | |
2208 | item.span, | |
2209 | "methods called `new` usually return `Self`", | |
2210 | ); | |
2211 | } | |
2212 | } | |
2213 | } | |
2214 | ||
2215 | extract_msrv_attr!(LateContext); | |
2216 | } | |
2217 | ||
cdc7bbd5 XL |
2218 | #[allow(clippy::too_many_lines)] |
2219 | fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { | |
2220 | if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { | |
2221 | match (name, args) { | |
136023e0 | 2222 | ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { |
17df50a5 | 2223 | zst_offset::check(cx, expr, recv); |
cdc7bbd5 XL |
2224 | }, |
2225 | ("and_then", [arg]) => { | |
2226 | let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); | |
2227 | let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); | |
2228 | if !biom_option_linted && !biom_result_linted { | |
2229 | unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); | |
f20569fa XL |
2230 | } |
2231 | }, | |
cdc7bbd5 XL |
2232 | ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), |
2233 | ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), | |
2234 | ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), | |
2235 | ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), | |
2236 | ("collect", []) => match method_call!(recv) { | |
a2a8927a XL |
2237 | Some((name @ ("cloned" | "copied"), [recv2], _)) => { |
2238 | iter_cloned_collect::check(cx, name, expr, recv2); | |
2239 | }, | |
cdc7bbd5 XL |
2240 | Some(("map", [m_recv, m_arg], _)) => { |
2241 | map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); | |
2242 | }, | |
17df50a5 XL |
2243 | Some(("take", [take_self_arg, take_arg], _)) => { |
2244 | if meets_msrv(msrv, &msrvs::STR_REPEAT) { | |
2245 | manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); | |
2246 | } | |
2247 | }, | |
cdc7bbd5 XL |
2248 | _ => {}, |
2249 | }, | |
2250 | ("count", []) => match method_call!(recv) { | |
2251 | Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { | |
2252 | iter_count::check(cx, expr, recv2, name); | |
2253 | }, | |
2254 | Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), | |
2255 | _ => {}, | |
2256 | }, | |
2257 | ("expect", [_]) => match method_call!(recv) { | |
2258 | Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), | |
2259 | _ => expect_used::check(cx, expr, recv), | |
2260 | }, | |
136023e0 XL |
2261 | ("extend", [arg]) => { |
2262 | string_extend_chars::check(cx, expr, recv, arg); | |
2263 | extend_with_drain::check(cx, expr, recv, arg); | |
2264 | }, | |
cdc7bbd5 XL |
2265 | ("filter_map", [arg]) => { |
2266 | unnecessary_filter_map::check(cx, expr, arg); | |
2267 | filter_map_identity::check(cx, expr, arg, span); | |
2268 | }, | |
2269 | ("flat_map", [arg]) => { | |
2270 | flat_map_identity::check(cx, expr, arg, span); | |
2271 | flat_map_option::check(cx, expr, arg, span); | |
2272 | }, | |
2273 | ("flatten", []) => { | |
2274 | if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { | |
2275 | map_flatten::check(cx, expr, recv, map_arg); | |
2276 | } | |
2277 | }, | |
2278 | ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), | |
2279 | ("for_each", [_]) => { | |
2280 | if let Some(("inspect", [_, _], span2)) = method_call!(recv) { | |
2281 | inspect_for_each::check(cx, expr, span2); | |
2282 | } | |
2283 | }, | |
2284 | ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), | |
2285 | ("is_file", []) => filetype_is_file::check(cx, expr, recv), | |
2286 | ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), | |
2287 | ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), | |
2288 | ("map", [m_arg]) => { | |
2289 | if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { | |
2290 | match (name, args) { | |
2291 | ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), | |
2292 | ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), | |
2293 | ("filter", [f_arg]) => { | |
17df50a5 | 2294 | filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); |
cdc7bbd5 XL |
2295 | }, |
2296 | ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), | |
2297 | _ => {}, | |
2298 | } | |
2299 | } | |
136023e0 | 2300 | map_identity::check(cx, expr, recv, m_arg, span); |
cdc7bbd5 XL |
2301 | }, |
2302 | ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), | |
2303 | ("next", []) => { | |
2304 | if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { | |
2305 | match (name, args) { | |
2306 | ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), | |
2307 | ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), | |
2308 | ("iter", []) => iter_next_slice::check(cx, expr, recv), | |
2309 | ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg), | |
2310 | ("skip_while", [_]) => skip_while_next::check(cx, expr), | |
2311 | _ => {}, | |
2312 | } | |
2313 | } | |
2314 | }, | |
2315 | ("nth", [n_arg]) => match method_call!(recv) { | |
2316 | Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), | |
2317 | Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), | |
2318 | Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), | |
2319 | _ => iter_nth_zero::check(cx, expr, recv, n_arg), | |
2320 | }, | |
2321 | ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), | |
2322 | ("or_else", [arg]) => { | |
2323 | if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { | |
2324 | unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); | |
2325 | } | |
2326 | }, | |
c295e0f8 XL |
2327 | ("splitn" | "rsplitn", [count_arg, pat_arg]) => { |
2328 | if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { | |
2329 | suspicious_splitn::check(cx, name, expr, recv, count); | |
2330 | if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { | |
a2a8927a XL |
2331 | str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg); |
2332 | } | |
2333 | if count >= 2 { | |
2334 | str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count); | |
c295e0f8 XL |
2335 | } |
2336 | } | |
2337 | }, | |
2338 | ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { | |
2339 | if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { | |
2340 | suspicious_splitn::check(cx, name, expr, recv, count); | |
2341 | } | |
17df50a5 | 2342 | }, |
cdc7bbd5 XL |
2343 | ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), |
2344 | ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { | |
2345 | implicit_clone::check(cx, name, expr, recv, span); | |
2346 | }, | |
2347 | ("unwrap", []) => match method_call!(recv) { | |
2348 | Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), | |
2349 | Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), | |
2350 | _ => unwrap_used::check(cx, expr, recv), | |
2351 | }, | |
2352 | ("unwrap_or", [u_arg]) => match method_call!(recv) { | |
2353 | Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { | |
2354 | manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); | |
2355 | }, | |
2356 | Some(("map", [m_recv, m_arg], span)) => { | |
17df50a5 | 2357 | option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); |
cdc7bbd5 XL |
2358 | }, |
2359 | _ => {}, | |
2360 | }, | |
2361 | ("unwrap_or_else", [u_arg]) => match method_call!(recv) { | |
2362 | Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, | |
94222f64 XL |
2363 | _ => { |
2364 | unwrap_or_else_default::check(cx, expr, recv, u_arg); | |
2365 | unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); | |
2366 | }, | |
cdc7bbd5 XL |
2367 | }, |
2368 | _ => {}, | |
f20569fa XL |
2369 | } |
2370 | } | |
2371 | } | |
2372 | ||
cdc7bbd5 XL |
2373 | fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { |
2374 | if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { | |
17df50a5 | 2375 | search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); |
cdc7bbd5 XL |
2376 | } |
2377 | } | |
2378 | ||
f20569fa XL |
2379 | /// Used for `lint_binary_expr_with_method_call`. |
2380 | #[derive(Copy, Clone)] | |
2381 | struct BinaryExprInfo<'a> { | |
2382 | expr: &'a hir::Expr<'a>, | |
2383 | chain: &'a hir::Expr<'a>, | |
2384 | other: &'a hir::Expr<'a>, | |
2385 | eq: bool, | |
2386 | } | |
2387 | ||
2388 | /// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. | |
2389 | fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) { | |
2390 | macro_rules! lint_with_both_lhs_and_rhs { | |
cdc7bbd5 | 2391 | ($func:expr, $cx:expr, $info:ident) => { |
f20569fa XL |
2392 | if !$func($cx, $info) { |
2393 | ::std::mem::swap(&mut $info.chain, &mut $info.other); | |
2394 | if $func($cx, $info) { | |
2395 | return; | |
2396 | } | |
2397 | } | |
2398 | }; | |
2399 | } | |
2400 | ||
cdc7bbd5 XL |
2401 | lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info); |
2402 | lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info); | |
2403 | lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info); | |
2404 | lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info); | |
f20569fa XL |
2405 | } |
2406 | ||
2407 | const FN_HEADER: hir::FnHeader = hir::FnHeader { | |
2408 | unsafety: hir::Unsafety::Normal, | |
2409 | constness: hir::Constness::NotConst, | |
2410 | asyncness: hir::IsAsync::NotAsync, | |
2411 | abi: rustc_target::spec::abi::Abi::Rust, | |
2412 | }; | |
2413 | ||
2414 | struct ShouldImplTraitCase { | |
2415 | trait_name: &'static str, | |
2416 | method_name: &'static str, | |
2417 | param_count: usize, | |
2418 | fn_header: hir::FnHeader, | |
2419 | // implicit self kind expected (none, self, &self, ...) | |
2420 | self_kind: SelfKind, | |
2421 | // checks against the output type | |
2422 | output_type: OutType, | |
2423 | // certain methods with explicit lifetimes can't implement the equivalent trait method | |
2424 | lint_explicit_lifetime: bool, | |
2425 | } | |
2426 | impl ShouldImplTraitCase { | |
2427 | const fn new( | |
2428 | trait_name: &'static str, | |
2429 | method_name: &'static str, | |
2430 | param_count: usize, | |
2431 | fn_header: hir::FnHeader, | |
2432 | self_kind: SelfKind, | |
2433 | output_type: OutType, | |
2434 | lint_explicit_lifetime: bool, | |
2435 | ) -> ShouldImplTraitCase { | |
2436 | ShouldImplTraitCase { | |
2437 | trait_name, | |
2438 | method_name, | |
2439 | param_count, | |
2440 | fn_header, | |
2441 | self_kind, | |
2442 | output_type, | |
2443 | lint_explicit_lifetime, | |
2444 | } | |
2445 | } | |
2446 | ||
2447 | fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { | |
2448 | self.lint_explicit_lifetime | |
2449 | || !impl_item.generics.params.iter().any(|p| { | |
2450 | matches!( | |
2451 | p.kind, | |
2452 | hir::GenericParamKind::Lifetime { | |
2453 | kind: hir::LifetimeParamKind::Explicit | |
2454 | } | |
2455 | ) | |
2456 | }) | |
2457 | } | |
2458 | } | |
2459 | ||
2460 | #[rustfmt::skip] | |
2461 | const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ | |
2462 | ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2463 | ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), | |
2464 | ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), | |
2465 | ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2466 | ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2467 | ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2468 | ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), | |
2469 | ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), | |
2470 | ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true), | |
2471 | ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true), | |
2472 | // FIXME: default doesn't work | |
2473 | ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true), | |
2474 | ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true), | |
2475 | ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), | |
2476 | ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2477 | ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true), | |
2478 | ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true), | |
2479 | ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true), | |
2480 | ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true), | |
2481 | ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true), | |
2482 | ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true), | |
2483 | ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), | |
2484 | ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2485 | ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2486 | ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2487 | ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false), | |
2488 | ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2489 | ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2490 | ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2491 | ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2492 | ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), | |
2493 | ]; | |
2494 | ||
f20569fa XL |
2495 | #[derive(Clone, Copy, PartialEq, Debug)] |
2496 | enum SelfKind { | |
2497 | Value, | |
2498 | Ref, | |
2499 | RefMut, | |
2500 | No, | |
2501 | } | |
2502 | ||
2503 | impl SelfKind { | |
2504 | fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { | |
2505 | fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool { | |
2506 | if ty == parent_ty { | |
2507 | true | |
2508 | } else if ty.is_box() { | |
2509 | ty.boxed_ty() == parent_ty | |
2510 | } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) { | |
2511 | if let ty::Adt(_, substs) = ty.kind() { | |
2512 | substs.types().next().map_or(false, |t| t == parent_ty) | |
2513 | } else { | |
2514 | false | |
2515 | } | |
2516 | } else { | |
2517 | false | |
2518 | } | |
2519 | } | |
2520 | ||
2521 | fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { | |
2522 | if let ty::Ref(_, t, m) = *ty.kind() { | |
2523 | return m == mutability && t == parent_ty; | |
2524 | } | |
2525 | ||
2526 | let trait_path = match mutability { | |
2527 | hir::Mutability::Not => &paths::ASREF_TRAIT, | |
2528 | hir::Mutability::Mut => &paths::ASMUT_TRAIT, | |
2529 | }; | |
2530 | ||
2531 | let trait_def_id = match get_trait_def_id(cx, trait_path) { | |
2532 | Some(did) => did, | |
2533 | None => return false, | |
2534 | }; | |
2535 | implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) | |
2536 | } | |
2537 | ||
2538 | match self { | |
2539 | Self::Value => matches_value(cx, parent_ty, ty), | |
2540 | Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), | |
2541 | Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), | |
2542 | Self::No => ty != parent_ty, | |
2543 | } | |
2544 | } | |
2545 | ||
2546 | #[must_use] | |
2547 | fn description(self) -> &'static str { | |
2548 | match self { | |
cdc7bbd5 XL |
2549 | Self::Value => "`self` by value", |
2550 | Self::Ref => "`self` by reference", | |
2551 | Self::RefMut => "`self` by mutable reference", | |
2552 | Self::No => "no `self`", | |
f20569fa XL |
2553 | } |
2554 | } | |
2555 | } | |
2556 | ||
2557 | #[derive(Clone, Copy)] | |
2558 | enum OutType { | |
2559 | Unit, | |
2560 | Bool, | |
2561 | Any, | |
2562 | Ref, | |
2563 | } | |
2564 | ||
2565 | impl OutType { | |
cdc7bbd5 XL |
2566 | fn matches(self, ty: &hir::FnRetTy<'_>) -> bool { |
2567 | let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); | |
f20569fa XL |
2568 | match (self, ty) { |
2569 | (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, | |
cdc7bbd5 XL |
2570 | (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, |
2571 | (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, | |
2572 | (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, | |
2573 | (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), | |
f20569fa XL |
2574 | _ => false, |
2575 | } | |
2576 | } | |
2577 | } | |
2578 | ||
2579 | fn is_bool(ty: &hir::Ty<'_>) -> bool { | |
cdc7bbd5 XL |
2580 | if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { |
2581 | matches!(path.res, Res::PrimTy(PrimTy::Bool)) | |
f20569fa XL |
2582 | } else { |
2583 | false | |
2584 | } | |
2585 | } | |
2586 | ||
2587 | fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { | |
2588 | expected.constness == actual.constness | |
2589 | && expected.unsafety == actual.unsafety | |
2590 | && expected.asyncness == actual.asyncness | |
2591 | } |