Skip to content

Commit

Permalink
Implement PossibleBorrowerMap using the borrowck crate
Browse files Browse the repository at this point in the history
  • Loading branch information
smoelius committed Feb 5, 2023
1 parent 71926a2 commit e8880c1
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 354 deletions.
2 changes: 1 addition & 1 deletion clippy_lints/src/dereference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,7 @@ fn referent_used_exactly_once<'tcx>(
.last()
.map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
{
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, body_owner_local_def_id)));
}
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
// If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/redundant_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {

let mir = cx.tcx.optimized_mir(def_id.to_def_id());

let mut possible_borrower = PossibleBorrowerMap::new(cx, mir);
let mut possible_borrower = PossibleBorrowerMap::new(cx, def_id);

for (bb, bbdata) in mir.basic_blocks.iter_enumerated() {
let terminator = bbdata.terminator();
Expand Down
3 changes: 3 additions & 0 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
extern crate rustc_ast;
extern crate rustc_ast_pretty;
extern crate rustc_attr;
extern crate rustc_borrowck;
extern crate rustc_data_structures;
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
#[allow(unused_extern_crates)]
Expand All @@ -33,7 +34,9 @@ extern crate rustc_infer;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_mir_build;
extern crate rustc_mir_dataflow;
extern crate rustc_mir_transform;
extern crate rustc_parse_format;
extern crate rustc_session;
extern crate rustc_span;
Expand Down
200 changes: 193 additions & 7 deletions clippy_utils/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use rustc_hir::{Expr, HirId};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, TyContext, Visitor,
};
use rustc_middle::mir::{
traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
traversal, Body, InlineAsmOperand, Local, LocalDecl, Location, Place, Statement, StatementKind, Terminator,
TerminatorKind, START_BLOCK,
};
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{Region, Ty, TyCtxt};

mod possible_borrower;
pub use possible_borrower::PossibleBorrowerMap;

mod possible_origin;

mod transitive_relation;

#[derive(Clone, Debug, Default)]
pub struct LocalUsage {
/// The locations where the local is used, if any.
Expand Down Expand Up @@ -119,6 +118,193 @@ pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
})
}

/// Tries to find the local in `to_mir` corresponding to `local` in `from_mir`.
pub fn translate_local<'tcx>(
tcx: TyCtxt<'tcx>,
from_mir: &Body<'tcx>,
to_mir: &Body<'tcx>,
local: Local,
) -> Option<Local> {
let equiv_decl = |lhs: &LocalDecl<'tcx>, rhs: &LocalDecl<'tcx>| {
lhs.mutability == rhs.mutability
&& tcx.erase_regions_ty(lhs.ty) == tcx.erase_regions_ty(rhs.ty)
&& lhs.source_info.span == rhs.source_info.span
};

let from_decl = &from_mir.local_decls[local];

// Fast path
if to_mir
.local_decls
.get(local)
.map_or(false, |to_decl| equiv_decl(from_decl, to_decl))
{
return Some(local);
}

// Slow path
to_mir
.local_decls
.iter()
.position(|to_decl| equiv_decl(from_decl, to_decl))
.map(Into::into)
}

/// Tries to find the location in `to_mir` corresponding to `location` in `from_mir`.
pub fn translate_location<'tcx>(
tcx: TyCtxt<'tcx>,
from_mir: &Body<'tcx>,
to_mir: &Body<'tcx>,
location: Location,
) -> Option<Location> {
let normalized_lhs = from_mir
.stmt_at(location)
.map_left(|statement| normalize_statement(tcx, statement))
.map_right(|terminator| normalize_terminator(tcx, terminator));

for (block, block_data) in to_mir.basic_blocks.iter_enumerated() {
if let Some(location) = normalized_lhs.as_ref().either(
|normalized_lhs| {
(0..block_data.statements.len()).find_map(|statement_index| {
let rhs = &block_data.statements[statement_index];
if normalized_lhs.source_info.span == rhs.source_info.span
&& normalized_lhs.kind == normalize_statement(tcx, rhs).kind
{
Some(Location { block, statement_index })
} else {
None
}
})
},
|normalized_lhs| {
if block_data.terminator.as_ref().map_or(false, |rhs| {
normalized_lhs.source_info.span == rhs.source_info.span
&& normalized_lhs.kind == normalize_terminator(tcx, rhs).kind
}) {
Some(Location {
block,
statement_index: block_data.statements.len(),
})
} else {
None
}
},
) {
return Some(location);
}
}

None
}

fn normalize_statement<'tcx>(tcx: TyCtxt<'tcx>, statement: &Statement<'tcx>) -> Statement<'tcx> {
let mut statement = statement.clone();
Normalizer { tcx }.visit_statement(&mut statement, Location::START);
statement
}

fn normalize_terminator<'tcx>(tcx: TyCtxt<'tcx>, terminator: &Terminator<'tcx>) -> Terminator<'tcx> {
let mut terminator = terminator.clone();
Normalizer { tcx }.visit_terminator(&mut terminator, Location::START);
match terminator.kind {
// No basic blocks
TerminatorKind::Abort
| TerminatorKind::GeneratorDrop
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::Unreachable => {},

// One basic block
TerminatorKind::Goto { ref mut target } => {
*target = Location::START.block;
},

// Two basic blocks
TerminatorKind::FalseEdge {
ref mut real_target,
ref mut imaginary_target,
} => {
*real_target = Location::START.block;
*imaginary_target = Location::START.block;
},

// One optional and one non-optional basic block
TerminatorKind::Assert {
ref mut target,
cleanup: ref mut unwind,
..
}
| TerminatorKind::Drop {
ref mut target,
ref mut unwind,
..
}
| TerminatorKind::DropAndReplace {
ref mut target,
ref mut unwind,
..
}
| TerminatorKind::FalseUnwind {
real_target: ref mut target,
ref mut unwind,
..
}
| TerminatorKind::Yield {
resume: ref mut target,
drop: ref mut unwind,
..
} => {
*target = Location::START.block;
*unwind = None;
},

// Two optional basic blocks
TerminatorKind::Call {
ref mut target,
ref mut cleanup,
..
}
| TerminatorKind::InlineAsm {
destination: ref mut target,
ref mut cleanup,
..
} => {
*target = None;
*cleanup = None;
},

// Arbitrarily many basic blocks
TerminatorKind::SwitchInt { ref mut targets, .. } => {
for (_, ref mut target) in targets.iter() {
*target = Location::START.block;
}
},
}
terminator
}

struct Normalizer<'tcx> {
tcx: TyCtxt<'tcx>,
}

impl<'tcx> MutVisitor<'tcx> for Normalizer<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}

fn visit_local(&mut self, local: &mut Local, _context: PlaceContext, _location: Location) {
*local = Local::from_u32(0);
}

fn visit_region(&mut self, region: &mut Region<'tcx>, _: Location) {
*region = self.tcx.lifetimes.re_erased;
}

fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
*ty = self.tcx.erase_regions_ty(*ty);
}
}

/// Returns a vector of `mir::Location` where `local` is assigned.
pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
let mut locations = Vec::new();
Expand Down
Loading

0 comments on commit e8880c1

Please sign in to comment.