Skip to content

Commit 70573af

Browse files
committed
Auto merge of #12147 - y21:needless_pass_by_ref_mut_rm_visitor, r=llogiq
Find function path references early in the same lint pass This removes a visitor that existed to collect paths to functions in a context where the exact signature is required in order to cancel the lint. E.g. when there's a `let _: fn(&mut i32) = path_to_fn_ref_mut_i32;` statement somewhere in the crate, we shouldn't suggest removing the mutable reference in the function signature. It was doing a whole pass through the crate at the end, which seems unnecessary. It seems like we should be able to add entries to the map in the same lint pass. The map is untouched all the way until `check_crate_post` (at which point it will be populated by the visitor and finally checked), so it doesn't seem like this changes behavior: it will only be fully populated by the time we reach `check_crate_post` no matter what. I don't think this will have a significant perf impact but it did show up in a profile with 0.5% for a crate I was looking into and looked like a low hanging fruit. changelog: none
2 parents 989ce17 + 0774489 commit 70573af

File tree

1 file changed

+24
-53
lines changed

1 file changed

+24
-53
lines changed

clippy_lints/src/needless_pass_by_ref_mut.rs

+24-53
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ use clippy_utils::visitors::for_each_expr_with_closures;
55
use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
66
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
77
use rustc_errors::Applicability;
8-
use rustc_hir::intravisit::{walk_qpath, FnKind, Visitor};
8+
use rustc_hir::intravisit::FnKind;
99
use rustc_hir::{
1010
BlockCheckMode, Body, Closure, Expr, ExprKind, FnDecl, HirId, HirIdMap, HirIdSet, Impl, ItemKind, Mutability, Node,
11-
PatKind, QPath,
11+
PatKind,
1212
};
1313
use rustc_hir_typeck::expr_use_visitor as euv;
1414
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
1515
use rustc_lint::{LateContext, LateLintPass};
1616
use rustc_middle::hir::map::associated_body;
17-
use rustc_middle::hir::nested_filter::OnlyBodies;
1817
use rustc_middle::mir::FakeReadCause;
1918
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath};
2019
use rustc_session::impl_lint_pass;
@@ -234,12 +233,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
234233
}
235234
}
236235

237-
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
238-
cx.tcx.hir().visit_all_item_likes_in_crate(&mut FnNeedsMutVisitor {
239-
cx,
240-
used_fn_def_ids: &mut self.used_fn_def_ids,
241-
});
236+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
237+
// #11182; do not lint if mutability is required elsewhere
238+
if let ExprKind::Path(..) = expr.kind
239+
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
240+
&& let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(expr).kind()
241+
&& let Some(def_id) = def_id.as_local()
242+
{
243+
if let Node::Expr(e) = parent
244+
&& let ExprKind::Call(call, _) = e.kind
245+
&& call.hir_id == expr.hir_id
246+
{
247+
return;
248+
}
242249

250+
// We don't need to check each argument individually as you cannot coerce a function
251+
// taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's
252+
// passed as a `fn`-like argument (or is unified) and should ignore every "unused"
253+
// argument entirely
254+
self.used_fn_def_ids.insert(def_id);
255+
}
256+
}
257+
258+
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
243259
for (fn_def_id, unused) in self
244260
.fn_def_ids_to_maybe_unused_mut
245261
.iter()
@@ -501,48 +517,3 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
501517
}
502518
}
503519
}
504-
505-
/// A final pass to check for paths referencing this function that require the argument to be
506-
/// `&mut`, basically if the function is ever used as a `fn`-like argument.
507-
struct FnNeedsMutVisitor<'a, 'tcx> {
508-
cx: &'a LateContext<'tcx>,
509-
used_fn_def_ids: &'a mut FxHashSet<LocalDefId>,
510-
}
511-
512-
impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
513-
type NestedFilter = OnlyBodies;
514-
515-
fn nested_visit_map(&mut self) -> Self::Map {
516-
self.cx.tcx.hir()
517-
}
518-
519-
fn visit_qpath(&mut self, qpath: &'tcx QPath<'tcx>, hir_id: HirId, _: Span) {
520-
walk_qpath(self, qpath, hir_id);
521-
522-
let Self { cx, used_fn_def_ids } = self;
523-
524-
// #11182; do not lint if mutability is required elsewhere
525-
if let Node::Expr(expr) = cx.tcx.hir_node(hir_id)
526-
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
527-
&& let ty::FnDef(def_id, _) = cx
528-
.tcx
529-
.typeck(cx.tcx.hir().enclosing_body_owner(hir_id))
530-
.expr_ty(expr)
531-
.kind()
532-
&& let Some(def_id) = def_id.as_local()
533-
{
534-
if let Node::Expr(e) = parent
535-
&& let ExprKind::Call(call, _) = e.kind
536-
&& call.hir_id == expr.hir_id
537-
{
538-
return;
539-
}
540-
541-
// We don't need to check each argument individually as you cannot coerce a function
542-
// taking `&mut` -> `&`, for some reason, so if we've gotten this far we know it's
543-
// passed as a `fn`-like argument (or is unified) and should ignore every "unused"
544-
// argument entirely
545-
used_fn_def_ids.insert(def_id);
546-
}
547-
}
548-
}

0 commit comments

Comments
 (0)