Skip to content

Commit 2536745

Browse files
committed
Auto merge of rust-lang#13313 - y21:issue13308, r=Centri3
Look at adjusted types instead of fn signature types in `ptr_arg` This simplifies the implementation of the `ptr_arg` lint a bit and: Fixes rust-lang#13308 Fixes rust-lang#10612 Currently, the lint checks if e.g. a `&String` parameter is only used in contexts where a `&str` could work. Part of it worked by looking for path exprs to that parameter in function call position specifically, looking at the callee signature and checking if that parameter type without refs is `String` (or one of a bunch of other special cases). This simplified version removes the special casing of function calls and looking at the parameter type and instead just looks at the **adjusted type**, regardless of the expression it's in. This naturally also covers what the previous version was doing (if the expression is in a function call argument position expecting a `&str`, then it will have that adjustment. If it requires a `&String` then it won't have that adjustment and we won't lint). The linked issue was a FP that happened because the previous implementation simply didn't consider projection types in the signature type, but with this simplification we don't really need to consider that (because we aren't looking at function signatures at all anymore -- the `&mut Self::Owned` parameter type of `clone_into` is the already-instantiated and normalized type `&mut String`). changelog: none
2 parents 2b7d80b + 15495eb commit 2536745

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)