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