]>
Commit | Line | Data |
---|---|---|
a2a8927a XL |
1 | use super::implicit_clone::is_clone_like; |
2 | use super::unnecessary_iter_cloned::{self, is_into_iter}; | |
3 | use clippy_utils::diagnostics::span_lint_and_sugg; | |
487cf647 | 4 | use clippy_utils::msrvs::{self, Msrv}; |
a2a8927a | 5 | use clippy_utils::source::snippet_opt; |
487cf647 | 6 | use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; |
f2b60f7d FG |
7 | use clippy_utils::visitors::find_all_ret_expressions; |
8 | use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; | |
a2a8927a | 9 | use rustc_errors::Applicability; |
add651ee FG |
10 | use rustc_hir::def_id::DefId; |
11 | use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node}; | |
2b03887a | 12 | use rustc_hir_typeck::{FnCtxt, Inherited}; |
f2b60f7d | 13 | use rustc_infer::infer::TyCtxtInferExt; |
a2a8927a XL |
14 | use rustc_lint::LateContext; |
15 | use rustc_middle::mir::Mutability; | |
16 | use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; | |
add651ee FG |
17 | use rustc_middle::ty::{ |
18 | self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, | |
19 | TraitPredicate, Ty, | |
20 | }; | |
a2a8927a | 21 | use rustc_span::{sym, Symbol}; |
add651ee FG |
22 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; |
23 | use rustc_trait_selection::traits::{Obligation, ObligationCause}; | |
a2a8927a XL |
24 | |
25 | use super::UNNECESSARY_TO_OWNED; | |
26 | ||
04454e1e FG |
27 | pub fn check<'tcx>( |
28 | cx: &LateContext<'tcx>, | |
29 | expr: &'tcx Expr<'tcx>, | |
30 | method_name: Symbol, | |
f2b60f7d FG |
31 | receiver: &'tcx Expr<'_>, |
32 | args: &'tcx [Expr<'_>], | |
487cf647 | 33 | msrv: &Msrv, |
04454e1e | 34 | ) { |
a2a8927a XL |
35 | if_chain! { |
36 | if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); | |
f2b60f7d | 37 | if args.is_empty(); |
a2a8927a XL |
38 | then { |
39 | if is_cloned_or_copied(cx, method_name, method_def_id) { | |
40 | unnecessary_iter_cloned::check(cx, expr, method_name, receiver); | |
f2b60f7d | 41 | } else if is_to_owned_like(cx, expr, method_name, method_def_id) { |
a2a8927a XL |
42 | // At this point, we know the call is of a `to_owned`-like function. The functions |
43 | // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary | |
44 | // based on its context, that is, whether it is a referent in an `AddrOf` expression, an | |
45 | // argument in a `into_iter` call, or an argument in the call of some other function. | |
46 | if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { | |
47 | return; | |
48 | } | |
04454e1e | 49 | if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { |
a2a8927a XL |
50 | return; |
51 | } | |
52 | check_other_call_arg(cx, expr, method_name, receiver); | |
53 | } | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
58 | /// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its | |
59 | /// call of a `to_owned`-like function is unnecessary. | |
60 | #[allow(clippy::too_many_lines)] | |
61 | fn check_addr_of_expr( | |
5099ac24 FG |
62 | cx: &LateContext<'_>, |
63 | expr: &Expr<'_>, | |
a2a8927a XL |
64 | method_name: Symbol, |
65 | method_def_id: DefId, | |
5099ac24 | 66 | receiver: &Expr<'_>, |
a2a8927a XL |
67 | ) -> bool { |
68 | if_chain! { | |
69 | if let Some(parent) = get_parent_expr(cx, expr); | |
70 | if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; | |
71 | let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>(); | |
923072b8 | 72 | if let |
a2a8927a XL |
73 | // For matching uses of `Cow::from` |
74 | [ | |
75 | Adjustment { | |
76 | kind: Adjust::Deref(None), | |
923072b8 | 77 | target: referent_ty, |
a2a8927a XL |
78 | }, |
79 | Adjustment { | |
80 | kind: Adjust::Borrow(_), | |
81 | target: target_ty, | |
82 | }, | |
83 | ] | |
84 | // For matching uses of arrays | |
85 | | [ | |
86 | Adjustment { | |
87 | kind: Adjust::Deref(None), | |
923072b8 | 88 | target: referent_ty, |
a2a8927a XL |
89 | }, |
90 | Adjustment { | |
91 | kind: Adjust::Borrow(_), | |
92 | .. | |
93 | }, | |
94 | Adjustment { | |
95 | kind: Adjust::Pointer(_), | |
96 | target: target_ty, | |
97 | }, | |
98 | ] | |
99 | // For matching everything else | |
100 | | [ | |
101 | Adjustment { | |
102 | kind: Adjust::Deref(None), | |
923072b8 | 103 | target: referent_ty, |
a2a8927a XL |
104 | }, |
105 | Adjustment { | |
106 | kind: Adjust::Deref(Some(OverloadedDeref { .. })), | |
107 | .. | |
108 | }, | |
109 | Adjustment { | |
110 | kind: Adjust::Borrow(_), | |
111 | target: target_ty, | |
112 | }, | |
923072b8 | 113 | ] = adjustments[..]; |
a2a8927a | 114 | let receiver_ty = cx.typeck_results().expr_ty(receiver); |
923072b8 FG |
115 | let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); |
116 | let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); | |
117 | // Only flag cases satisfying at least one of the following three conditions: | |
118 | // * the referent and receiver types are distinct | |
119 | // * the referent/receiver type is a copyable array | |
120 | // * the method is `Cow::into_owned` | |
121 | // This restriction is to ensure there is no overlap between `redundant_clone` and this | |
122 | // lint. It also avoids the following false positive: | |
123 | // https://github.com/rust-lang/rust-clippy/issues/8759 | |
124 | // Arrays are a bit of a corner case. Non-copyable arrays are handled by | |
125 | // `redundant_clone`, but copyable arrays are not. | |
126 | if *referent_ty != receiver_ty | |
127 | || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) | |
128 | || is_cow_into_owned(cx, method_name, method_def_id); | |
a2a8927a XL |
129 | if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); |
130 | then { | |
a2a8927a XL |
131 | if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { |
132 | span_lint_and_sugg( | |
133 | cx, | |
134 | UNNECESSARY_TO_OWNED, | |
135 | parent.span, | |
2b03887a | 136 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a | 137 | "use", |
5e7ed085 | 138 | format!( |
2b03887a | 139 | "{:&>width$}{receiver_snippet}", |
5e7ed085 | 140 | "", |
5e7ed085 FG |
141 | width = n_target_refs - n_receiver_refs |
142 | ), | |
a2a8927a XL |
143 | Applicability::MachineApplicable, |
144 | ); | |
145 | return true; | |
146 | } | |
147 | if_chain! { | |
148 | if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); | |
149 | if implements_trait(cx, receiver_ty, deref_trait_id, &[]); | |
487cf647 | 150 | if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty); |
fe692bf9 FG |
151 | // Make sure that it's actually calling the right `.to_string()`, (#10033) |
152 | // *or* this is a `Cow::into_owned()` call (which would be the wrong into_owned receiver (str != Cow) | |
153 | // but that's ok for Cow::into_owned specifically) | |
154 | if cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() == target_ty | |
155 | || is_cow_into_owned(cx, method_name, method_def_id); | |
a2a8927a XL |
156 | then { |
157 | if n_receiver_refs > 0 { | |
158 | span_lint_and_sugg( | |
159 | cx, | |
160 | UNNECESSARY_TO_OWNED, | |
161 | parent.span, | |
2b03887a | 162 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a XL |
163 | "use", |
164 | receiver_snippet, | |
165 | Applicability::MachineApplicable, | |
166 | ); | |
167 | } else { | |
168 | span_lint_and_sugg( | |
169 | cx, | |
170 | UNNECESSARY_TO_OWNED, | |
171 | expr.span.with_lo(receiver.span.hi()), | |
2b03887a | 172 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a XL |
173 | "remove this", |
174 | String::new(), | |
175 | Applicability::MachineApplicable, | |
176 | ); | |
177 | } | |
178 | return true; | |
179 | } | |
180 | } | |
181 | if_chain! { | |
182 | if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); | |
183 | if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]); | |
184 | then { | |
185 | span_lint_and_sugg( | |
186 | cx, | |
187 | UNNECESSARY_TO_OWNED, | |
188 | parent.span, | |
2b03887a | 189 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a | 190 | "use", |
2b03887a | 191 | format!("{receiver_snippet}.as_ref()"), |
a2a8927a XL |
192 | Applicability::MachineApplicable, |
193 | ); | |
194 | return true; | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | false | |
200 | } | |
201 | ||
202 | /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its | |
203 | /// call of a `to_owned`-like function is unnecessary. | |
04454e1e FG |
204 | fn check_into_iter_call_arg( |
205 | cx: &LateContext<'_>, | |
206 | expr: &Expr<'_>, | |
207 | method_name: Symbol, | |
208 | receiver: &Expr<'_>, | |
487cf647 | 209 | msrv: &Msrv, |
04454e1e | 210 | ) -> bool { |
a2a8927a XL |
211 | if_chain! { |
212 | if let Some(parent) = get_parent_expr(cx, expr); | |
213 | if let Some(callee_def_id) = fn_def_id(cx, parent); | |
214 | if is_into_iter(cx, callee_def_id); | |
215 | if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); | |
216 | let parent_ty = cx.typeck_results().expr_ty(parent); | |
217 | if implements_trait(cx, parent_ty, iterator_trait_id, &[]); | |
218 | if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); | |
219 | if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); | |
220 | then { | |
5e7ed085 | 221 | if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { |
a2a8927a XL |
222 | return true; |
223 | } | |
487cf647 | 224 | let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { |
923072b8 FG |
225 | "copied" |
226 | } else { | |
227 | "cloned" | |
228 | }; | |
5099ac24 FG |
229 | // The next suggestion may be incorrect because the removal of the `to_owned`-like |
230 | // function could cause the iterator to hold a reference to a resource that is used | |
231 | // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. | |
a2a8927a XL |
232 | span_lint_and_sugg( |
233 | cx, | |
234 | UNNECESSARY_TO_OWNED, | |
235 | parent.span, | |
2b03887a | 236 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a | 237 | "use", |
2b03887a | 238 | format!("{receiver_snippet}.iter().{cloned_or_copied}()"), |
5099ac24 | 239 | Applicability::MaybeIncorrect, |
a2a8927a XL |
240 | ); |
241 | return true; | |
242 | } | |
243 | } | |
244 | false | |
245 | } | |
246 | ||
247 | /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call | |
248 | /// of a `to_owned`-like function is unnecessary. | |
5099ac24 | 249 | fn check_other_call_arg<'tcx>( |
a2a8927a XL |
250 | cx: &LateContext<'tcx>, |
251 | expr: &'tcx Expr<'tcx>, | |
252 | method_name: Symbol, | |
253 | receiver: &'tcx Expr<'tcx>, | |
254 | ) -> bool { | |
255 | if_chain! { | |
256 | if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr); | |
add651ee FG |
257 | if let Some((callee_def_id, _, recv, call_args)) = get_callee_generic_args_and_args(cx, maybe_call); |
258 | let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); | |
f2b60f7d | 259 | if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id); |
a2a8927a | 260 | if let Some(input) = fn_sig.inputs().get(i); |
5099ac24 | 261 | let (input, n_refs) = peel_mid_ty_refs(*input); |
f2b60f7d | 262 | if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input); |
a2a8927a XL |
263 | if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait(); |
264 | if let [trait_predicate] = trait_predicates | |
265 | .iter() | |
266 | .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id) | |
267 | .collect::<Vec<_>>()[..]; | |
268 | if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref); | |
269 | if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); | |
f2b60f7d | 270 | if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; |
a2a8927a | 271 | let receiver_ty = cx.typeck_results().expr_ty(receiver); |
a2a8927a XL |
272 | // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match |
273 | // `Target = T`. | |
487cf647 FG |
274 | if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { |
275 | Some((n_refs, receiver_ty)) | |
276 | } else if trait_predicate.def_id() != deref_trait_id { | |
fe692bf9 | 277 | Some((1, Ty::new_ref(cx.tcx, |
487cf647 FG |
278 | cx.tcx.lifetimes.re_erased, |
279 | ty::TypeAndMut { | |
280 | ty: receiver_ty, | |
281 | mutbl: Mutability::Not, | |
282 | }, | |
283 | ))) | |
284 | } else { | |
285 | None | |
286 | }; | |
287 | if can_change_type(cx, maybe_arg, receiver_ty); | |
a2a8927a XL |
288 | if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); |
289 | then { | |
290 | span_lint_and_sugg( | |
291 | cx, | |
292 | UNNECESSARY_TO_OWNED, | |
293 | maybe_arg.span, | |
2b03887a | 294 | &format!("unnecessary use of `{method_name}`"), |
a2a8927a | 295 | "use", |
2b03887a | 296 | format!("{:&>n_refs$}{receiver_snippet}", ""), |
a2a8927a XL |
297 | Applicability::MachineApplicable, |
298 | ); | |
299 | return true; | |
300 | } | |
301 | } | |
302 | false | |
303 | } | |
304 | ||
305 | /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such | |
306 | /// expression found (if any) along with the immediately prior expression. | |
5099ac24 | 307 | fn skip_addr_of_ancestors<'tcx>( |
a2a8927a XL |
308 | cx: &LateContext<'tcx>, |
309 | mut expr: &'tcx Expr<'tcx>, | |
310 | ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { | |
311 | while let Some(parent) = get_parent_expr(cx, expr) { | |
312 | if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind { | |
313 | expr = parent; | |
314 | } else { | |
315 | return Some((parent, expr)); | |
316 | } | |
317 | } | |
318 | None | |
319 | } | |
320 | ||
321 | /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, | |
add651ee FG |
322 | /// `GenericArgs`, and arguments. |
323 | fn get_callee_generic_args_and_args<'tcx>( | |
a2a8927a XL |
324 | cx: &LateContext<'tcx>, |
325 | expr: &'tcx Expr<'tcx>, | |
add651ee FG |
326 | ) -> Option<( |
327 | DefId, | |
328 | GenericArgsRef<'tcx>, | |
329 | Option<&'tcx Expr<'tcx>>, | |
330 | &'tcx [Expr<'tcx>], | |
331 | )> { | |
a2a8927a XL |
332 | if_chain! { |
333 | if let ExprKind::Call(callee, args) = expr.kind; | |
334 | let callee_ty = cx.typeck_results().expr_ty(callee); | |
335 | if let ty::FnDef(callee_def_id, _) = callee_ty.kind(); | |
336 | then { | |
add651ee FG |
337 | let generic_args = cx.typeck_results().node_args(callee.hir_id); |
338 | return Some((*callee_def_id, generic_args, None, args)); | |
a2a8927a XL |
339 | } |
340 | } | |
341 | if_chain! { | |
f2b60f7d | 342 | if let ExprKind::MethodCall(_, recv, args, _) = expr.kind; |
a2a8927a XL |
343 | if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); |
344 | then { | |
add651ee FG |
345 | let generic_args = cx.typeck_results().node_args(expr.hir_id); |
346 | return Some((method_def_id, generic_args, Some(recv), args)); | |
a2a8927a XL |
347 | } |
348 | } | |
349 | None | |
350 | } | |
351 | ||
352 | /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. | |
5099ac24 | 353 | fn get_input_traits_and_projections<'tcx>( |
a2a8927a XL |
354 | cx: &LateContext<'tcx>, |
355 | callee_def_id: DefId, | |
356 | input: Ty<'tcx>, | |
357 | ) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) { | |
358 | let mut trait_predicates = Vec::new(); | |
359 | let mut projection_predicates = Vec::new(); | |
f2b60f7d | 360 | for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { |
a2a8927a | 361 | match predicate.kind().skip_binder() { |
fe692bf9 | 362 | ClauseKind::Trait(trait_predicate) => { |
f2b60f7d | 363 | if trait_predicate.trait_ref.self_ty() == input { |
a2a8927a XL |
364 | trait_predicates.push(trait_predicate); |
365 | } | |
366 | }, | |
fe692bf9 | 367 | ClauseKind::Projection(projection_predicate) => { |
f2b60f7d | 368 | if projection_predicate.projection_ty.self_ty() == input { |
a2a8927a XL |
369 | projection_predicates.push(projection_predicate); |
370 | } | |
371 | }, | |
372 | _ => {}, | |
373 | } | |
374 | } | |
375 | (trait_predicates, projection_predicates) | |
376 | } | |
377 | ||
f2b60f7d FG |
378 | fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool { |
379 | for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) { | |
380 | match node { | |
381 | Node::Stmt(_) => return true, | |
382 | Node::Block(..) => continue, | |
383 | Node::Item(item) => { | |
384 | if let ItemKind::Fn(_, _, body_id) = &item.kind | |
9ffffee4 | 385 | && let output_ty = return_ty(cx, item.owner_id) |
353b0b11 FG |
386 | && let inherited = Inherited::new(cx.tcx, item.owner_id.def_id) |
387 | && let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id) | |
388 | && fn_ctxt.can_coerce(ty, output_ty) | |
389 | { | |
f2b60f7d FG |
390 | if has_lifetime(output_ty) && has_lifetime(ty) { |
391 | return false; | |
392 | } | |
393 | let body = cx.tcx.hir().body(*body_id); | |
394 | let body_expr = &body.value; | |
395 | let mut count = 0; | |
396 | return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 }); | |
397 | } | |
a2a8927a | 398 | } |
f2b60f7d | 399 | Node::Expr(parent_expr) => { |
add651ee FG |
400 | if let Some((callee_def_id, call_generic_args, recv, call_args)) |
401 | = get_callee_generic_args_and_args(cx, parent_expr) | |
f2b60f7d | 402 | { |
add651ee | 403 | // FIXME: the `instantiate_identity()` below seems incorrect, since we eventually |
781aab86 | 404 | // call `tcx.try_instantiate_and_normalize_erasing_regions` further down |
49aad941 | 405 | // (i.e., we are explicitly not in the identity context). |
add651ee | 406 | let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder(); |
f2b60f7d FG |
407 | if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) |
408 | && let Some(param_ty) = fn_sig.inputs().get(arg_index) | |
409 | && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind() | |
487cf647 | 410 | // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 |
add651ee | 411 | && (*param_index as usize) < call_generic_args.len() |
f2b60f7d FG |
412 | { |
413 | if fn_sig | |
414 | .inputs() | |
415 | .iter() | |
416 | .enumerate() | |
417 | .filter(|(i, _)| *i != arg_index) | |
418 | .any(|(_, ty)| ty.contains(*param_ty)) | |
419 | { | |
420 | return false; | |
421 | } | |
422 | ||
423 | let mut trait_predicates = cx.tcx.param_env(callee_def_id) | |
424 | .caller_bounds().iter().filter(|predicate| { | |
fe692bf9 | 425 | if let ClauseKind::Trait(trait_predicate) |
487cf647 FG |
426 | = predicate.kind().skip_binder() |
427 | && trait_predicate.trait_ref.self_ty() == *param_ty | |
428 | { | |
429 | true | |
430 | } else { | |
f2b60f7d FG |
431 | false |
432 | } | |
433 | }); | |
434 | ||
add651ee FG |
435 | let new_subst = cx.tcx.mk_args_from_iter( |
436 | call_generic_args.iter() | |
f2b60f7d FG |
437 | .enumerate() |
438 | .map(|(i, t)| | |
439 | if i == (*param_index as usize) { | |
440 | GenericArg::from(ty) | |
441 | } else { | |
442 | t | |
443 | })); | |
444 | ||
445 | if trait_predicates.any(|predicate| { | |
add651ee | 446 | let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst); |
487cf647 | 447 | let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate); |
2b03887a | 448 | !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) |
f2b60f7d FG |
449 | }) { |
450 | return false; | |
451 | } | |
452 | ||
453 | let output_ty = fn_sig.output(); | |
454 | if output_ty.contains(*param_ty) { | |
781aab86 | 455 | if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions( |
fe692bf9 | 456 | new_subst, cx.param_env, EarlyBinder::bind(output_ty)) { |
f2b60f7d FG |
457 | expr = parent_expr; |
458 | ty = new_ty; | |
459 | continue; | |
460 | } | |
461 | return false; | |
462 | } | |
463 | ||
464 | return true; | |
465 | } | |
466 | } else if let ExprKind::Block(..) = parent_expr.kind { | |
467 | continue; | |
468 | } | |
469 | return false; | |
470 | }, | |
471 | _ => return false, | |
472 | } | |
473 | } | |
474 | ||
475 | false | |
476 | } | |
477 | ||
478 | fn has_lifetime(ty: Ty<'_>) -> bool { | |
479 | ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_))) | |
a2a8927a XL |
480 | } |
481 | ||
482 | /// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`. | |
483 | fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { | |
484 | (method_name.as_str() == "cloned" || method_name.as_str() == "copied") | |
485 | && is_diag_trait_item(cx, method_def_id, sym::Iterator) | |
486 | } | |
487 | ||
488 | /// Returns true if the named method can be used to convert the receiver to its "owned" | |
489 | /// representation. | |
f2b60f7d | 490 | fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { |
923072b8 | 491 | is_clone_like(cx, method_name.as_str(), method_def_id) |
a2a8927a | 492 | || is_cow_into_owned(cx, method_name, method_def_id) |
f2b60f7d | 493 | || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) |
a2a8927a XL |
494 | } |
495 | ||
496 | /// Returns true if the named method is `Cow::into_owned`. | |
497 | fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { | |
498 | method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow) | |
499 | } | |
500 | ||
f2b60f7d | 501 | /// Returns true if the named method is `ToString::to_string` and it's called on a type that |
487cf647 | 502 | /// is string-like i.e. implements `AsRef<str>` or `Deref<Target = str>`. |
f2b60f7d FG |
503 | fn is_to_string_on_string_like<'a>( |
504 | cx: &LateContext<'_>, | |
505 | call_expr: &'a Expr<'a>, | |
506 | method_name: Symbol, | |
507 | method_def_id: DefId, | |
508 | ) -> bool { | |
509 | if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) { | |
510 | return false; | |
511 | } | |
512 | ||
add651ee FG |
513 | if let Some(args) = cx.typeck_results().node_args_opt(call_expr.hir_id) |
514 | && let [generic_arg] = args.as_slice() | |
f2b60f7d FG |
515 | && let GenericArgKind::Type(ty) = generic_arg.unpack() |
516 | && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) | |
517 | && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef) | |
487cf647 | 518 | && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) || |
f2b60f7d FG |
519 | implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) { |
520 | true | |
521 | } else { | |
522 | false | |
523 | } | |
a2a8927a | 524 | } |