Fix cycle detection in `needless_borrow`

This commit is contained in:
Jason Newcomb 2023-09-15 07:06:50 -04:00
parent 6734e96ba4
commit fed036a57c
2 changed files with 34 additions and 9 deletions

View File

@ -12,7 +12,6 @@ use hir::def::DefKind;
use hir::MatchSource;
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
@ -1172,8 +1171,6 @@ fn referent_used_exactly_once<'tcx>(
&& let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
&& let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
&& !place.is_indirect_first_projection()
// Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
&& TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
{
let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
if possible_borrowers

View File

@ -1,7 +1,8 @@
use rustc_hir::{Expr, HirId};
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
};
use rustc_middle::ty::TyCtxt;
@ -79,8 +80,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
}
}
/// Checks if the block is part of a cycle
pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
let mut seen = BitSet::new_empty(body.basic_blocks.len());
let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
seen.insert(block);
let mut next = block;
loop {
for succ in body.basic_blocks[next].terminator().successors() {
if seen.insert(succ) {
to_visit.push(succ);
} else if succ == block {
return true;
}
}
if let Some(x) = to_visit.pop() {
next = x;
} else {
return false;
}
}
}
/// Convenience wrapper around `visit_local_usage`.
pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
visit_local_usage(
&[local],
mir,
@ -91,11 +116,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
)
.map(|mut vec| {
let LocalUsage { local_use_locs, .. } = vec.remove(0);
local_use_locs
let mut locations = local_use_locs
.into_iter()
.filter(|location| !is_local_assignment(mir, local, *location))
.count()
== 1
.filter(|&location| !is_local_assignment(mir, local, location));
if let Some(location) = locations.next() {
locations.next().is_none() && !block_in_cycle(mir, location.block)
} else {
false
}
})
}