Skip to content

Commit 4992548

Browse files
committed
Auto merge of rust-lang#8271 - Jarcho:ptr_arg_214, r=flip1995
Check usages in `ptr_arg` fixes rust-lang#214 fixes rust-lang#1981 fixes rust-lang#3381 fixes rust-lang#6406 fixes rust-lang#6964 This does not take into account the return type of the function currently, so `(&Vec<_>) -> &Vec<_>` functions may still be false positives. The name given for the type also has to match the real type name, so `type Foo = Vec<u32>` won't trigger the lint, but `type Vec = Vec<u32>` will. I'm not sure if this is the best way to handle this, or if a note about the actual type should be added instead. changelog: Check if the argument is used in a way which requires the original type in `ptr_arg` changelog: Lint mutable references in `ptr_arg`
2 parents f4709e6 + 048297b commit 4992548

11 files changed

+649
-326
lines changed

clippy_lints/src/ptr.rs

Lines changed: 419 additions & 194 deletions
Large diffs are not rendered by default.

clippy_lints/src/unnested_or_patterns.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
291291
fn extend_with_struct_pat(
292292
qself1: &Option<ast::QSelf>,
293293
path1: &ast::Path,
294-
fps1: &mut Vec<ast::PatField>,
294+
fps1: &mut [ast::PatField],
295295
rest1: bool,
296296
start: usize,
297297
alternatives: &mut Vec<P<Pat>>,
@@ -332,7 +332,7 @@ fn extend_with_struct_pat(
332332
/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
333333
/// where `~` denotes semantic equality.
334334
fn extend_with_matching_product(
335-
targets: &mut Vec<P<Pat>>,
335+
targets: &mut [P<Pat>],
336336
start: usize,
337337
alternatives: &mut Vec<P<Pat>>,
338338
predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,

clippy_utils/src/lib.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,7 +1850,8 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
18501850
}
18511851

18521852
/// Gets the node where an expression is either used, or it's type is unified with another branch.
1853-
pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
1853+
/// Returns both the node and the `HirId` of the closest child node.
1854+
pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
18541855
let mut child_id = expr.hir_id;
18551856
let mut iter = tcx.hir().parent_iter(child_id);
18561857
loop {
@@ -1862,9 +1863,9 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>
18621863
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
18631864
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
18641865
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
1865-
_ => break Some(Node::Expr(expr)),
1866+
_ => break Some((Node::Expr(expr), child_id)),
18661867
},
1867-
Some((_, node)) => break Some(node),
1868+
Some((_, node)) => break Some((node, child_id)),
18681869
}
18691870
}
18701871
}
@@ -1873,18 +1874,21 @@ pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>
18731874
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
18741875
!matches!(
18751876
get_expr_use_or_unification_node(tcx, expr),
1876-
None | Some(Node::Stmt(Stmt {
1877-
kind: StmtKind::Expr(_)
1878-
| StmtKind::Semi(_)
1879-
| StmtKind::Local(Local {
1880-
pat: Pat {
1881-
kind: PatKind::Wild,
1877+
None | Some((
1878+
Node::Stmt(Stmt {
1879+
kind: StmtKind::Expr(_)
1880+
| StmtKind::Semi(_)
1881+
| StmtKind::Local(Local {
1882+
pat: Pat {
1883+
kind: PatKind::Wild,
1884+
..
1885+
},
18821886
..
1883-
},
1884-
..
1885-
}),
1886-
..
1887-
}))
1887+
}),
1888+
..
1889+
}),
1890+
_
1891+
))
18881892
)
18891893
}
18901894

clippy_utils/src/ty.rs

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@
55
use rustc_ast::ast::Mutability;
66
use rustc_data_structures::fx::FxHashMap;
77
use rustc_hir as hir;
8+
use rustc_hir::def::{CtorKind, DefKind, Res};
89
use rustc_hir::def_id::DefId;
9-
use rustc_hir::{TyKind, Unsafety};
10+
use rustc_hir::{Expr, TyKind, Unsafety};
1011
use rustc_infer::infer::TyCtxtInferExt;
1112
use rustc_lint::LateContext;
12-
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
13-
use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy};
13+
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
14+
use rustc_middle::ty::{
15+
self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
16+
};
1417
use rustc_span::symbol::Ident;
1518
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
1619
use rustc_trait_selection::infer::InferCtxtExt;
1720
use rustc_trait_selection::traits::query::normalize::AtExt;
1821
use std::iter;
1922

20-
use crate::{match_def_path, must_use_attr};
23+
use crate::{expr_path_res, match_def_path, must_use_attr};
2124

