]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; |
2 | use clippy_utils::source::{snippet, snippet_with_applicability}; | |
a2a8927a | 3 | use clippy_utils::sugg::deref_closure_args; |
487cf647 | 4 | use clippy_utils::ty::is_type_lang_item; |
cdc7bbd5 | 5 | use clippy_utils::{is_trait_method, strip_pat_refs}; |
f20569fa XL |
6 | use if_chain::if_chain; |
7 | use rustc_errors::Applicability; | |
8 | use rustc_hir as hir; | |
9 | use rustc_hir::PatKind; | |
10 | use rustc_lint::LateContext; | |
11 | use rustc_middle::ty; | |
12 | use rustc_span::source_map::Span; | |
13 | use rustc_span::symbol::sym; | |
14 | ||
15 | use super::SEARCH_IS_SOME; | |
16 | ||
17 | /// lint searching an Iterator followed by `is_some()` | |
cdc7bbd5 XL |
18 | /// or calling `find()` on a string followed by `is_some()` or `is_none()` |
19 | #[allow(clippy::too_many_arguments, clippy::too_many_lines)] | |
f20569fa | 20 | pub(super) fn check<'tcx>( |
cdc7bbd5 | 21 | cx: &LateContext<'_>, |
f20569fa XL |
22 | expr: &'tcx hir::Expr<'_>, |
23 | search_method: &str, | |
cdc7bbd5 XL |
24 | is_some: bool, |
25 | search_recv: &hir::Expr<'_>, | |
26 | search_arg: &'tcx hir::Expr<'_>, | |
27 | is_some_recv: &hir::Expr<'_>, | |
f20569fa XL |
28 | method_span: Span, |
29 | ) { | |
cdc7bbd5 | 30 | let option_check_method = if is_some { "is_some" } else { "is_none" }; |
f20569fa | 31 | // lint if caller of search is an Iterator |
cdc7bbd5 | 32 | if is_trait_method(cx, is_some_recv, sym::Iterator) { |
2b03887a | 33 | let msg = format!("called `{option_check_method}()` after searching an `Iterator` with `{search_method}`"); |
cdc7bbd5 | 34 | let search_snippet = snippet(cx, search_arg.span, ".."); |
f20569fa XL |
35 | if search_snippet.lines().count() <= 1 { |
36 | // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` | |
37 | // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` | |
a2a8927a | 38 | let mut applicability = Applicability::MachineApplicable; |
f20569fa XL |
39 | let any_search_snippet = if_chain! { |
40 | if search_method == "find"; | |
064997fb | 41 | if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind; |
923072b8 | 42 | let closure_body = cx.tcx.hir().body(body); |
f20569fa XL |
43 | if let Some(closure_arg) = closure_body.params.get(0); |
44 | then { | |
45 | if let hir::PatKind::Ref(..) = closure_arg.pat.kind { | |
46 | Some(search_snippet.replacen('&', "", 1)) | |
a2a8927a XL |
47 | } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind { |
48 | // `find()` provides a reference to the item, but `any` does not, | |
49 | // so we should fix item usages for suggestion | |
50 | if let Some(closure_sugg) = deref_closure_args(cx, search_arg) { | |
51 | applicability = closure_sugg.applicability; | |
52 | Some(closure_sugg.suggestion) | |
53 | } else { | |
54 | Some(search_snippet.to_string()) | |
55 | } | |
f20569fa XL |
56 | } else { |
57 | None | |
58 | } | |
59 | } else { | |
60 | None | |
61 | } | |
62 | }; | |
63 | // add note if not multi-line | |
cdc7bbd5 XL |
64 | if is_some { |
65 | span_lint_and_sugg( | |
66 | cx, | |
67 | SEARCH_IS_SOME, | |
68 | method_span.with_hi(expr.span.hi()), | |
69 | &msg, | |
70 | "use `any()` instead", | |
71 | format!( | |
72 | "any({})", | |
73 | any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) | |
74 | ), | |
a2a8927a | 75 | applicability, |
cdc7bbd5 XL |
76 | ); |
77 | } else { | |
78 | let iter = snippet(cx, search_recv.span, ".."); | |
79 | span_lint_and_sugg( | |
80 | cx, | |
81 | SEARCH_IS_SOME, | |
82 | expr.span, | |
83 | &msg, | |
84 | "use `!_.any()` instead", | |
85 | format!( | |
2b03887a | 86 | "!{iter}.any({})", |
cdc7bbd5 XL |
87 | any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) |
88 | ), | |
a2a8927a | 89 | applicability, |
cdc7bbd5 XL |
90 | ); |
91 | } | |
f20569fa | 92 | } else { |
cdc7bbd5 XL |
93 | let hint = format!( |
94 | "this is more succinctly expressed by calling `any()`{}", | |
95 | if option_check_method == "is_none" { | |
96 | " with negation" | |
97 | } else { | |
98 | "" | |
99 | } | |
100 | ); | |
101 | span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint); | |
f20569fa XL |
102 | } |
103 | } | |
104 | // lint if `find()` is called by `String` or `&str` | |
105 | else if search_method == "find" { | |
106 | let is_string_or_str_slice = |e| { | |
107 | let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); | |
487cf647 | 108 | if is_type_lang_item(cx, self_ty, hir::LangItem::String) { |
f20569fa XL |
109 | true |
110 | } else { | |
111 | *self_ty.kind() == ty::Str | |
112 | } | |
113 | }; | |
114 | if_chain! { | |
cdc7bbd5 XL |
115 | if is_string_or_str_slice(search_recv); |
116 | if is_string_or_str_slice(search_arg); | |
f20569fa | 117 | then { |
2b03887a | 118 | let msg = format!("called `{option_check_method}()` after calling `find()` on a string"); |
cdc7bbd5 XL |
119 | match option_check_method { |
120 | "is_some" => { | |
121 | let mut applicability = Applicability::MachineApplicable; | |
122 | let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); | |
123 | span_lint_and_sugg( | |
124 | cx, | |
125 | SEARCH_IS_SOME, | |
126 | method_span.with_hi(expr.span.hi()), | |
127 | &msg, | |
128 | "use `contains()` instead", | |
2b03887a | 129 | format!("contains({find_arg})"), |
cdc7bbd5 XL |
130 | applicability, |
131 | ); | |
132 | }, | |
133 | "is_none" => { | |
134 | let string = snippet(cx, search_recv.span, ".."); | |
135 | let mut applicability = Applicability::MachineApplicable; | |
136 | let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); | |
137 | span_lint_and_sugg( | |
138 | cx, | |
139 | SEARCH_IS_SOME, | |
140 | expr.span, | |
141 | &msg, | |
142 | "use `!_.contains()` instead", | |
2b03887a | 143 | format!("!{string}.contains({find_arg})"), |
cdc7bbd5 XL |
144 | applicability, |
145 | ); | |
146 | }, | |
147 | _ => (), | |
148 | } | |
f20569fa XL |
149 | } |
150 | } | |
151 | } | |
152 | } |