Skip to content

Commit fe3eae3

Browse files
committed
Auto merge of #114938 - flip1995:clippy_backport, r=matthiaskrgr
Clippy backport r? `@Manishearth` This is the accompanying PR to #114937. This needs to be merged before tomorrow, so that it gets into master, before beta is branched. The second commit is pretty much an out-of cycle sync, so that we don't get backport-debt for next release cycle right away. cc `@Mark-Simulacrum` also mentioning you here, to make sure this is included in the beta branching.
2 parents 9b41190 + ff89efe commit fe3eae3

8 files changed

+201
-36
lines changed

src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs

+81-22
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
55
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
66
use rustc_errors::Applicability;
77
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
8-
use rustc_hir::{Body, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath};
8+
use rustc_hir::{
9+
Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node, PatKind, QPath,
10+
};
911
use rustc_hir_typeck::expr_use_visitor as euv;
1012
use rustc_infer::infer::TyCtxtInferExt;
1113
use rustc_lint::{LateContext, LateLintPass};
1214
use rustc_middle::hir::map::associated_body;
1315
use rustc_middle::hir::nested_filter::OnlyBodies;
1416
use rustc_middle::mir::FakeReadCause;
15-
use rustc_middle::ty::{self, Ty, UpvarId, UpvarPath};
17+
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath};
1618
use rustc_session::{declare_tool_lint, impl_lint_pass};
1719
use rustc_span::def_id::LocalDefId;
1820
use rustc_span::symbol::kw;
@@ -147,22 +149,36 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
147149
// Collect variables mutably used and spans which will need dereferencings from the
148150
// function body.
149151
let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = {
150-
let mut ctx = MutablyUsedVariablesCtxt::default();
152+
let mut ctx = MutablyUsedVariablesCtxt {
153+
mutably_used_vars: HirIdSet::default(),
154+
prev_bind: None,
155+
prev_move_to_closure: HirIdSet::default(),
156+
aliases: HirIdMap::default(),
157+
async_closures: FxHashSet::default(),
158+
tcx: cx.tcx,
159+
};
151160
let infcx = cx.tcx.infer_ctxt().build();
152161
euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
153162
if is_async {
154-
let closures = ctx.async_closures.clone();
155-
let hir = cx.tcx.hir();
156-
for closure in closures {
157-
ctx.prev_bind = None;
158-
ctx.prev_move_to_closure.clear();
159-
if let Some(body) = hir
160-
.find_by_def_id(closure)
161-
.and_then(associated_body)
162-
.map(|(_, body_id)| hir.body(body_id))
163-
{
164-
euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results())
165-
.consume_body(body);
163+
let mut checked_closures = FxHashSet::default();
164+
while !ctx.async_closures.is_empty() {
165+
let closures = ctx.async_closures.clone();
166+
ctx.async_closures.clear();
167+
let hir = cx.tcx.hir();
168+
for closure in closures {
169+
if !checked_closures.insert(closure) {
170+
continue;
171+
}
172+
ctx.prev_bind = None;
173+
ctx.prev_move_to_closure.clear();
174+
if let Some(body) = hir
175+
.find_by_def_id(closure)
176+
.and_then(associated_body)
177+
.map(|(_, body_id)| hir.body(body_id))
178+
{
179+
euv::ExprUseVisitor::new(&mut ctx, &infcx, closure, cx.param_env, cx.typeck_results())
180+
.consume_body(body);
181+
}
166182
}
167183
}
168184
}
@@ -225,26 +241,44 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
225241
}
226242
}
227243

228-
#[derive(Default)]
229-
struct MutablyUsedVariablesCtxt {
244+
struct MutablyUsedVariablesCtxt<'tcx> {
230245
mutably_used_vars: HirIdSet,
231246
prev_bind: Option<HirId>,
232247
prev_move_to_closure: HirIdSet,
233248
aliases: HirIdMap<HirId>,
234249
async_closures: FxHashSet<LocalDefId>,
250+
tcx: TyCtxt<'tcx>,
235251
}
236252

237-
impl MutablyUsedVariablesCtxt {
253+
impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
238254
fn add_mutably_used_var(&mut self, mut used_id: HirId) {
239255
while let Some(id) = self.aliases.get(&used_id) {
240256
self.mutably_used_vars.insert(used_id);
241257
used_id = *id;
242258
}
243259
self.mutably_used_vars.insert(used_id);
244260
}
261+
262+
fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool {
263+
while let Some(id) = self.aliases.get(&target) {
264+
if *id == alias {
265+
return true;
266+
}
267+
target = *id;
268+
}
269+
false
270+
}
271+
272+
fn add_alias(&mut self, alias: HirId, target: HirId) {
273+
// This is to prevent alias loop.
274+
if alias == target || self.would_be_alias_cycle(alias, target) {
275+
return;
276+
}
277+
self.aliases.insert(alias, target);
278+
}
245279
}
246280

