Skip to content

Commit 15495eb

Browse files
committed
Look at adjusted types instead of fn signature types in ptr_arg
1 parent 2b7d80b commit 15495eb

File tree

3 files changed

+75
-61
lines changed

3 files changed

+75
-61
lines changed

clippy_lints/src/ptr.rs

+40-60
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
22
use clippy_utils::source::SpanRangeExt;
3-
use clippy_utils::ty::expr_sig;
43
use clippy_utils::visitors::contains_unsafe_block;
54
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
65
use hir::LifetimeName;
76
use rustc_errors::{Applicability, MultiSpan};
8-
use rustc_hir::def_id::DefId;
97
use rustc_hir::hir_id::{HirId, HirIdMap};
108
use rustc_hir::intravisit::{walk_expr, Visitor};
119
use rustc_hir::{
@@ -323,7 +321,6 @@ struct PtrArg<'tcx> {
323321
idx: usize,
324322
emission_id: HirId,
325323
span: Span,
326-
ty_did: DefId,
327324
ty_name: Symbol,
328325
method_renames: &'static [(&'static str, &'static str)],
329326
ref_prefix: RefPrefix,
@@ -411,7 +408,6 @@ impl<'tcx> DerefTy<'tcx> {
411408
}
412409
}
413410

414-
#[expect(clippy::too_many_lines)]
415411
fn check_fn_args<'cx, 'tcx: 'cx>(
416412
cx: &'cx LateContext<'tcx>,
417413
fn_sig: ty::FnSig<'tcx>,
@@ -514,7 +510,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
514510
idx: i,
515511
emission_id,
516512
span: hir_ty.span,
517-
ty_did: adt.did(),
518513
ty_name: name.ident.name,
519514
method_renames,
520515
ref_prefix: RefPrefix { lt: *lt, mutability },
@@ -610,65 +605,50 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
610605
set_skip_flag();
611606
}
612607
},
613-
Some((Node::Expr(e), child_id)) => match e.kind {
614-
ExprKind::Call(f, expr_args) => {
615-
let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
616-
if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
617-
match *ty.skip_binder().peel_refs().kind() {
618-
ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
619-
ty::Param(_) => true,
620-
ty::Adt(def, _) => def.did() == args.ty_did,
621-
_ => false,
622-
}
623-
}) {
624-
// Passed to a function taking the non-dereferenced type.
625-
set_skip_flag();
626-
}
627-
},
628-
ExprKind::MethodCall(name, self_arg, expr_args, _) => {
629-
let i = iter::once(self_arg)
630-
.chain(expr_args.iter())
631-
.position(|arg| arg.hir_id == child_id)
632-
.unwrap_or(0);
633-
if i == 0 {
634-
// Check if the method can be renamed.
635-
let name = name.ident.as_str();
636-
if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
637-
result.replacements.push(PtrArgReplacement {
638-
expr_span: e.span,
639-
self_span: self_arg.span,
640-
replacement,
641-
});
642-
return;
643-
}
608+
Some((Node::Expr(use_expr), child_id)) => {
609+
if let ExprKind::Index(e, ..) = use_expr.kind
610+
&& e.hir_id == child_id
611+
{
612+
// Indexing works with both owned and its dereferenced type
613+
return;
614+
}
615+
616+
if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind
617+
&& receiver.hir_id == child_id
618+
{
619+
let name = name.ident.as_str();
620+
621+
// Check if the method can be renamed.
622+
if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
623+
result.replacements.push(PtrArgReplacement {
624+
expr_span: use_expr.span,
625+
self_span: receiver.span,
626+
replacement,
627+
});
628+
return;
644629
}
645630

646-
let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
647-
set_skip_flag();
631+
// Some methods exist on both `[T]` and `Vec<T>`, such as `len`, where the receiver type
632+
// doesn't coerce to a slice and our adjusted type check below isn't enough,
633+
// but it would still be valid to call with a slice
634+
if is_allowed_vec_method(self.cx, use_expr) {
648635
return;
649-
};
650-
651-
match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i]
652-
.peel_refs()
653-
.kind()
654-
{
655-
ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
656-
set_skip_flag();
657-
},
658-
ty::Param(_) => {
659-
set_skip_flag();
660-
},
661-
// If the types match check for methods which exist on both types. e.g. `Vec::len` and
662-
// `slice::len`
663-
ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => {
664-
set_skip_flag();
665-
},
666-
_ => (),
667636
}
668-
},
669-
// Indexing is fine for currently supported types.
670-
ExprKind::Index(e, _, _) if e.hir_id == child_id => (),
671-
_ => set_skip_flag(),
637+
}
638+
639+
let deref_ty = args.deref_ty.ty(self.cx);
640+
let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs();
641+
if adjusted_ty == deref_ty {
642+
return;
643+
}
644+
645+
if let ty::Dynamic(preds, ..) = adjusted_ty.kind()
646+
&& matches_preds(self.cx, deref_ty, preds)
647+
{
648+
return;
649+
}
650+
651+
set_skip_flag();
672652
},
673653
_ => set_skip_flag(),
674654
}

tests/ui/ptr_arg.rs

+22
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,25 @@ mod issue_11181 {
309309
extern "C" fn allowed(_v: &Vec<u32>) {}
310310
}
311311
}
312+
313+
mod issue_13308 {
314+
use std::ops::Deref;
315+
316+
fn repro(source: &str, destination: &mut String) {
317+
source.clone_into(destination);
318+
}
319+
fn repro2(source: &str, destination: &mut String) {
320+
ToOwned::clone_into(source, destination);
321+
}
322+
323+
fn h1(_: &<String as Deref>::Target) {}
324+
fn h2<T: Deref>(_: T, _: &T::Target) {}
325+
326+
// Other cases that are still ok to lint and ideally shouldn't regress
327+
fn good(v1: &String, v2: &String) {
328+
//~^ ERROR: writing `&String` instead of `&str`
329+
//~^^ ERROR: writing `&String` instead of `&str`
330+
h1(v1);
331+
h2(String::new(), v2);
332+
}
333+
}

tests/ui/ptr_arg.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -221,5 +221,17 @@ error: using a reference to `Cow` is not recommended
221221
LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str {
222222
| ^^^^^^^^^^^^^^^^ help: change this to: `&str`
223223

224-
error: aborting due to 25 previous errors
224+
error: writing `&String` instead of `&str` involves a new object where a slice will do
225+
--> tests/ui/ptr_arg.rs:327:17
226+
|
227+
LL | fn good(v1: &String, v2: &String) {
228+
| ^^^^^^^ help: change this to: `&str`
229+
230+
error: writing `&String` instead of `&str` involves a new object where a slice will do
231+
--> tests/ui/ptr_arg.rs:327:30
232+
|
233+
LL | fn good(v1: &String, v2: &String) {
234+
| ^^^^^^^ help: change this to: `&str`
235+
236+
error: aborting due to 27 previous errors
225237

0 commit comments

Comments
 (0)