]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / unnecessary_to_owned.rs
CommitLineData
a2a8927a
XL
1use super::implicit_clone::is_clone_like;
2use super::unnecessary_iter_cloned::{self, is_into_iter};
3use clippy_utils::diagnostics::span_lint_and_sugg;
487cf647 4use clippy_utils::msrvs::{self, Msrv};
a2a8927a 5use clippy_utils::source::snippet_opt;
487cf647 6use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
f2b60f7d
FG
7use clippy_utils::visitors::find_all_ret_expressions;
8use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
a2a8927a 9use rustc_errors::Applicability;
add651ee
FG
10use rustc_hir::def_id::DefId;
11use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node};
2b03887a 12use rustc_hir_typeck::{FnCtxt, Inherited};
f2b60f7d 13use rustc_infer::infer::TyCtxtInferExt;
a2a8927a
XL
14use rustc_lint::LateContext;
15use rustc_middle::mir::Mutability;
16use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
add651ee
FG
17use rustc_middle::ty::{
18 self, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate,
19 TraitPredicate, Ty,
20};
a2a8927a 21use rustc_span::{sym, Symbol};
add651ee
FG
22use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
23use rustc_trait_selection::traits::{Obligation, ObligationCause};
a2a8927a
XL
24
25use super::UNNECESSARY_TO_OWNED;
26
04454e1e
FG
27pub 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)]
61fn 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
204fn 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 249fn 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 307fn 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.
323fn 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 353fn 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
378fn 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
478fn 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`.
483fn 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 490fn 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`.
497fn 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
503fn 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}