]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/search_is_some.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / search_is_some.rs
CommitLineData
cdc7bbd5
XL
1use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
2use clippy_utils::source::{snippet, snippet_with_applicability};
a2a8927a 3use clippy_utils::sugg::deref_closure_args;
487cf647 4use clippy_utils::ty::is_type_lang_item;
cdc7bbd5 5use clippy_utils::{is_trait_method, strip_pat_refs};
f20569fa
XL
6use if_chain::if_chain;
7use rustc_errors::Applicability;
8use rustc_hir as hir;
9use rustc_hir::PatKind;
10use rustc_lint::LateContext;
11use rustc_middle::ty;
12use rustc_span::source_map::Span;
13use rustc_span::symbol::sym;
14
15use 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 20pub(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}