Skip to content

Commit 3f0c977

Browse files
committed
Auto merge of #7531 - Jarcho:manual_map_7413, r=camsteffen
Manual map 7413 fixes: #7413 This only fixes the specific problem from #7413, not the general case. The full fix requires interacting with the borrow checker to determine the lifetime of all the borrows made in the function. I'll open an issue about it later. changelog: Don't suggest using `map` when the option is borrowed in the match, and also consumed in the arm. changelog: Locals declared within the would-be closure will not prevent the closure from being suggested in `manual_map` and `map_entry`
2 parents d4e2fca + f0444d7 commit 3f0c977

12 files changed

+519
-60
lines changed

clippy_lints/src/entry.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ use clippy_utils::{
77
};
88
use rustc_errors::Applicability;
99
use rustc_hir::{
10+
hir_id::HirIdSet,
1011
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
11-
Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
12+
Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
1213
};
1314
use rustc_lint::{LateContext, LateLintPass};
1415
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -336,6 +337,8 @@ struct InsertSearcher<'cx, 'tcx> {
336337
edits: Vec<Edit<'tcx>>,
337338
/// A stack of loops the visitor is currently in.
338339
loops: Vec<HirId>,
340+
/// Local variables created in the expression. These don't need to be captured.
341+
locals: HirIdSet,
339342
}
340343
impl<'tcx> InsertSearcher<'_, 'tcx> {
341344
/// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
@@ -383,13 +386,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
383386
}
384387
},
385388
StmtKind::Expr(e) => self.visit_expr(e),
386-
StmtKind::Local(Local { init: Some(e), .. }) => {
387-
self.allow_insert_closure &= !self.in_tail_pos;
388-
self.in_tail_pos = false;
389-
self.is_single_insert = false;
390-
self.visit_expr(e);
389+
StmtKind::Local(l) => {
390+
self.visit_pat(l.pat);
391+
if let Some(e) = l.init {
392+
self.allow_insert_closure &= !self.in_tail_pos;
393+
self.in_tail_pos = false;
394+
self.is_single_insert = false;
395+
self.visit_expr(e);
396+
}
391397
},
392-
_ => {
398+
StmtKind::Item(_) => {
393399
self.allow_insert_closure &= !self.in_tail_pos;
394400
self.is_single_insert = false;
395401
},
@@ -471,6 +477,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
471477
// Each branch may contain it's own insert expression.
472478
let mut is_map_used = self.is_map_used;
473479
for arm in arms {
480+
self.visit_pat(arm.pat);
474481
if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
475482
self.visit_non_tail_expr(guard);
476483
}
@@ -496,7 +503,8 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
496503
},
497504
_ => {
498505
self.allow_insert_closure &= !self.in_tail_pos;
499-
self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
506+
self.allow_insert_closure &=
507+
can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals);
500508
// Sub expressions are no longer in the tail position.
501509
self.is_single_insert = false;
502510
self.in_tail_pos = false;
@@ -505,6 +513,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
505513
},
506514
}
507515
}
516+
517+
fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
518+
p.each_binding_or_first(&mut |_, id, _, _| {
519+
self.locals.insert(id);
520+
});
521+
}
508522
}
509523

510524
struct InsertSearchResults<'tcx> {
@@ -630,6 +644,7 @@ fn find_insert_calls(
630644
in_tail_pos: true,
631645
is_single_insert: true,
632646
loops: Vec::new(),
647+
locals: HirIdSet::default(),
633648
};
634649
s.visit_expr(expr);
635650
let allow_insert_closure = s.allow_insert_closure;

clippy_lints/src/manual_map.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
44
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
55
use clippy_utils::{
66
can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
7-
peel_hir_expr_refs,
7+
peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
88
};
99
use rustc_ast::util::parser::PREC_POSTFIX;
1010
use rustc_errors::Applicability;
1111
use rustc_hir::LangItem::{OptionNone, OptionSome};
12-
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
12+
use rustc_hir::{
13+
def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind, Path, QPath,
14+
};
1315
use rustc_lint::{LateContext, LateLintPass, LintContext};
1416
use rustc_middle::lint::in_external_macro;
1517
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -111,10 +113,6 @@ impl LateLintPass<'_> for ManualMap {
111113
return;
112114
}
113115

114-
if !can_move_expr_to_closure(cx, some_expr) {
115-
return;
116-
}
117-
118116
// Determine which binding mode to use.
119117
let explicit_ref = some_pat.contains_explicit_ref_binding();
120118
let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
@@ -125,6 +123,32 @@ impl LateLintPass<'_> for ManualMap {
125123
None => "",
126124
};
127125

126+
match can_move_expr_to_closure(cx, some_expr) {
127+
Some(captures) => {
128+
// Check if captures the closure will need conflict with borrows made in the scrutinee.
129+
// TODO: check all the references made in the scrutinee expression. This will require interacting
130+
// with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
131+
if let Some(binding_ref_mutability) = binding_ref {
132+
let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
133+
ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
134+
_ => None,
135+
});
136+
if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
137+
match captures.get(l) {
138+
Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
139+
Some(CaptureKind::Ref(Mutability::Not))
140+
if binding_ref_mutability == Mutability::Mut =>
141+
{
142+
return;
143+
}
144+
Some(CaptureKind::Ref(Mutability::Not)) | None => (),
145+
}
146+
}
147+
}
148+
},
149+
None => return,
150+
};
151+
128152
let mut app = Applicability::MachineApplicable;
129153

130154
// Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or

0 commit comments

Comments
 (0)