247-
impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
281+
impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
248282
fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
249283
if let euv::Place {
250284
base:
@@ -259,7 +293,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
259293
{
260294
if let Some(bind_id) = self.prev_bind.take() {
261295
if bind_id != *vid {
262-
self.aliases.insert(bind_id, *vid);
296+
self.add_alias(bind_id, *vid);
263297
}
264298
} else if !self.prev_move_to_closure.contains(vid)
265299
&& matches!(base_ty.ref_mutability(), Some(Mutability::Mut))
@@ -289,14 +323,38 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
289323
{
290324
self.add_mutably_used_var(*vid);
291325
}
326+
} else if borrow == ty::ImmBorrow {
327+
// If there is an `async block`, it'll contain a call to a closure which we need to
328+
// go into to ensure all "mutate" checks are found.
329+
if let Node::Expr(Expr {
330+
kind:
331+
ExprKind::Call(
332+
_,
333+
[
334+
Expr {
335+
kind: ExprKind::Closure(Closure { def_id, .. }),
336+
..
337+
},
338+
],
339+
),
340+
..
341+
}) = self.tcx.hir().get(cmt.hir_id)
342+
{
343+
self.async_closures.insert(*def_id);
344+
}
292345
}
293346
}
294347

295348
fn mutate(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _id: HirId) {
296349
self.prev_bind = None;
297350
if let euv::Place {
298351
projections,
299-
base: euv::PlaceBase::Local(vid),
352+
base:
353+
euv::PlaceBase::Local(vid)
354+
| euv::PlaceBase::Upvar(UpvarId {
355+
var_path: UpvarPath { hir_id: vid },
356+
..
357+
}),
300358
..
301359
} = &cmt.place
302360
{
@@ -329,8 +387,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
329387
// Seems like we are inside an async function. We need to store the closure `DefId`
330388
// to go through it afterwards.
331389
self.async_closures.insert(inner);
332-
self.aliases.insert(cmt.hir_id, *vid);
390+
self.add_alias(cmt.hir_id, *vid);
333391
self.prev_move_to_closure.insert(*vid);
392+
self.prev_bind = None;
334393
}
335394
}
336395
}

