Skip to content

Commit 949712c

Browse files
committed
Reborrow mutable references in explicit_iter_loop
1 parent 482baf2 commit 949712c

24 files changed

+159
-58
lines changed

clippy_lints/src/attrs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
808808
}
809809

810810
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
811-
for item in items.iter() {
811+
for item in items {
812812
if let NestedMetaItem::MetaItem(meta) = item {
813813
if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
814814
continue;
@@ -842,7 +842,7 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
842842
}
843843

844844
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
845-
for item in items.iter() {
845+
for item in items {
846846
if let NestedMetaItem::MetaItem(meta) = item {
847847
if meta.has_name(sym!(features)) && let Some(val) = meta.value_str() {
848848
span_lint_and_sugg(

clippy_lints/src/default_numeric_fallback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
161161
let fields_def = &variant.fields;
162162

163163
// Push field type then visit each field expr.
164-
for field in fields.iter() {
164+
for field in *fields {
165165
let bound =
166166
fields_def
167167
.iter()

clippy_lints/src/lifetimes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
562562
// if the bounds define new lifetimes, they are fine to occur
563563
let allowed_lts = allowed_lts_from(pred.bound_generic_params);
564564
// now walk the bounds
565-
for bound in pred.bounds.iter() {
565+
for bound in pred.bounds {
566566
walk_param_bound(&mut visitor, bound);
567567
}
568568
// and check that all lifetimes are allowed

clippy_lints/src/loops/explicit_into_iter_loop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<
8484
"it is more concise to loop over containers instead of using explicit \
8585
iteration methods",
8686
"to write this more concisely, try",
87-
format!("{}{}", adjust.display(), object.to_string()),
87+
format!("{}{object}", adjust.display()),
8888
applicability,
8989
);
9090
}

clippy_lints/src/loops/explicit_iter_loop.rs

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,19 @@ pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<
3333

3434
let mut applicability = Applicability::MachineApplicable;
3535
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
36-
let prefix = match adjust {
37-
AdjustKind::None => "",
38-
AdjustKind::Borrow => "&",
39-
AdjustKind::BorrowMut => "&mut ",
40-
AdjustKind::Deref => "*",
41-
AdjustKind::Reborrow => "&*",
42-
AdjustKind::ReborrowMut => "&mut *",
43-
};
4436
span_lint_and_sugg(
4537
cx,
4638
EXPLICIT_ITER_LOOP,
4739
call_expr.span,
4840
"it is more concise to loop over references to containers instead of using explicit \
4941
iteration methods",
5042
"to write this more concisely, try",
51-
format!("{prefix}{object}"),
43+
format!("{}{object}", adjust.display()),
5244
applicability,
5345
);
5446
}
5547

48+
#[derive(Clone, Copy)]
5649
enum AdjustKind {
5750
None,
5851
Borrow,
@@ -76,16 +69,35 @@ impl AdjustKind {
7669
}
7770
}
7871

79-
fn reborrow(mutbl: AutoBorrowMutability) -> Self {
72+
fn reborrow(mutbl: Mutability) -> Self {
73+
match mutbl {
74+
Mutability::Not => Self::Reborrow,
75+
Mutability::Mut => Self::ReborrowMut,
76+
}
77+
}
78+
79+
fn auto_reborrow(mutbl: AutoBorrowMutability) -> Self {
8080
match mutbl {
8181
AutoBorrowMutability::Not => Self::Reborrow,
8282
AutoBorrowMutability::Mut { .. } => Self::ReborrowMut,
8383
}
8484
}
85+
86+
fn display(self) -> &'static str {
87+
match self {
88+
Self::None => "",
89+
Self::Borrow => "&",
90+
Self::BorrowMut => "&mut ",
91+
Self::Deref => "*",
92+
Self::Reborrow => "&*",
93+
Self::ReborrowMut => "&mut *",
94+
}
95+
}
8596
}
8697

8798
/// Checks if an `iter` or `iter_mut` call returns `IntoIterator::IntoIter`. Returns how the
8899
/// argument needs to be adjusted.
100+
#[expect(clippy::too_many_lines)]
89101
fn is_ref_iterable<'tcx>(
90102
cx: &LateContext<'tcx>,
91103
self_arg: &Expr<'_>,
@@ -108,27 +120,50 @@ fn is_ref_iterable<'tcx>(
108120
let self_is_copy = is_copy(cx, self_ty);
109121

110122
if adjustments.is_empty() && self_is_copy {
123+
// Exact type match, already checked earlier
111124
return Some((AdjustKind::None, self_ty));
112125
}
113126

114-
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty).subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
115-
if !adjustments.is_empty() && self_is_copy {
116-
if implements_trait(cx, self_ty, trait_id, &[])
117-
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
118-
&& ty == res_ty
119-
{
120-
return Some((AdjustKind::None, self_ty));
121-
}
122-
}
123-
127+
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
128+
.subst(cx.tcx, typeck.node_substs(call_expr.hir_id)));
124129
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
125130
Some(mutbl)
126131
} else {
127132
None
128133
};
134+
135+
if !adjustments.is_empty() {
136+
if self_is_copy {
137+
// Using by value won't consume anything
138+
if implements_trait(cx, self_ty, trait_id, &[])
139+
&& let Some(ty) =
140+
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
141+
&& ty == res_ty
142+
{
143+
return Some((AdjustKind::None, self_ty));
144+
}
145+
} else if let ty::Ref(region, ty, Mutability::Mut) = *self_ty.kind()
146+
&& let Some(mutbl) = mutbl
147+
{
148+
// Attempt to reborrow the mutable reference
149+
let self_ty = if mutbl.is_mut() {
150+
self_ty
151+
} else {
152+
cx.tcx.mk_ref(region, TypeAndMut { ty, mutbl })
153+
};
154+
if implements_trait(cx, self_ty, trait_id, &[])
155+
&& let Some(ty) =
156+
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
157+
&& ty == res_ty
158+
{
159+
return Some((AdjustKind::reborrow(mutbl), self_ty));
160+
}
161+
}
162+
}
129163
if let Some(mutbl) = mutbl
130164
&& !self_ty.is_ref()
131165
{
166+
// Attempt to borrow
132167
let self_ty = cx.tcx.mk_ref(cx.tcx.lifetimes.re_erased, TypeAndMut {
133168
ty: self_ty,
134169
mutbl,
@@ -157,7 +192,7 @@ fn is_ref_iterable<'tcx>(
157192
make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
158193
&& ty == res_ty
159194
{
160-
Some((AdjustKind::reborrow(mutbl), target))
195+
Some((AdjustKind::auto_reborrow(mutbl), target))
161196
} else {
162197
None
163198
}

clippy_lints/src/loops/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
687687
manual_while_let_some::check(cx, condition, body, span);
688688
}
689689
}
690+
691+
extract_msrv_attr!(LateContext);
690692
}
691693

692694
impl Loops {

clippy_lints/src/loops/same_item_push.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
148148
}
149149

150150
fn visit_block(&mut self, b: &'tcx Block<'_>) {
151-
for stmt in b.stmts.iter() {
151+
for stmt in b.stmts {
152152
self.visit_stmt(stmt);
153153
}
154154
}

clippy_lints/src/macro_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
101101
});
102102
if !id.is_local();
103103
then {
104-
for kid in cx.tcx.module_children(id).iter() {
104+
for kid in cx.tcx.module_children(id) {
105105
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
106106
let span = mac_attr.span;
107107
let def_path = cx.tcx.def_path_str(mac_id);

clippy_lints/src/matches/match_wild_err_arm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
2525
let mut ident_bind_name = kw::Underscore;
2626
if !matching_wild {
2727
// Looking for unused bindings (i.e.: `_e`)
28-
for pat in inner.iter() {
28+
for pat in inner {
2929
if let PatKind::Binding(_, id, ident, None) = pat.kind {
3030
if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
3131
ident_bind_name = ident.name;

clippy_lints/src/matches/significant_drop_in_scrutinee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
140140
}
141141
}
142142

143-
for generic_arg in b.iter() {
143+
for generic_arg in *b {
144144
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
145145
if self.has_sig_drop_attr(cx, ty) {
146146
return true;

0 commit comments

Comments
 (0)