use crate::transform::MirPass;
use crate::util::pretty;
use crate::util::spanview::{self, SpanViewable};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_middle::hir;
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::ich::StableHashingContext;
use rustc_middle::mir;
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{
AggregateKind, BasicBlock, BasicBlockData, Coverage, CoverageInfo, FakeReadCause, Location,
Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::DefId;
use rustc_span::source_map::original_sp;
use rustc_span::{BytePos, CharPos, Pos, SourceFile, Span, Symbol, SyntaxContext};
use std::cmp::Ordering;
const ID_SEPARATOR: &str = ",";
pub struct InstrumentCoverage;
pub(crate) fn provide(providers: &mut Providers) {
providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id);
}
struct CoverageVisitor {
info: CoverageInfo,
}
impl Visitor<'_> for CoverageVisitor {
fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) {
match coverage.kind {
CoverageKind::Counter { id, .. } => {
let counter_id = u32::from(id);
self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
}
CoverageKind::Expression { id, .. } => {
let expression_index = u32::MAX - u32::from(id);
self.info.num_expressions =
std::cmp::max(self.info.num_expressions, expression_index + 1);
}
_ => {}
}
}
}
fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
let mir_body = tcx.optimized_mir(def_id);
let mut coverage_visitor =
CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } };
coverage_visitor.visit_body(mir_body);
coverage_visitor.info
}
impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
if mir_body.source.promoted.is_some() {
trace!(
"InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)",
mir_body.source.def_id()
);
return;
}
let hir_id = tcx.hir().local_def_id_to_hir_id(mir_body.source.def_id().expect_local());
let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
if !is_fn_like {
trace!(
"InstrumentCoverage skipped for {:?} (not an FnLikeNode)",
mir_body.source.def_id(),
);
return;
}
trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id());
Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
trace!("InstrumentCoverage starting for {:?}", mir_body.source.def_id());
}
}
#[derive(Debug, Clone)]
struct BasicCoverageBlock {
pub blocks: Vec<BasicBlock>,
}
impl BasicCoverageBlock {
pub fn leader_bb(&self) -> BasicBlock {
self.blocks[0]
}
pub fn id(&self) -> String {
format!(
"@{}",
self.blocks
.iter()
.map(|bb| bb.index().to_string())
.collect::<Vec<_>>()
.join(ID_SEPARATOR)
)
}
}
struct BasicCoverageBlocks {
vec: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
}
impl BasicCoverageBlocks {
pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self {
let mut basic_coverage_blocks =
BasicCoverageBlocks { vec: IndexVec::from_elem_n(None, mir_body.basic_blocks().len()) };
basic_coverage_blocks.extract_from_mir(mir_body);
basic_coverage_blocks
}
pub fn iter(&self) -> impl Iterator<Item = &BasicCoverageBlock> {
self.vec.iter().filter_map(|option| option.as_ref())
}
fn extract_from_mir(&mut self, mir_body: &mir::Body<'tcx>) {
let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| {
let mut successors = term_kind.successors();
match &term_kind {
TerminatorKind::SwitchInt { .. } => successors,
_ => successors.next().into_iter().chain(&[]),
}
});
let mut blocks = Vec::new();
for (bb, data) in cfg_without_unwind {
if let Some(last) = blocks.last() {
let predecessors = &mir_body.predecessors()[bb];
if predecessors.len() > 1 || !predecessors.contains(last) {
self.add_basic_coverage_block(blocks.split_off(0));
debug!(
" because {}",
if predecessors.len() > 1 {
"predecessors.len() > 1".to_owned()
} else {
format!("bb {} is not in precessors: {:?}", bb.index(), predecessors)
}
);
}
}
blocks.push(bb);
let term = data.terminator();
match term.kind {
TerminatorKind::Return { .. }
| TerminatorKind::Abort
| TerminatorKind::Assert { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } => {
self.add_basic_coverage_block(blocks.split_off(0));
debug!(" because term.kind = {:?}", term.kind);
}
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => {}
}
}
if !blocks.is_empty() {
self.add_basic_coverage_block(blocks.split_off(0));
debug!(" because the end of the CFG was reached while traversing");
}
}
fn add_basic_coverage_block(&mut self, blocks: Vec<BasicBlock>) {
let leader_bb = blocks[0];
let bcb = BasicCoverageBlock { blocks };
debug!("adding BCB: {:?}", bcb);
self.vec[leader_bb] = Some(bcb);
}
}
impl std::ops::Index<BasicBlock> for BasicCoverageBlocks {
type Output = BasicCoverageBlock;
fn index(&self, index: BasicBlock) -> &Self::Output {
self.vec[index].as_ref().expect("is_some if BasicBlock is a BasicCoverageBlock leader")
}
}
#[derive(Debug, Copy, Clone)]
enum CoverageStatement {
Statement(BasicBlock, Span, usize),
Terminator(BasicBlock, Span),
}
impl CoverageStatement {
pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
match *self {
Self::Statement(bb, span, stmt_index) => {
let stmt = &mir_body.basic_blocks()[bb].statements[stmt_index];
format!(
"{}: @{}[{}]: {:?}",
spanview::source_range_no_file(tcx, &span),
bb.index(),
stmt_index,
stmt
)
}
Self::Terminator(bb, span) => {
let term = mir_body.basic_blocks()[bb].terminator();
format!(
"{}: @{}.{}: {:?}",
spanview::source_range_no_file(tcx, &span),
bb.index(),
term_type(&term.kind),
term.kind
)
}
}
}
pub fn span(&self) -> &Span {
match self {
Self::Statement(_, span, _) | Self::Terminator(_, span) => span,
}
}
}
fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
match kind {
TerminatorKind::Goto { .. } => "Goto",
TerminatorKind::SwitchInt { .. } => "SwitchInt",
TerminatorKind::Resume => "Resume",
TerminatorKind::Abort => "Abort",
TerminatorKind::Return => "Return",
TerminatorKind::Unreachable => "Unreachable",
TerminatorKind::Drop { .. } => "Drop",
TerminatorKind::DropAndReplace { .. } => "DropAndReplace",
TerminatorKind::Call { .. } => "Call",
TerminatorKind::Assert { .. } => "Assert",
TerminatorKind::Yield { .. } => "Yield",
TerminatorKind::GeneratorDrop => "GeneratorDrop",
TerminatorKind::FalseEdge { .. } => "FalseEdge",
TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
TerminatorKind::InlineAsm { .. } => "InlineAsm",
}
}
#[derive(Debug, Clone)]
struct CoverageSpan {
span: Span,
bcb_leader_bb: BasicBlock,
coverage_statements: Vec<CoverageStatement>,
is_closure: bool,
}
impl CoverageSpan {
pub fn for_statement(
statement: &Statement<'tcx>,
span: Span,
bcb: &BasicCoverageBlock,
bb: BasicBlock,
stmt_index: usize,
) -> Self {
let is_closure = match statement.kind {
StatementKind::Assign(box (
_,
Rvalue::Aggregate(box AggregateKind::Closure(_, _), _),
)) => true,
_ => false,
};
Self {
span,
bcb_leader_bb: bcb.leader_bb(),
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
is_closure,
}
}
pub fn for_terminator(span: Span, bcb: &'a BasicCoverageBlock, bb: BasicBlock) -> Self {
Self {
span,
bcb_leader_bb: bcb.leader_bb(),
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
is_closure: false,
}
}
pub fn merge_from(&mut self, mut other: CoverageSpan) {
debug_assert!(self.is_mergeable(&other));
self.span = self.span.to(other.span);
if other.is_closure {
self.is_closure = true;
}
self.coverage_statements.append(&mut other.coverage_statements);
}
pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos);
if let Some(highest_covstmt) =
self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi())
{
self.span = self.span.with_hi(highest_covstmt.span().hi());
}
}
pub fn is_dominated_by(
&self,
other: &CoverageSpan,
dominators: &Dominators<BasicBlock>,
) -> bool {
debug_assert!(!self.is_in_same_bcb(other));
dominators.is_dominated_by(self.bcb_leader_bb, other.bcb_leader_bb)
}
pub fn is_mergeable(&self, other: &Self) -> bool {
self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure)
}
pub fn is_in_same_bcb(&self, other: &Self) -> bool {
self.bcb_leader_bb == other.bcb_leader_bb
}
}
struct Instrumentor<'a, 'tcx> {
pass_name: &'a str,
tcx: TyCtxt<'tcx>,
mir_body: &'a mut mir::Body<'tcx>,
hir_body: &'tcx rustc_hir::Body<'tcx>,
dominators: Option<Dominators<BasicBlock>>,
basic_coverage_blocks: Option<BasicCoverageBlocks>,
function_source_hash: Option<u64>,
next_counter_id: u32,
num_expressions: u32,
}
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
let hir_body = hir_body(tcx, mir_body.source.def_id());
Self {
pass_name,
tcx,
mir_body,
hir_body,
dominators: None,
basic_coverage_blocks: None,
function_source_hash: None,
next_counter_id: CounterValueReference::START.as_u32(),
num_expressions: 0,
}
}
fn next_counter(&mut self) -> CounterValueReference {
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
let next = self.next_counter_id;
self.next_counter_id += 1;
CounterValueReference::from(next)
}
fn next_expression(&mut self) -> InjectedExpressionIndex {
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
let next = u32::MAX - self.num_expressions;
self.num_expressions += 1;
InjectedExpressionIndex::from(next)
}
fn dominators(&self) -> &Dominators<BasicBlock> {
self.dominators.as_ref().expect("dominators must be initialized before calling")
}
fn basic_coverage_blocks(&self) -> &BasicCoverageBlocks {
self.basic_coverage_blocks
.as_ref()
.expect("basic_coverage_blocks must be initialized before calling")
}
fn function_source_hash(&mut self) -> u64 {
match self.function_source_hash {
Some(hash) => hash,
None => {
let hash = hash_mir_source(self.tcx, self.hir_body);
self.function_source_hash.replace(hash);
hash
}
}
}
fn inject_counters(&mut self) {
let tcx = self.tcx;
let source_map = tcx.sess.source_map();
let def_id = self.mir_body.source.def_id();
let mir_body = &self.mir_body;
let body_span = self.body_span();
let source_file = source_map.lookup_source_file(body_span.lo());
let file_name = Symbol::intern(&source_file.name.to_string());
debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span));
self.dominators.replace(mir_body.dominators());
self.basic_coverage_blocks.replace(BasicCoverageBlocks::from_mir(mir_body));
let coverage_spans = self.coverage_spans();
let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) {
Some(self.span_viewables(&coverage_spans))
} else {
None
};
let mut bb_counters = IndexVec::from_elem_n(None, mir_body.basic_blocks().len());
for CoverageSpan { span, bcb_leader_bb: bb, .. } in coverage_spans {
if let Some(&counter_operand) = bb_counters[bb].as_ref() {
let expression =
self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO);
debug!(
"Injecting counter expression {:?} at: {:?}:\n{}\n==========",
expression,
span,
source_map.span_to_snippet(span).expect("Error getting source for span"),
);
self.inject_statement(file_name, &source_file, expression, span, bb);
} else {
let counter = self.make_counter();
debug!(
"Injecting counter {:?} at: {:?}:\n{}\n==========",
counter,
span,
source_map.span_to_snippet(span).expect("Error getting source for span"),
);
let counter_operand = counter.as_operand_id();
bb_counters[bb] = Some(counter_operand);
self.inject_statement(file_name, &source_file, counter, span, bb);
}
}
if let Some(span_viewables) = span_viewables {
let mut file = pretty::create_dump_file(
tcx,
"html",
None,
self.pass_name,
&0,
self.mir_body.source,
)
.expect("Unexpected error creating MIR spanview HTML file");
let crate_name = tcx.crate_name(def_id.krate);
let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate();
let title = format!("{}.{} - Coverage Spans", crate_name, item_name);
spanview::write_document(tcx, def_id, span_viewables, &title, &mut file)
.expect("Unexpected IO error dumping coverage spans as HTML");
}
}
fn make_counter(&mut self) -> CoverageKind {
CoverageKind::Counter {
function_source_hash: self.function_source_hash(),
id: self.next_counter(),
}
}
fn make_expression(
&mut self,
lhs: ExpressionOperandId,
op: Op,
rhs: ExpressionOperandId,
) -> CoverageKind {
CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs }
}
fn inject_statement(
&mut self,
file_name: Symbol,
source_file: &Lrc<SourceFile>,
coverage_kind: CoverageKind,
span: Span,
block: BasicBlock,
) {
let code_region = make_code_region(file_name, source_file, span);
debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region);
let data = &mut self.mir_body[block];
let source_info = data.terminator().source_info;
let statement = Statement {
source_info,
kind: StatementKind::Coverage(box Coverage { kind: coverage_kind, code_region }),
};
data.statements.push(statement);
}
fn span_viewables(&self, coverage_spans: &Vec<CoverageSpan>) -> Vec<SpanViewable> {
let tcx = self.tcx;
let mir_body = &self.mir_body;
let mut span_viewables = Vec::new();
for coverage_span in coverage_spans {
let bcb = self.bcb_from_coverage_span(coverage_span);
let CoverageSpan { span, bcb_leader_bb: bb, coverage_statements, .. } = coverage_span;
let id = bcb.id();
let mut sorted_coverage_statements = coverage_statements.clone();
sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {
CoverageStatement::Statement(bb, _, index) => (bb, index),
CoverageStatement::Terminator(bb, _) => (bb, usize::MAX),
});
let tooltip = sorted_coverage_statements
.iter()
.map(|covstmt| covstmt.format(tcx, mir_body))
.collect::<Vec<_>>()
.join("\n");
span_viewables.push(SpanViewable { bb: *bb, span: *span, id, tooltip });
}
span_viewables
}
#[inline(always)]
fn bcb_from_coverage_span(&self, coverage_span: &CoverageSpan) -> &BasicCoverageBlock {
&self.basic_coverage_blocks()[coverage_span.bcb_leader_bb]
}
#[inline(always)]
fn body_span(&self) -> Span {
self.hir_body.value.span
}
fn extract_spans(&self, bcb: &'a BasicCoverageBlock) -> Vec<CoverageSpan> {
let body_span = self.body_span();
let mir_basic_blocks = self.mir_body.basic_blocks();
bcb.blocks
.iter()
.map(|bbref| {
let bb = *bbref;
let data = &mir_basic_blocks[bb];
data.statements
.iter()
.enumerate()
.filter_map(move |(index, statement)| {
filtered_statement_span(statement, body_span).map(|span| {
CoverageSpan::for_statement(statement, span, bcb, bb, index)
})
})
.chain(
filtered_terminator_span(data.terminator(), body_span)
.map(|span| CoverageSpan::for_terminator(span, bcb, bb)),
)
})
.flatten()
.collect()
}
fn coverage_spans(&self) -> Vec<CoverageSpan> {
let mut initial_spans =
Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks().len() * 2);
for bcb in self.basic_coverage_blocks().iter() {
for coverage_span in self.extract_spans(bcb) {
initial_spans.push(coverage_span);
}
}
if initial_spans.is_empty() {
return initial_spans;
}
initial_spans.sort_unstable_by(|a, b| {
if a.span.lo() == b.span.lo() {
if a.span.hi() == b.span.hi() {
if a.is_in_same_bcb(b) {
Some(Ordering::Equal)
} else {
self.dominators().rank_partial_cmp(b.bcb_leader_bb, a.bcb_leader_bb)
}
} else {
b.span.hi().partial_cmp(&a.span.hi())
}
} else {
a.span.lo().partial_cmp(&b.span.lo())
}
.unwrap()
});
let refinery = CoverageSpanRefinery::from_sorted_spans(initial_spans, self.dominators());
refinery.to_refined_spans()
}
}
struct CoverageSpanRefinery<'a> {
sorted_spans_iter: std::vec::IntoIter<CoverageSpan>,
dominators: &'a Dominators<BasicBlock>,
some_curr: Option<CoverageSpan>,
curr_original_span: Span,
some_prev: Option<CoverageSpan>,
prev_original_span: Span,
pending_dups: Vec<CoverageSpan>,
refined_spans: Vec<CoverageSpan>,
}
impl<'a> CoverageSpanRefinery<'a> {
fn from_sorted_spans(
sorted_spans: Vec<CoverageSpan>,
dominators: &'a Dominators<BasicBlock>,
) -> Self {
let refined_spans = Vec::with_capacity(sorted_spans.len());
let mut sorted_spans_iter = sorted_spans.into_iter();
let prev = sorted_spans_iter.next().expect("at least one span");
let prev_original_span = prev.span;
Self {
sorted_spans_iter,
dominators,
refined_spans,
some_curr: None,
curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
some_prev: Some(prev),
prev_original_span,
pending_dups: Vec::new(),
}
}
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
while self.next_coverage_span() {
if self.curr().is_mergeable(self.prev()) {
debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev());
let prev = self.take_prev();
self.curr_mut().merge_from(prev);
} else if self.prev_ends_before_curr() {
debug!(
" different bcbs and disjoint spans, so keep curr for next iter, and add \
prev={:?}",
self.prev()
);
let prev = self.take_prev();
self.add_refined_span(prev);
} else if self.prev().is_closure {
debug!(
" curr overlaps a closure (prev). Drop curr and keep prev for next iter. \
prev={:?}",
self.prev()
);
self.discard_curr();
} else if self.curr().is_closure {
self.carve_out_span_for_closure();
} else if self.prev_original_span == self.curr().span {
self.hold_pending_dups_unless_dominated();
} else {
self.cutoff_prev_at_overlapping_curr();
}
}
debug!(" AT END, adding last prev={:?}", self.prev());
let pending_dups = self.pending_dups.split_off(0);
for dup in pending_dups.into_iter() {
debug!(" ...adding at least one pending dup={:?}", dup);
self.add_refined_span(dup);
}
let prev = self.take_prev();
self.add_refined_span(prev);
self.to_refined_spans_without_closures()
}
fn add_refined_span(&mut self, coverage_span: CoverageSpan) {
self.refined_spans.push(coverage_span);
}
fn to_refined_spans_without_closures(mut self) -> Vec<CoverageSpan> {
self.refined_spans.retain(|covspan| !covspan.is_closure);
self.refined_spans
}
fn curr(&self) -> &CoverageSpan {
self.some_curr
.as_ref()
.unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr"))
}
fn curr_mut(&mut self) -> &mut CoverageSpan {
self.some_curr
.as_mut()
.unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr"))
}
fn prev(&self) -> &CoverageSpan {
self.some_prev
.as_ref()
.unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev"))
}
fn prev_mut(&mut self) -> &mut CoverageSpan {
self.some_prev
.as_mut()
.unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev"))
}
fn take_prev(&mut self) -> CoverageSpan {
self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev"))
}
fn check_pending_dups(&mut self) {
if let Some(dup) = self.pending_dups.last() {
if dup.span != self.prev().span {
debug!(
" SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \
previous iteration, or prev started a new disjoint span"
);
if dup.span.hi() <= self.curr().span.lo() {
let pending_dups = self.pending_dups.split_off(0);
for dup in pending_dups.into_iter() {
debug!(" ...adding at least one pending={:?}", dup);
self.add_refined_span(dup);
}
} else {
self.pending_dups.clear();
}
}
}
}
fn next_coverage_span(&mut self) -> bool {
if let Some(curr) = self.some_curr.take() {
self.some_prev = Some(curr);
self.prev_original_span = self.curr_original_span;
}
while let Some(curr) = self.sorted_spans_iter.next() {
debug!("FOR curr={:?}", curr);
if self.prev_starts_after_next(&curr) {
debug!(
" prev.span starts after curr.span, so curr will be dropped (skipping past \
closure?); prev={:?}",
self.prev()
);
} else {
self.curr_original_span = curr.span;
self.some_curr.replace(curr);
self.check_pending_dups();
return true;
}
}
false
}
fn discard_curr(&mut self) {
self.some_curr = None;
}
fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool {
self.prev().span.lo() > next_curr.span.lo()
}
fn prev_ends_before_curr(&self) -> bool {
self.prev().span.hi() <= self.curr().span.lo()
}
fn carve_out_span_for_closure(&mut self) {
let curr_span = self.curr().span;
let left_cutoff = curr_span.lo();
let right_cutoff = curr_span.hi();
let has_pre_closure_span = self.prev().span.lo() < right_cutoff;
let has_post_closure_span = self.prev().span.hi() > right_cutoff;
let mut pending_dups = self.pending_dups.split_off(0);
if has_pre_closure_span {
let mut pre_closure = self.prev().clone();
pre_closure.span = pre_closure.span.with_hi(left_cutoff);
debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
if !pending_dups.is_empty() {
for mut dup in pending_dups.iter().cloned() {
dup.span = dup.span.with_hi(left_cutoff);
debug!(" ...and at least one pre_closure dup={:?}", dup);
self.add_refined_span(dup);
}
}
self.add_refined_span(pre_closure);
}
if has_post_closure_span {
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
self.prev_original_span = self.prev().span;
for dup in pending_dups.iter_mut() {
dup.span = dup.span.with_lo(right_cutoff);
}
self.pending_dups.append(&mut pending_dups);
self.discard_curr();
} else {
pending_dups.clear();
}
}
fn hold_pending_dups_unless_dominated(&mut self) {
debug_assert!(!self.prev().is_dominated_by(self.curr(), self.dominators));
if self.curr().is_dominated_by(&self.prev(), self.dominators) {
debug!(
" different bcbs but SAME spans, and prev dominates curr. Drop curr and \
keep prev for next iter. prev={:?}",
self.prev()
);
self.discard_curr();
} else {
debug!(
" different bcbs but SAME spans, and neither dominates, so keep curr for \
next iter, and, pending upcoming spans (unless overlapping) add prev={:?}",
self.prev()
);
let prev = self.take_prev();
self.pending_dups.push(prev);
}
}
fn cutoff_prev_at_overlapping_curr(&mut self) {
debug!(
" different bcbs, overlapping spans, so ignore/drop pending and only add prev \
if it has statements that end before curr={:?}",
self.prev()
);
if self.pending_dups.is_empty() {
let curr_span = self.curr().span;
self.prev_mut().cutoff_statements_at(curr_span.lo());
if self.prev().coverage_statements.is_empty() {
debug!(" ... no non-overlapping statements to add");
} else {
debug!(" ... adding modified prev={:?}", self.prev());
let prev = self.take_prev();
self.add_refined_span(prev);
}
} else {
self.pending_dups.clear();
}
}
}
fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option<Span> {
match statement.kind {
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Coverage(_)
| StatementKind::Nop => None,
StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
StatementKind::FakeRead(_, _)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }
| StatementKind::LlvmInlineAsm(_)
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _) => {
Some(source_info_span(&statement.source_info, body_span))
}
}
}
fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option<Span> {
match terminator.kind {
TerminatorKind::Unreachable
| TerminatorKind::Assert { .. }
| TerminatorKind::Drop { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Goto { .. }
| TerminatorKind::FalseEdge { .. } => None,
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Call { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => {
Some(source_info_span(&terminator.source_info, body_span))
}
}
}
#[inline(always)]
fn source_info_span(source_info: &SourceInfo, body_span: Span) -> Span {
let span = original_sp(source_info.span, body_span).with_ctxt(SyntaxContext::root());
if body_span.contains(span) { span } else { body_span }
}
fn make_code_region(file_name: Symbol, source_file: &Lrc<SourceFile>, span: Span) -> CodeRegion {
let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo());
let (end_line, end_col) = if span.hi() == span.lo() {
let (end_line, mut end_col) = (start_line, start_col);
let CharPos(char_pos) = start_col;
if char_pos > 0 {
start_col = CharPos(char_pos - 1);
} else {
end_col = CharPos(char_pos + 1);
}
(end_line, end_col)
} else {
source_file.lookup_file_pos(span.hi())
};
CodeRegion {
file_name,
start_line: start_line as u32,
start_col: start_col.to_u32() + 1,
end_line: end_line as u32,
end_col: end_col.to_u32() + 1,
}
}
fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
tcx.hir().body(fn_body_id)
}
fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
let mut hcx = tcx.create_no_span_stable_hashing_context();
hash(&mut hcx, &hir_body.value).to_smaller_hash()
}
fn hash(
hcx: &mut StableHashingContext<'tcx>,
node: &impl HashStable<StableHashingContext<'tcx>>,
) -> Fingerprint {
let mut stable_hasher = StableHasher::new();
node.hash_stable(hcx, &mut stable_hasher);
stable_hasher.finish()
}
pub struct ShortCircuitPreorder<
'a,
'tcx,
F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>,
> {
body: &'a mir::Body<'tcx>,
visited: BitSet<BasicBlock>,
worklist: Vec<BasicBlock>,
filtered_successors: F,
}
impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>>
ShortCircuitPreorder<'a, 'tcx, F>
{
pub fn new(
body: &'a mir::Body<'tcx>,
filtered_successors: F,
) -> ShortCircuitPreorder<'a, 'tcx, F> {
let worklist = vec![mir::START_BLOCK];
ShortCircuitPreorder {
body,
visited: BitSet::new_empty(body.basic_blocks().len()),
worklist,
filtered_successors,
}
}
}
impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator
for ShortCircuitPreorder<'a, 'tcx, F>
{
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
while let Some(idx) = self.worklist.pop() {
if !self.visited.insert(idx) {
continue;
}
let data = &self.body[idx];
if let Some(ref term) = data.terminator {
self.worklist.extend((self.filtered_successors)(&term.kind));
}
return Some((idx, data));
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.body.basic_blocks().len() - self.visited.count();
(size, Some(size))
}
}