src/tools/clippy/clippy_lints/src/useless_conversion.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
55
use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
8+
use rustc_hir::def::DefKind;
89
use rustc_hir::def_id::DefId;
910
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
1011
use rustc_lint::{LateContext, LateLintPass};
@@ -39,6 +40,7 @@ declare_clippy_lint! {
3940
#[derive(Default)]
4041
pub struct UselessConversion {
4142
try_desugar_arm: Vec<HirId>,
43+
expn_depth: u32,
4244
}
4345

4446
impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
@@ -105,6 +107,7 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -
105107
impl<'tcx> LateLintPass<'tcx> for UselessConversion {
106108
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
107109
if e.span.from_expansion() {
110+
self.expn_depth += 1;
108111
return;
109112
}
110113

@@ -150,9 +153,14 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
150153
{
151154
if let Some(parent) = get_parent_expr(cx, e) {
152155
let parent_fn = match parent.kind {
153-
ExprKind::Call(recv, args) if let ExprKind::Path(ref qpath) = recv.kind => {
154-
cx.qpath_res(qpath, recv.hir_id).opt_def_id()
155-
.map(|did| (did, args, MethodOrFunction::Function))
156+
ExprKind::Call(recv, args)
157+
if let ExprKind::Path(ref qpath) = recv.kind
158+
&& let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
159+
// make sure that the path indeed points to a fn-like item, so that
160+
// `fn_sig` does not ICE. (see #11065)
161+
&& cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
162+
{
163+
Some((did, args, MethodOrFunction::Function))
156164
}
157165
ExprKind::MethodCall(.., args, _) => {
158166
cx.typeck_results().type_dependent_def_id(parent.hir_id)
@@ -168,6 +176,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
168176
&& let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
169177
&& let ty::Param(param) = into_iter_param.kind()
170178
&& let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
179+
&& self.expn_depth == 0
171180
{
172181
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
173182
// `foo.into_iter().into_iter()`
@@ -303,5 +312,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
303312
if Some(&e.hir_id) == self.try_desugar_arm.last() {
304313
self.try_desugar_arm.pop();
305314
}
315+
if e.span.from_expansion() {
316+
self.expn_depth -= 1;
317+
}
306318
}
307319
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![warn(clippy::useless_conversion)]
2+
3+
use std::iter::FromIterator;
4+
use std::option::IntoIter as OptionIter;
5+
6+
fn eq<T: Eq>(a: T, b: T) -> bool {
7+
a == b
8+
}
9+
10+
macro_rules! tests {
11+
($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
12+
const C: $ty = $expr;
13+
assert!(eq(C($($test),*), $expr($($test),*)));
14+
})+})
15+
}
16+
17+
tests! {
18+
FromIterator::from_iter, fn(OptionIter<i32>) -> Vec<i32>, (Some(5).into_iter());
19+
}

src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs

+35
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,41 @@ mod foo {
196196
//~| NOTE: this is cfg-gated and may require further changes
197197
}
198198

199+
// Should not warn.
200+
async fn inner_async(x: &mut i32, y: &mut u32) {
201+
async {
202+
*y += 1;
203+
*x += 1;
204+
}
205+
.await;
206+
}
207+
208+
async fn inner_async2(x: &mut i32, y: &mut u32) {
209+
//~^ ERROR: this argument is a mutable reference, but not used mutably
210+
async {
211+
*x += 1;
212+
}
213+
.await;
214+
}
215+
216+
async fn inner_async3(x: &mut i32, y: &mut u32) {
217+
//~^ ERROR: this argument is a mutable reference, but not used mutably
218+
async {
219+
*y += 1;
220+
}
221+
.await;
222+
}
223+
224+
// Should not warn.
225+
async fn async_vec(b: &mut Vec<bool>) {
226+
b.append(&mut vec![]);
227+
}
228+
229+
// Should not warn.
230+
async fn async_vec2(b: &mut Vec<bool>) {
231+
b.push(true);
232+
}
233+
199234
fn main() {
200235
let mut u = 0;
201236
let mut v = vec![0];

src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,17 @@ LL | fn cfg_warn(s: &mut u32) {}
9494
|
9595
= note: this is cfg-gated and may require further changes
9696

97-
error: aborting due to 15 previous errors
97+
error: this argument is a mutable reference, but not used mutably
98+
--> $DIR/needless_pass_by_ref_mut.rs:208:39
99+
|
100+
LL | async fn inner_async2(x: &mut i32, y: &mut u32) {
101+
| ^^^^^^^^ help: consider changing to: `&u32`
102+
103+
error: this argument is a mutable reference, but not used mutably
104+
--> $DIR/needless_pass_by_ref_mut.rs:216:26
105+
|
106+
LL | async fn inner_async3(x: &mut i32, y: &mut u32) {
107+
| ^^^^^^^^ help: consider changing to: `&i32`
108+
109+
error: aborting due to 17 previous errors
98110

src/tools/clippy/tests/ui/useless_conversion.fixed

+14
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ fn main() {
155155
let _ = vec![s4, s4, s4].into_iter();
156156
}
157157

158+
#[allow(dead_code)]
159+
fn issue11065_fp() {
160+
use std::option::IntoIter;
161+
fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
162+
163+
macro_rules! x {
164+
($e:expr) => {
165+
takes_into_iter($e);
166+
let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here
167+
};
168+
}
169+
x!(Some(5).into_iter());
170+
}
171+
158172
#[allow(dead_code)]
159173
fn explicit_into_iter_fn_arg() {
160174
fn a<T>(_: T) {}

src/tools/clippy/tests/ui/useless_conversion.rs

+14
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ fn main() {
155155
let _ = vec![s4, s4, s4].into_iter().into_iter();
156156
}
157157

158+
#[allow(dead_code)]
159+
fn issue11065_fp() {
160+
use std::option::IntoIter;
161+
fn takes_into_iter(_: impl IntoIterator<Item = i32>) {}
162+
163+
macro_rules! x {
164+
($e:expr) => {
165+
takes_into_iter($e);
166+
let _: IntoIter<i32> = $e; // removing `.into_iter()` leads to a type error here
167+
};
168+
}
169+
x!(Some(5).into_iter());
170+
}
171+
158172
#[allow(dead_code)]
159173
fn explicit_into_iter_fn_arg() {
160174
fn a<T>(_: T) {}

0 commit comments

Comments
 (0)