2225
// Checks if the given type implements copy.
2326
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@@ -410,3 +413,105 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
410413
})
411414
.flatten()
412415
}
416+
417+
/// A signature for a function like type.
418+
#[derive(Clone, Copy)]
419+
pub enum ExprFnSig<'tcx> {
420+
Sig(Binder<'tcx, FnSig<'tcx>>),
421+
Closure(Binder<'tcx, FnSig<'tcx>>),
422+
Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
423+
}
424+
impl<'tcx> ExprFnSig<'tcx> {
425+
/// Gets the argument type at the given offset.
426+
pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> {
427+
match self {
428+
Self::Sig(sig) => sig.input(i),
429+
Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
430+
Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_element_ty(i).unwrap()),
431+
}
432+
}
433+
434+
/// Gets the result type, if one could be found. Note that the result type of a trait may not be
435+
/// specified.
436+
pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
437+
match self {
438+
Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()),
439+
Self::Trait(_, output) => output,
440+
}
441+
}
442+
}
443+
444+
/// If the expression is function like, get the signature for it.
445+
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
446+
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) {
447+
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
448+
} else {
449+
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
450+
match *ty.kind() {
451+
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
452+
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))),
453+
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
454+
ty::Dynamic(bounds, _) => {
455+
let lang_items = cx.tcx.lang_items();
456+
match bounds.principal() {
457+
Some(bound)
458+
if Some(bound.def_id()) == lang_items.fn_trait()
459+
|| Some(bound.def_id()) == lang_items.fn_once_trait()
460+
|| Some(bound.def_id()) == lang_items.fn_mut_trait() =>
461+
{
462+
let output = bounds
463+
.projection_bounds()
464+
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
465+
.map(|p| p.map_bound(|p| p.ty));
466+
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
467+
},
468+
_ => None,
469+
}
470+
},
471+
ty::Param(_) | ty::Projection(..) => {
472+
let mut inputs = None;
473+
let mut output = None;
474+
let lang_items = cx.tcx.lang_items();
475+
476+
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
477+
let mut is_input = false;
478+
if let Some(ty) = pred
479+
.kind()
480+
.map_bound(|pred| match pred {
481+
PredicateKind::Trait(p)
482+
if (lang_items.fn_trait() == Some(p.def_id())
483+
|| lang_items.fn_mut_trait() == Some(p.def_id())
484+
|| lang_items.fn_once_trait() == Some(p.def_id()))
485+
&& p.self_ty() == ty =>
486+
{
487+
is_input = true;
488+
Some(p.trait_ref.substs.type_at(1))
489+
},
490+
PredicateKind::Projection(p)
491+
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
492+
&& p.projection_ty.self_ty() == ty =>
493+
{
494+
is_input = false;
495+
Some(p.ty)
496+
},
497+
_ => None,
498+
})
499+
.transpose()
500+
{
501+
if is_input && inputs.is_none() {
502+
inputs = Some(ty);
503+
} else if !is_input && output.is_none() {
504+
output = Some(ty);
505+
} else {
506+
// Multiple different fn trait impls. Is this even allowed?
507+
return None;
508+
}
509+
}
510+
}
511+
512+
inputs.map(|ty| ExprFnSig::Trait(ty, output))
513+
},
514+
_ => None,
515+
}
516+
}
517+
}

tests/ui/ptr_arg.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ fn do_vec(x: &Vec<i64>) {
99
}
1010

1111
fn do_vec_mut(x: &mut Vec<i64>) {
12-
// no error here
1312
//Nothing here
1413
}
1514

@@ -18,7 +17,6 @@ fn do_str(x: &String) {
1817
}
1918

2019
fn do_str_mut(x: &mut String) {
21-
// no error here
2220
//Nothing here either
2321
}
2422

@@ -27,7 +25,6 @@ fn do_path(x: &PathBuf) {
2725
}
2826

2927
fn do_path_mut(x: &mut PathBuf) {
30-
// no error here
3128
//Nothing here either
3229
}
3330

@@ -52,7 +49,7 @@ fn cloned(x: &Vec<u8>) -> Vec<u8> {
5249
let e = x.clone();
5350
let f = e.clone(); // OK
5451
let g = x;
55-
let h = g.clone(); // Alas, we cannot reliably detect this without following data.
52+
let h = g.clone();
5653
let i = (e).clone();
5754
x.clone()
5855
}
@@ -156,6 +153,30 @@ mod issue6509 {
156153
}
157154
}
158155

156+
fn mut_vec_slice_methods(v: &mut Vec<u32>) {
157+
v.copy_within(1..5, 10);
158+
}
159+
160+
fn mut_vec_vec_methods(v: &mut Vec<u32>) {
161+
v.clear();
162+
}
163+
164+
fn vec_contains(v: &Vec<u32>) -> bool {
165+
[vec![], vec![0]].as_slice().contains(v)
166+
}
167+
168+
fn fn_requires_vec(v: &Vec<u32>) -> bool {
169+
vec_contains(v)
170+
}
171+
172+
fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) {
173+
f(v);
174+
}
175+
176+
fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) {
177+
f(v);
178+
}
179+
159180
// No error for types behind an alias (#7699)
160181
type A = Vec<u8>;
161182
fn aliased(a: &A) {}

0 commit comments

Comments
 (0)