diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index c1163a871cf1f..41827a91ba4be 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -129,7 +129,7 @@ impl CoverageMapGenerator { let (filenames_index, _) = self.filenames.insert_full(c_filename); virtual_file_mapping.push(filenames_index as u32); } - debug!("Adding counter {:?} to map for {:?}", counter, region,); + debug!("Adding counter {:?} to map for {:?}", counter, region); mapping_regions.push(CounterMappingRegion::code_region( counter, current_file_id, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 7fdbe1a55128a..e21e03822ebb3 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::FxHashMap; use rustc_llvm::RustString; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op, }; use rustc_middle::ty::Instance; @@ -27,8 +27,8 @@ const COVMAP_VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'tcx> { - // Coverage region data for each instrumented function identified by DefId. - pub(crate) function_coverage_map: RefCell, FunctionCoverage>>, + // Coverage data for each instrumented function identified by DefId. + pub(crate) function_coverage_map: RefCell, FunctionCoverage<'tcx>>>, } impl<'tcx> CrateCoverageContext<'tcx> { @@ -36,7 +36,7 @@ impl<'tcx> CrateCoverageContext<'tcx> { Self { function_coverage_map: Default::default() } } - pub fn take_function_coverage_map(&self) -> FxHashMap, FunctionCoverage> { + pub fn take_function_coverage_map(&self) -> FxHashMap, FunctionCoverage<'tcx>> { self.function_coverage_map.replace(FxHashMap::default()) } } @@ -58,47 +58,66 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } } - fn add_counter_region( + fn set_function_source_hash( &mut self, instance: Instance<'tcx>, function_source_hash: u64, + ) -> bool { + if let Some(coverage_context) = self.coverage_context() { + debug!( + "ensuring function source hash is set for instance={:?}; function_source_hash={}", + instance, function_source_hash, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .set_function_source_hash(function_source_hash); + true + } else { + false + } + } + + fn add_coverage_counter( + &mut self, + instance: Instance<'tcx>, id: CounterValueReference, region: CodeRegion, ) -> bool { if let Some(coverage_context) = self.coverage_context() { debug!( - "adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \ - at {:?}", - instance, function_source_hash, id, region, + "adding counter to coverage_map: instance={:?}, id={:?}, region={:?}", + instance, id, region, ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter(function_source_hash, id, region); + .add_counter(id, region); true } else { false } } - fn add_counter_expression_region( + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) -> bool { if let Some(coverage_context) = self.coverage_context() { debug!( - "adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \ - at {:?}", + "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \ + region: {:?}", instance, id, lhs, op, rhs, region, ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) .add_counter_expression(id, lhs, op, rhs, region); @@ -108,14 +127,14 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } } - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool { + fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool { if let Some(coverage_context) = self.coverage_context() { debug!( - "adding unreachable code to coverage_regions: instance={:?}, at {:?}", + "adding unreachable code to coverage_map: instance={:?}, at {:?}", instance, region, ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map .entry(instance) .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) .add_unreachable_region(region); diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index a266d179a421b..bcac2c90fdc20 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -3,7 +3,7 @@ use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; /// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L91) #[derive(Copy, Clone, Debug)] #[repr(C)] -enum CounterKind { +pub enum CounterKind { Zero = 0, CounterValueReference = 1, Expression = 2, @@ -23,8 +23,8 @@ enum CounterKind { #[repr(C)] pub struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. - kind: CounterKind, - id: u32, + pub kind: CounterKind, + pub id: u32, } impl Counter { @@ -55,9 +55,9 @@ pub enum ExprKind { #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct CounterExpression { - kind: ExprKind, - lhs: Counter, - rhs: Counter, + pub kind: ExprKind, + pub lhs: Counter, + pub rhs: Counter, } impl CounterExpression { diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index d8bde8ee70533..b0d7953f511c7 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -2,18 +2,18 @@ pub use super::ffi::*; use rustc_index::vec::IndexVec; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, - MappedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, + InjectedExpressionIndex, MappedExpressionIndex, Op, }; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; #[derive(Clone, Debug)] -pub struct ExpressionRegion { +pub struct Expression { lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, } /// Collects all of the coverage regions associated with (a) injected counters, (b) counter @@ -28,17 +28,23 @@ pub struct ExpressionRegion { /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count /// for a gap area is only used as the line execution count if there are no other regions on a /// line." -pub struct FunctionCoverage { +pub struct FunctionCoverage<'tcx> { + instance: Instance<'tcx>, source_hash: u64, counters: IndexVec>, - expressions: IndexVec>, + expressions: IndexVec>, unreachable_regions: Vec, } -impl FunctionCoverage { - pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { +impl<'tcx> FunctionCoverage<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); + debug!( + "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", + instance, coverageinfo + ); Self { + instance, source_hash: 0, // will be set with the first `add_counter()` counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), @@ -46,15 +52,18 @@ impl FunctionCoverage { } } - /// Adds a code region to be counted by an injected counter intrinsic. - /// The source_hash (computed during coverage instrumentation) should also be provided, and - /// should be the same for all counters in a given function. - pub fn add_counter(&mut self, source_hash: u64, id: CounterValueReference, region: CodeRegion) { + /// Sets the function source hash value. If called multiple times for the same function, all + /// calls should have the same hash value. + pub fn set_function_source_hash(&mut self, source_hash: u64) { if self.source_hash == 0 { self.source_hash = source_hash; } else { debug_assert_eq!(source_hash, self.source_hash); } + } + + /// Adds a code region to be counted by an injected counter intrinsic. + pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`"); } @@ -74,15 +83,19 @@ impl FunctionCoverage { /// counters and expressions have been added. pub fn add_counter_expression( &mut self, - expression_id: InjectedExpressionIndex, + expression_id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) { + debug!( + "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", + expression_id, lhs, op, rhs, region + ); let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] - .replace(ExpressionRegion { lhs, op, rhs, region }) + .replace(Expression { lhs, op, rhs, region }) .expect_none("add_counter_expression called with duplicate `id_descending_from_max`"); } @@ -103,7 +116,11 @@ impl FunctionCoverage { pub fn get_expressions_and_counter_regions<'a>( &'a self, ) -> (Vec, impl Iterator) { - assert!(self.source_hash != 0); + assert!( + self.source_hash != 0, + "No counters provided the source_hash for function: {:?}", + self.instance + ); let counter_regions = self.counter_regions(); let (counter_expressions, expression_regions) = self.expressions_with_regions(); @@ -129,54 +146,78 @@ impl FunctionCoverage { ) -> (Vec, impl Iterator) { let mut counter_expressions = Vec::with_capacity(self.expressions.len()); let mut expression_regions = Vec::with_capacity(self.expressions.len()); - let mut new_indexes = - IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len()); - // Note, the initial value shouldn't matter since every index in use in `self.expressions` - // will be set, and after that, `new_indexes` will only be accessed using those same - // indexes. + let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); - // Note that an `ExpressionRegion`s at any given index can include other expressions as + // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or + // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type + // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range + // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value + // matches the injected counter index); and any other value is converted into a + // `CounterKind::Expression` with the expression's `new_index`. + // + // Expressions will be returned from this function in a sequential vector (array) of + // `CounterExpression`, so the expression IDs must be mapped from their original, + // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. + // + // An `Expression` as an operand will have already been encountered as an `Expression` with + // operands, so its new_index will already have been generated (as a 1-up index value). + // (If an `Expression` as an operand does not have a corresponding new_index, it was + // probably optimized out, after the expression was injected into the MIR, so it will + // get a `CounterKind::Zero` instead.) + // + // In other words, an `Expression`s at any given index can include other expressions as // operands, but expression operands can only come from the subset of expressions having - // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is + // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. let id_to_counter = - |new_indexes: &IndexVec, + |new_indexes: &IndexVec>, id: ExpressionOperandId| { if id == ExpressionOperandId::ZERO { Some(Counter::zero()) } else if id.index() < self.counters.len() { + // Note: Some codegen-injected Counters may be only referenced by `Expression`s, + // and may not have their own `CodeRegion`s, let index = CounterValueReference::from(id.index()); - self.counters - .get(index) - .unwrap() // pre-validated - .as_ref() - .map(|_| Counter::counter_value_reference(index)) + Some(Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); self.expressions .get(index) .expect("expression id is out of range") .as_ref() - .map(|_| Counter::expression(new_indexes[index])) + // If an expression was optimized out, assume it would have produced a count + // of zero. This ensures that expressions dependent on optimized-out + // expressions are still valid. + .map_or(Some(Counter::zero()), |_| { + new_indexes[index].map(|new_index| Counter::expression(new_index)) + }) } }; - for (original_index, expression_region) in + for (original_index, expression) in self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { // Option::map() will return None to filter out missing expressions. This may happen // if, for example, a MIR-instrumented expression is removed during an optimization. - entry.as_ref().map(|region| (original_index, region)) + entry.as_ref().map(|expression| (original_index, expression)) }) { - let region = &expression_region.region; - let ExpressionRegion { lhs, op, rhs, .. } = *expression_region; + let optional_region = &expression.region; + let Expression { lhs, op, rhs, .. } = *expression; if let Some(Some((lhs_counter, rhs_counter))) = id_to_counter(&new_indexes, lhs).map(|lhs_counter| { id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) }) { + debug_assert!( + (lhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); + debug_assert!( + (rhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); // Both operands exist. `Expression` operands exist in `self.expressions` and have // been assigned a `new_index`. let mapped_expression_index = @@ -190,12 +231,20 @@ impl FunctionCoverage { rhs_counter, ); debug!( - "Adding expression {:?} = {:?} at {:?}", - mapped_expression_index, expression, region + "Adding expression {:?} = {:?}, region: {:?}", + mapped_expression_index, expression, optional_region ); counter_expressions.push(expression); - new_indexes[original_index] = mapped_expression_index; - expression_regions.push((Counter::expression(mapped_expression_index), region)); + new_indexes[original_index] = Some(mapped_expression_index); + if let Some(region) = optional_region { + expression_regions.push((Counter::expression(mapped_expression_index), region)); + } + } else { + debug!( + "Ignoring expression with one or more missing operands: \ + original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", + original_index, lhs, op, rhs, optional_region, + ) } } (counter_expressions, expression_regions.into_iter()) diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index 4811adea9ec06..a115d35866638 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -10,25 +10,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let Coverage { kind, code_region } = coverage; match kind { CoverageKind::Counter { function_source_hash, id } => { - if bx.add_counter_region(self.instance, function_source_hash, id, code_region) { + if bx.set_function_source_hash(self.instance, function_source_hash) { + // If `set_function_source_hash()` returned true, the coverage map is enabled, + // so continue adding the counter. + if let Some(code_region) = code_region { + // Note: Some counters do not have code regions, but may still be referenced + // from expressions. In that case, don't add the counter to the coverage map, + // but do inject the counter intrinsic. + bx.add_coverage_counter(self.instance, id, code_region); + } + let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); let fn_name = bx.create_pgo_func_name_var(self.instance); let hash = bx.const_u64(function_source_hash); let num_counters = bx.const_u32(coverageinfo.num_counters); - let id = bx.const_u32(u32::from(id)); + let index = bx.const_u32(u32::from(id)); debug!( "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", - fn_name, hash, num_counters, id, + fn_name, hash, num_counters, index, ); - bx.instrprof_increment(fn_name, hash, num_counters, id); + bx.instrprof_increment(fn_name, hash, num_counters, index); } } CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_counter_expression_region(self.instance, id, lhs, op, rhs, code_region); + bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region); } CoverageKind::Unreachable => { - bx.add_unreachable_region(self.instance, code_region); + bx.add_coverage_unreachable( + self.instance, + code_region.expect("unreachable regions always have code regions"), + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 3b1654f3ad4fc..95bddfb4b41d1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -9,29 +9,37 @@ pub trait CoverageInfoMethods: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; + /// Returns true if the function source hash was added to the coverage map (even if it had + /// already been added, for this instance). Returns false *only* if `-Z instrument-coverage` is + /// not enabled (a coverage map is not being generated). + fn set_function_source_hash( + &mut self, + instance: Instance<'tcx>, + function_source_hash: u64, + ) -> bool; + /// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage` /// is not enabled (a coverage map is not being generated). - fn add_counter_region( + fn add_coverage_counter( &mut self, instance: Instance<'tcx>, - function_source_hash: u64, - id: CounterValueReference, + index: CounterValueReference, region: CodeRegion, ) -> bool; /// Returns true if the expression was added to the coverage map; false if /// `-Z instrument-coverage` is not enabled (a coverage map is not being generated). - fn add_counter_expression_region( + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) -> bool; /// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage` /// is not enabled (a coverage map is not being generated). - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool; + fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool; } diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index d332466160ce5..9653ff022f192 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -643,6 +643,7 @@ where } if options.contains(&RenderOption::DarkTheme) { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 0421eabc2dc05..3a97e05a39fe8 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -7,6 +7,10 @@ use std::cmp::Ord; use std::fmt::{self, Debug, Formatter}; rustc_index::newtype_index! { + /// An ExpressionOperandId value is assigned directly from either a + /// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32() + /// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a + /// constant value of `0`. pub struct ExpressionOperandId { derive [HashStable] DEBUG_FORMAT = "ExpressionOperandId({})", @@ -42,6 +46,20 @@ impl CounterValueReference { } rustc_index::newtype_index! { + /// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32() + /// + /// Values descend from u32::MAX. + pub struct InjectedExpressionId { + derive [HashStable] + DEBUG_FORMAT = "InjectedExpressionId({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + /// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32() + /// + /// Values ascend from 0. pub struct InjectedExpressionIndex { derive [HashStable] DEBUG_FORMAT = "InjectedExpressionIndex({})", @@ -50,6 +68,9 @@ rustc_index::newtype_index! { } rustc_index::newtype_index! { + /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their + /// array position in the LLVM coverage map "Expressions" array, which is assembled during the + /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s. pub struct MappedExpressionIndex { derive [HashStable] DEBUG_FORMAT = "MappedExpressionIndex({})", @@ -64,21 +85,21 @@ impl From for ExpressionOperandId { } } -impl From for ExpressionOperandId { +impl From for ExpressionOperandId { #[inline] - fn from(v: InjectedExpressionIndex) -> ExpressionOperandId { + fn from(v: InjectedExpressionId) -> ExpressionOperandId { ExpressionOperandId::from(v.as_u32()) } } -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub enum CoverageKind { Counter { function_source_hash: u64, id: CounterValueReference, }, Expression { - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, @@ -88,12 +109,47 @@ pub enum CoverageKind { impl CoverageKind { pub fn as_operand_id(&self) -> ExpressionOperandId { + use CoverageKind::*; match *self { - CoverageKind::Counter { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Expression { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Unreachable => { - bug!("Unreachable coverage cannot be part of an expression") - } + Counter { id, .. } => ExpressionOperandId::from(id), + Expression { id, .. } => ExpressionOperandId::from(id), + Unreachable => bug!("Unreachable coverage cannot be part of an expression"), + } + } + + pub fn is_counter(&self) -> bool { + match self { + Self::Counter { .. } => true, + _ => false, + } + } + + pub fn is_expression(&self) -> bool { + match self { + Self::Expression { .. } => true, + _ => false, + } + } + + pub fn is_unreachable(&self) -> bool { + *self == Self::Unreachable + } +} + +impl Debug for CoverageKind { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use CoverageKind::*; + match self { + Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), + Expression { id, lhs, op, rhs } => write!( + fmt, + "Expression({:?}) = {} {} {}", + id.index(), + lhs.index(), + if *op == Op::Add { "+" } else { "-" }, + rhs.index(), + ), + Unreachable => write!(fmt, "Unreachable"), } } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index bf091201e1004..5fe7b0f647dcd 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1585,21 +1585,10 @@ impl Debug for Statement<'_> { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } Coverage(box ref coverage) => { - let rgn = &coverage.code_region; - match coverage.kind { - CoverageKind::Counter { id, .. } => { - write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn) - } - CoverageKind::Expression { id, lhs, op, rhs } => write!( - fmt, - "Coverage::Expression({:?}) = {} {} {} for {:?}", - id.index(), - lhs.index(), - if op == coverage::Op::Add { "+" } else { "-" }, - rhs.index(), - rgn - ), - CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn), + if let Some(rgn) = &coverage.code_region { + write!(fmt, "Coverage::{:?} for {:?}", coverage.kind, rgn) + } else { + write!(fmt, "Coverage::{:?}", coverage.kind) } } Nop => write!(fmt, "nop"), @@ -1610,7 +1599,7 @@ impl Debug for Statement<'_> { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct Coverage { pub kind: CoverageKind, - pub code_region: CodeRegion, + pub code_region: Option, } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 431225e276751..89fd803fe5140 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -300,6 +300,7 @@ CloneTypeFoldableAndLiftImpls! { ::rustc_target::spec::abi::Abi, crate::mir::coverage::ExpressionOperandId, crate::mir::coverage::CounterValueReference, + crate::mir::coverage::InjectedExpressionId, crate::mir::coverage::InjectedExpressionIndex, crate::mir::coverage::MappedExpressionIndex, crate::mir::Local, diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs new file mode 100644 index 0000000000000..d6c2f7f7aaf1d --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -0,0 +1,615 @@ +use super::Error; + +use super::debug; +use super::graph; +use super::spans; + +use debug::{DebugCounters, NESTED_INDENT}; +use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops}; +use spans::CoverageSpan; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::coverage::*; + +/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR +/// `Coverage` statements. +pub(crate) struct CoverageCounters { + function_source_hash: u64, + next_counter_id: u32, + num_expressions: u32, + pub debug_counters: DebugCounters, +} + +impl CoverageCounters { + pub fn new(function_source_hash: u64) -> Self { + Self { + function_source_hash, + next_counter_id: CounterValueReference::START.as_u32(), + num_expressions: 0, + debug_counters: DebugCounters::new(), + } + } + + /// Activate the `DebugCounters` data structures, to provide additional debug formatting + /// features when formating `CoverageKind` (counter) values. + pub fn enable_debug(&mut self) { + self.debug_counters.enable(); + } + + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s + /// representing intermediate values. + pub fn make_bcb_counters( + &mut self, + basic_coverage_blocks: &mut CoverageGraph, + coverage_spans: &Vec, + ) -> Result, Error> { + let mut bcb_counters = BcbCounters::new(self, basic_coverage_blocks); + bcb_counters.make_bcb_counters(coverage_spans) + } + + fn make_counter(&mut self, debug_block_label_fn: F) -> CoverageKind + where + F: Fn() -> Option, + { + let counter = CoverageKind::Counter { + function_source_hash: self.function_source_hash, + id: self.next_counter(), + }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&counter, (debug_block_label_fn)()); + } + counter + } + + fn make_expression( + &mut self, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + debug_block_label_fn: F, + ) -> CoverageKind + where + F: Fn() -> Option, + { + let id = self.next_expression(); + let expression = CoverageKind::Expression { id, lhs, op, rhs }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&expression, (debug_block_label_fn)()); + } + expression + } + + pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + let some_debug_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() + } else { + None + }; + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_debug_block_label.clone() + }) + } + + /// Counter IDs start from one and go up. + 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) + } + + /// Expression IDs start from u32::MAX and go down because a Expression can reference + /// (add or subtract counts) of both Counter regions and Expression regions. The counter + /// expression operand IDs must be unique across both types. + fn next_expression(&mut self) -> InjectedExpressionId { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = u32::MAX - self.num_expressions; + self.num_expressions += 1; + InjectedExpressionId::from(next) + } +} + +/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be +/// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable expression +/// (adding or subtracting two other counters or expressions) can compute the same result as an +/// embedded counter, an `Expression` should be used. +struct BcbCounters<'a> { + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, +} + +// FIXME(richkadel): Add unit tests for `BcbCounters` functions/algorithms. +impl<'a> BcbCounters<'a> { + fn new( + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, + ) -> Self { + Self { coverage_counters, basic_coverage_blocks } + } + + /// If two `BasicCoverageBlock`s branch from another `BasicCoverageBlock`, one of the branches + /// can be counted by `Expression` by subtracting the other branch from the branching + /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. + /// One way to predict which branch executes the least is by considering loops. A loop is exited + /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost + /// always executed less than the branch that does not exit the loop. + /// + /// Returns any non-code-span expressions created to represent intermediate values (such as to + /// add two counters so the result can be subtracted from another counter), or an Error with + /// message for subsequent debugging. + fn make_bcb_counters( + &mut self, + coverage_spans: &Vec, + ) -> Result, Error> { + debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); + let num_bcbs = self.basic_coverage_blocks.num_nodes(); + let mut collect_intermediate_expressions = Vec::with_capacity(num_bcbs); + + let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs); + for covspan in coverage_spans { + bcbs_with_coverage.insert(covspan.bcb); + } + + // Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated + // `CoverageSpan`, add a counter. If the `BasicCoverageBlock` branches, add a counter or + // expression to each branch `BasicCoverageBlock` (if the branch BCB has only one incoming + // edge) or edge from the branching BCB to the branch BCB (if the branch BCB has multiple + // incoming edges). + // + // The `TraverseCoverageGraphWithLoops` traversal ensures that, when a loop is encountered, + // all `BasicCoverageBlock` nodes in the loop are visited before visiting any node outside + // the loop. The `traversal` state includes a `context_stack`, providing a way to know if + // the current BCB is in one or more nested loops or not. + let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks); + while let Some(bcb) = traversal.next(self.basic_coverage_blocks) { + if bcbs_with_coverage.contains(bcb) { + debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); + let branching_counter_operand = + self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?; + + if self.bcb_needs_branch_counters(bcb) { + self.make_branch_counters( + &mut traversal, + bcb, + branching_counter_operand, + &mut collect_intermediate_expressions, + )?; + } + } else { + debug!( + "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ + and when a covered BCB has an expression dependency.", + bcb, + ); + } + } + + if traversal.is_complete() { + Ok(collect_intermediate_expressions) + } else { + Error::from_string(format!( + "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", + traversal.unvisited(), + )) + } + } + + fn make_branch_counters( + &mut self, + traversal: &mut TraverseCoverageGraphWithLoops, + branching_bcb: BasicCoverageBlock, + branching_counter_operand: ExpressionOperandId, + collect_intermediate_expressions: &mut Vec, + ) -> Result<(), Error> { + let branches = self.bcb_branches(branching_bcb); + debug!( + "{:?} has some branch(es) without counters:\n {}", + branching_bcb, + branches + .iter() + .map(|branch| { + format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks)) + }) + .collect::>() + .join("\n "), + ); + + // Use the `traversal` state to decide if a subset of the branches exit a loop, making it + // likely that branch is executed less than branches that do not exit the same loop. In this + // case, any branch that does not exit the loop (and has not already been assigned a + // counter) should be counted by expression, if possible. (If a preferred expression branch + // is not selected based on the loop context, select any branch without an existing + // counter.) + let expression_branch = self.choose_preferred_expression_branch(traversal, &branches); + + // Assign a Counter or Expression to each branch, plus additional `Expression`s, as needed, + // to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + // Skip the selected `expression_branch`, if any. It's expression will be assigned after + // all others. + if branch != expression_branch { + let branch_counter_operand = if branch.is_only_path_to_target() { + debug!( + " {:?} has only one incoming edge (from {:?}), so adding a \ + counter", + branch, branching_bcb + ); + self.get_or_make_counter_operand( + branch.target_bcb, + collect_intermediate_expressions, + )? + } else { + debug!(" {:?} has multiple incoming edges, so adding an edge counter", branch); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch.target_bcb, + collect_intermediate_expressions, + )? + }; + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + branch_counter_operand, + Op::Add, + sumup_counter_operand, + || None, + ); + debug!( + " [new intermediate expression: {}]", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); + } + } + } + + // Assign the final expression to the `expression_branch` by subtracting the total of all + // other branches from the counter of the branching BCB. + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + debug!( + "Making an expression for the selected expression_branch: {:?} \ + (expression_branch predecessors: {:?})", + expression_branch, + self.bcb_predecessors(expression_branch.target_bcb), + ); + let expression = self.coverage_counters.make_expression( + branching_counter_operand, + Op::Subtract, + sumup_counter_operand, + || Some(format!("{:?}", expression_branch)), + ); + debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); + let bcb = expression_branch.target_bcb; + if expression_branch.is_only_path_to_target() { + self.basic_coverage_blocks[bcb].set_counter(expression)?; + } else { + self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?; + } + Ok(()) + } + + fn get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) + } + + fn recursive_get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + // If the BCB already has a counter, return it. + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!( + "{}{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(counter_kind), + ); + return Ok(counter_kind.as_operand_id()); + } + + // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`). + // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the + // program results in a tight infinite loop, but it should still compile. + let one_path_to_target = self.bcb_has_one_path_to_target(bcb); + if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { + let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb))); + if one_path_to_target { + debug!( + "{}{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } else { + debug!( + "{}{:?} has itself as its own predecessor. It can't be part of its own \ + Expression sum, so it will get its own new counter: {}. (Note, the compiled \ + code will generate an infinite loop.)", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } + return self.basic_coverage_blocks[bcb].set_counter(counter_kind); + } + + // A BCB with multiple incoming edges can compute its count by `Expression`, summing up the + // counters and/or expressions of its incoming edges. This will recursively get or create + // counters for those incoming edges first, then call `make_expression()` to sum them up, + // with additional intermediate expressions as needed. + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + debug!( + "{}{:?} has multiple incoming edges and will get an expression that sums them up...", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + ); + let first_edge_counter_operand = self.recursive_get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self.recursive_get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || None, + ); + debug!( + "{}new intermediate expression: {}", + NESTED_INDENT.repeat(debug_indent_level), + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand.replace(intermediate_expression_operand); + } + } + let counter_kind = self.coverage_counters.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || Some(format!("{:?}", bcb)), + ); + debug!( + "{}{:?} gets a new counter (sum of predecessor counters): {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } + + fn get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_edge_counter_operand( + from_bcb, + to_bcb, + collect_intermediate_expressions, + 1, + ) + } + + fn recursive_get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + // If the source BCB has only one successor (assumed to be the given target), an edge + // counter is unnecessary. Just get or make a counter for the source BCB. + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() == 1 { + return self.recursive_get_or_make_counter_operand( + from_bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + ); + } + + // If the edge already has a counter, return it. + if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) { + debug!( + "{}Edge {:?}->{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); + return Ok(counter_kind.as_operand_id()); + } + + // Make a new counter to count this edge. + let counter_kind = + self.coverage_counters.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + "{}Edge {:?}->{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) + } + + /// Select a branch for the expression, either the recommended `reloop_branch`, or if none was + /// found, select any branch. + fn choose_preferred_expression_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> BcbBranch { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches); + if let Some(reloop_branch_without_counter) = + some_reloop_branch.filter(branch_needs_a_counter) + { + debug!( + "Selecting reloop_branch={:?} that still needs a counter, to get the \ + `Expression`", + reloop_branch_without_counter + ); + reloop_branch_without_counter + } else { + let &branch_without_counter = branches + .iter() + .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); + debug!( + "Selecting any branch={:?} that still needs a counter, to get the \ + `Expression` because there was no `reloop_branch`, or it already had a \ + counter", + branch_without_counter + ); + branch_without_counter + } + } + + /// At most, one of the branches (or its edge, from the branching_bcb, if the branch has + /// multiple incoming edges) can have a counter computed by expression. + /// + /// If at least one of the branches leads outside of a loop (`found_loop_exit` is + /// true), and at least one other branch does not exit the loop (the first of which + /// is captured in `some_reloop_branch`), it's likely any reloop branch will be + /// executed far more often than loop exit branch, making the reloop branch a better + /// candidate for an expression. + fn find_some_reloop_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> Option { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let mut some_reloop_branch: Option = None; + for context in traversal.context_stack.iter().rev() { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { + let mut found_loop_exit = false; + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) + }) { + if let Some(reloop_branch) = some_reloop_branch { + if reloop_branch.counter(&self.basic_coverage_blocks).is_none() { + // we already found a candidate reloop_branch that still + // needs a counter + continue; + } + } + // The path from branch leads back to the top of the loop. Set this + // branch as the `reloop_branch`. If this branch already has a + // counter, and we find another reloop branch that doesn't have a + // counter yet, that branch will be selected as the `reloop_branch` + // instead. + some_reloop_branch = Some(branch); + } else { + // The path from branch leads outside this loop + found_loop_exit = true; + } + if found_loop_exit + && some_reloop_branch.filter(branch_needs_a_counter).is_some() + { + // Found both a branch that exits the loop and a branch that returns + // to the top of the loop (`reloop_branch`), and the `reloop_branch` + // doesn't already have a counter. + break; + } + } + if !found_loop_exit { + debug!( + "No branches exit the loop, so any branch without an existing \ + counter can have the `Expression`." + ); + break; + } + if some_reloop_branch.is_some() { + debug!( + "Found a branch that exits the loop and a branch the loops back to \ + the top of the loop (`reloop_branch`). The `reloop_branch` will \ + get the `Expression`, as long as it still needs a counter." + ); + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + some_reloop_branch + } + + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { + self.bcb_successors(from_bcb) + .iter() + .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) + .collect::>() + } + + fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + let branches = self.bcb_branches(bcb); + branches.len() > 1 && branches.iter().any(branch_needs_a_counter) + } + + /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be + /// the entry point for the function.) + #[inline] + fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() <= 1 + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.basic_coverage_blocks.is_dominated_by(node, dom) + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs new file mode 100644 index 0000000000000..cc697dfd7fe28 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -0,0 +1,836 @@ +//! The `InstrumentCoverage` MIR pass implementation includes debugging tools and options +//! to help developers understand and/or improve the analysis and instrumentation of a MIR. +//! +//! To enable coverage, include the rustc command line option: +//! +//! * `-Z instrument-coverage` +//! +//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview +//! ------------------------------------------------------------------------------------ +//! +//! Additional debugging options include: +//! +//! * `-Z dump-mir=InstrumentCoverage` - Generate `.mir` files showing the state of the MIR, +//! before and after the `InstrumentCoverage` pass, for each compiled function. +//! +//! * `-Z dump-mir-graphviz` - If `-Z dump-mir` is also enabled for the current MIR node path, +//! each MIR dump is accompanied by a before-and-after graphical view of the MIR, in Graphviz +//! `.dot` file format (which can be visually rendered as a graph using any of a number of free +//! Graphviz viewers and IDE extensions). +//! +//! For the `InstrumentCoverage` pass, this option also enables generation of an additional +//! Graphviz `.dot` file for each function, rendering the `CoverageGraph`: the control flow +//! graph (CFG) of `BasicCoverageBlocks` (BCBs), as nodes, internally labeled to show the +//! `CoverageSpan`-based MIR elements each BCB represents (`BasicBlock`s, `Statement`s and +//! `Terminator`s), assigned coverage counters and/or expressions, and edge counters, as needed. +//! +//! (Note the additional option, `-Z graphviz-dark-mode`, can be added, to change the rendered +//! output from its default black-on-white background to a dark color theme, if desired.) +//! +//! * `-Z dump-mir-spanview` - If `-Z dump-mir` is also enabled for the current MIR node path, +//! each MIR dump is accompanied by a before-and-after `.html` document showing the function's +//! original source code, highlighted by it's MIR spans, at the `statement`-level (by default), +//! `terminator` only, or encompassing span for the `Terminator` plus all `Statement`s, in each +//! `block` (`BasicBlock`). +//! +//! For the `InstrumentCoverage` pass, this option also enables generation of an additional +//! spanview `.html` file for each function, showing the aggregated `CoverageSpan`s that will +//! require counters (or counter expressions) for accurate coverage analysis. +//! +//! Debug Logging +//! ------------- +//! +//! The `InstrumentCoverage` pass includes debug logging messages at various phases and decision +//! points, which can be enabled via environment variable: +//! +//! ```shell +//! RUSTC_LOG=rustc_mir::transform::coverage=debug +//! ``` +//! +//! Other module paths with coverage-related debug logs may also be of interest, particularly for +//! debugging the coverage map data, injected as global variables in the LLVM IR (during rustc's +//! code generation pass). For example: +//! +//! ```shell +//! RUSTC_LOG=rustc_mir::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug +//! ``` +//! +//! Coverage Debug Options +//! --------------------------------- +//! +//! Additional debugging options can be enabled using the environment variable: +//! +//! ```shell +//! RUSTC_COVERAGE_DEBUG_OPTIONS= +//! ``` +//! +//! These options are comma-separated, and specified in the format `option-name=value`. For example: +//! +//! ```shell +//! $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=id+operation,allow-unused-expressions=yes cargo build +//! ``` +//! +//! Coverage debug options include: +//! +//! * `allow-unused-expressions=yes` or `no` (default: `no`) +//! +//! The `InstrumentCoverage` algorithms _should_ only create and assign expressions to a +//! `BasicCoverageBlock`, or an incoming edge, if that expression is either (a) required to +//! count a `CoverageSpan`, or (b) a dependency of some other required counter expression. +//! +//! If an expression is generated that does not map to a `CoverageSpan` or dependency, this +//! probably indicates there was a bug in the algorithm that creates and assigns counters +//! and expressions. +//! +//! When this kind of bug is encountered, the rustc compiler will panic by default. Setting: +//! `allow-unused-expressions=yes` will log a warning message instead of panicking (effectively +//! ignoring the unused expressions), which may be helpful when debugging the root cause of +//! the problem. +//! +//! * `counter-format=`, where `` can be any plus-separated combination of `id`, +//! `block`, and/or `operation` (default: `block+operation`) +//! +//! This option effects both the `CoverageGraph` (graphviz `.dot` files) and debug logging, when +//! generating labels for counters and expressions. +//! +//! Depending on the values and combinations, counters can be labeled by: +//! +//! * `id` - counter or expression ID (ascending counter IDs, starting at 1, or descending +//! expression IDs, starting at `u32:MAX`) +//! * `block` - the `BasicCoverageBlock` label (for example, `bcb0`) or edge label (for +//! example `bcb0->bcb1`), for counters or expressions assigned to count a +//! `BasicCoverageBlock` or edge. Intermediate expressions (not directly associated with +//! a BCB or edge) will be labeled by their expression ID, unless `operation` is also +//! specified. +//! * `operation` - applied to expressions only, labels include the left-hand-side counter +//! or expression label (lhs operand), the operator (`+` or `-`), and the right-hand-side +//! counter or expression (rhs operand). Expression operand labels are generated +//! recursively, generating labels with nested operations, enclosed in parentheses +//! (for example: `bcb2 + (bcb0 - bcb1)`). + +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use super::spans::CoverageSpan; + +use crate::util::generic_graphviz::GraphvizWriter; +use crate::util::pretty; +use crate::util::spanview::{self, SpanViewable}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_index::vec::Idx; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; +use rustc_middle::ty::TyCtxt; + +use std::lazy::SyncOnceCell; + +pub const NESTED_INDENT: &str = " "; + +const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; + +pub(crate) fn debug_options<'a>() -> &'a DebugOptions { + static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); + + &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) +} + +/// Parses and maintains coverage-specific debug options captured from the environment variable +/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. +#[derive(Debug, Clone)] +pub(crate) struct DebugOptions { + pub allow_unused_expressions: bool, + counter_format: ExpressionFormat, +} + +impl DebugOptions { + fn from_env() -> Self { + let mut allow_unused_expressions = true; + let mut counter_format = ExpressionFormat::default(); + + if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { + for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(",") { + let mut setting = setting_str.splitn(2, "="); + match setting.next() { + Some(option) if option == "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, setting.next()); + debug!( + "{} env option `allow_unused_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions + ); + } + Some(option) if option == "counter_format" => { + if let Some(strval) = setting.next() { + counter_format = counter_format_option_val(strval); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); + } else { + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); + } + } + Some("") => {} + Some(invalid) => bug!( + "Unsupported setting `{}` in environment variable {}", + invalid, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + None => {} + } + } + } + + Self { allow_unused_expressions, counter_format } + } +} + +fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { + if let Some(val) = some_strval { + if vec!["yes", "y", "on", "true"].contains(&val) { + true + } else if vec!["no", "n", "off", "false"].contains(&val) { + false + } else { + bug!( + "Unsupported value `{}` for option `{}` in environment variable {}", + option, + val, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) + } + } else { + true + } +} + +fn counter_format_option_val(strval: &str) -> ExpressionFormat { + let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; + let components = strval.splitn(3, "+"); + for component in components { + match component { + "id" => counter_format.id = true, + "block" => counter_format.block = true, + "operation" => counter_format.operation = true, + _ => bug!( + "Unsupported counter_format choice `{}` in environment variable {}", + component, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + } + } + counter_format +} + +#[derive(Debug, Clone)] +struct ExpressionFormat { + id: bool, + block: bool, + operation: bool, +} + +impl Default for ExpressionFormat { + fn default() -> Self { + Self { id: false, block: true, operation: true } + } +} + +/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to +/// the `CoverageKind` data and optional label (normally, the counter's associated +/// `BasicCoverageBlock` format string, if any). +/// +/// Use `format_counter` to convert one of these `CoverageKind` counters to a debug output string, +/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump +/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment +/// variable. +/// +/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be +/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. +pub(crate) struct DebugCounters { + some_counters: Option>, +} + +impl DebugCounters { + pub fn new() -> Self { + Self { some_counters: None } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_counters.replace(FxHashMap::default()); + } + + pub fn is_enabled(&self) -> bool { + self.some_counters.is_some() + } + + pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { + if let Some(counters) = &mut self.some_counters { + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => bug!( + "the given `CoverageKind` is not an counter or expression: {:?}", + counter_kind + ), + }; + counters + .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect_none( + "attempt to add the same counter_kind to DebugCounters more than once", + ); + } + } + + pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + self.some_counters.as_ref().map_or(None, |counters| { + counters + .get(&operand) + .map_or(None, |debug_counter| debug_counter.some_block_label.as_ref()) + }) + } + + pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + match *counter_kind { + CoverageKind::Counter { .. } => { + format!("Counter({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Expression { .. } => { + format!("Expression({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), + } + } + + fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + let counter_format = &debug_options().counter_format; + if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if counter_format.operation { + return format!( + "{}{} {} {}", + if counter_format.id || self.some_counters.is_none() { + format!("#{} = ", id.index()) + } else { + String::new() + }, + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ); + } + } + + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => { + bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind) + } + }; + if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { + let counters = self.some_counters.as_ref().unwrap(); + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = + counters.get(&id.into()) + { + return if counter_format.id { + format!("{}#{}", block_label, id.index()) + } else { + format!("{}", block_label) + }; + } + } + format!("#{}", id.index()) + } + + fn format_operand(&self, operand: ExpressionOperandId) -> String { + if operand.index() == 0 { + return String::from("0"); + } + if let Some(counters) = &self.some_counters { + if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let CoverageKind::Expression { .. } = counter_kind { + if let Some(block_label) = some_block_label { + if debug_options().counter_format.block { + return format!( + "{}:({})", + block_label, + self.format_counter_kind(counter_kind) + ); + } + } + return format!("({})", self.format_counter_kind(counter_kind)); + } + return format!("{}", self.format_counter_kind(counter_kind)); + } + } + format!("#{}", operand.index().to_string()) + } +} + +/// A non-public support class to `DebugCounters`. +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { counter_kind, some_block_label } + } +} + +/// If enabled, this data structure captures additional debugging information used when generating +/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. +pub(crate) struct GraphvizData { + some_bcb_to_coverage_spans_with_counters: + Option>>, + some_bcb_to_dependency_counters: Option>>, + some_edge_to_counter: Option>, +} + +impl GraphvizData { + pub fn new() -> Self { + Self { + some_bcb_to_coverage_spans_with_counters: None, + some_bcb_to_dependency_counters: None, + some_edge_to_counter: None, + } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); + self.some_bcb_to_dependency_counters = Some(FxHashMap::default()); + self.some_edge_to_counter = Some(FxHashMap::default()); + } + + pub fn is_enabled(&self) -> bool { + self.some_bcb_to_coverage_spans_with_counters.is_some() + } + + pub fn add_bcb_coverage_span_with_counter( + &mut self, + bcb: BasicCoverageBlock, + coverage_span: &CoverageSpan, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_mut() + { + bcb_to_coverage_spans_with_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push((coverage_span.clone(), counter_kind.clone())); + } + } + + pub fn get_bcb_coverage_spans_with_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec<(CoverageSpan, CoverageKind)>> { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_ref() + { + bcb_to_coverage_spans_with_counters.get(&bcb) + } else { + None + } + } + + pub fn add_bcb_dependency_counter( + &mut self, + bcb: BasicCoverageBlock, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { + bcb_to_dependency_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push(counter_kind.clone()); + } + } + + pub fn get_bcb_dependency_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec> { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { + bcb_to_dependency_counters.get(&bcb) + } else { + None + } + } + + pub fn set_edge_counter( + &mut self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + counter_kind: &CoverageKind, + ) { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { + edge_to_counter.insert((from_bcb, to_bb), counter_kind.clone()).expect_none( + "invalid attempt to insert more than one edge counter for the same edge", + ); + } + } + + pub fn get_edge_counter( + &self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + ) -> Option<&CoverageKind> { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { + edge_to_counter.get(&(from_bcb, to_bb)) + } else { + None + } + } +} + +/// If enabled, this struct captures additional data used to track whether expressions were used, +/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are +/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs +/// and/or a `CoverageGraph` graphviz output). +pub(crate) struct UsedExpressions { + some_used_expression_operands: + Option>>, + some_unused_expressions: + Option, BasicCoverageBlock)>>, +} + +impl UsedExpressions { + pub fn new() -> Self { + Self { some_used_expression_operands: None, some_unused_expressions: None } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_used_expression_operands = Some(FxHashMap::default()); + self.some_unused_expressions = Some(Vec::new()); + } + + pub fn is_enabled(&self) -> bool { + self.some_used_expression_operands.is_some() + } + + pub fn add_expression_operands(&mut self, expression: &CoverageKind) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { + if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } + } + + pub fn expression_is_used(&self, expression: &CoverageKind) -> bool { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + used_expression_operands.contains_key(&expression.as_operand_id()) + } else { + false + } + } + + pub fn add_unused_expression_if_not_found( + &mut self, + expression: &CoverageKind, + edge_from_bcb: Option, + target_bcb: BasicCoverageBlock, + ) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + if !used_expression_operands.contains_key(&expression.as_operand_id()) { + self.some_unused_expressions.as_mut().unwrap().push(( + expression.clone(), + edge_from_bcb, + target_bcb, + )); + } + } + } + + /// Return the list of unused counters (if any) as a tuple with the counter (`CoverageKind`), + /// optional `from_bcb` (if it was an edge counter), and `target_bcb`. + pub fn get_unused_expressions( + &self, + ) -> Vec<(CoverageKind, Option, BasicCoverageBlock)> { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + unused_expressions.clone() + } else { + Vec::new() + } + } + + /// If enabled, validate that every BCB or edge counter not directly associated with a coverage + /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_ + /// associated with a coverage span). + pub fn validate( + &mut self, + bcb_counters_without_direct_coverage_spans: &Vec<( + Option, + BasicCoverageBlock, + CoverageKind, + )>, + ) { + if self.is_enabled() { + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if self.expression_is_used(counter_kind) { + self.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); + } + } + } + } + } + + pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions { + let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "non-coverage edge counter found without a dependent expression, in \ + {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "non-coverage counter found without a dependent expression, in {:?}; \ + counter={}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + }; + + if debug_options().allow_unused_expressions { + debug!("WARNING: {}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); + } + } + } + } +} + +/// Generates the MIR pass `CoverageSpan`-specific spanview dump file. +pub(crate) fn dump_coverage_spanview( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + pass_name: &str, + coverage_spans: &Vec, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + + let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans); + let mut file = pretty::create_dump_file(tcx, "html", None, pass_name, &0, mir_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"); +} + +/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. +fn span_viewables( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + coverage_spans: &Vec, +) -> Vec { + let mut span_viewables = Vec::new(); + for coverage_span in coverage_spans { + let tooltip = coverage_span.format_coverage_statements(tcx, mir_body); + let CoverageSpan { span, bcb, .. } = coverage_span; + let bcb_data = &basic_coverage_blocks[*bcb]; + let id = bcb_data.id(); + let leader_bb = bcb_data.leader_bb(); + span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); + } + span_viewables +} + +/// Generates the MIR pass coverage-specific graphviz dump file. +pub(crate) fn dump_coverage_graphviz( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + pass_name: &str, + basic_coverage_blocks: &CoverageGraph, + debug_counters: &DebugCounters, + graphviz_data: &GraphvizData, + intermediate_expressions: &Vec, + debug_used_expressions: &UsedExpressions, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + let node_content = |bcb| { + bcb_to_string_sections( + tcx, + mir_body, + debug_counters, + &basic_coverage_blocks[bcb], + graphviz_data.get_bcb_coverage_spans_with_counters(bcb), + graphviz_data.get_bcb_dependency_counters(bcb), + // intermediate_expressions are injected into the mir::START_BLOCK, so + // include them in the first BCB. + if bcb.index() == 0 { Some(&intermediate_expressions) } else { None }, + ) + }; + let edge_labels = |from_bcb| { + let from_bcb_data = &basic_coverage_blocks[from_bcb]; + let from_terminator = from_bcb_data.terminator(mir_body); + let mut edge_labels = from_terminator.kind.fmt_successor_labels(); + edge_labels.retain(|label| label.to_string() != "unreachable"); + let edge_counters = from_terminator + .successors() + .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); + edge_labels + .iter() + .zip(edge_counters) + .map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, debug_counters.format_counter(counter)) + } else { + label.to_string() + } + }) + .collect::>() + }; + let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); + let mut graphviz_writer = + GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels); + let unused_expressions = debug_used_expressions.get_unused_expressions(); + if unused_expressions.len() > 0 { + graphviz_writer.set_graph_label(&format!( + "Unused expressions:\n {}", + unused_expressions + .as_slice() + .iter() + .map(|(counter_kind, edge_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "{:?}: {}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } + }) + .collect::>() + .join("\n ") + )); + } + let mut file = pretty::create_dump_file(tcx, "dot", None, pass_name, &0, mir_source) + .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); + graphviz_writer + .write_graphviz(tcx, &mut file) + .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); +} + +fn bcb_to_string_sections( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + debug_counters: &DebugCounters, + bcb_data: &BasicCoverageBlockData, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, +) -> Vec { + let len = bcb_data.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| { + format!("Intermediate {}", debug_counters.format_counter(expression)) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| { + format!( + "{} at {}", + debug_counters.format_counter(counter), + covspan.format(tcx, mir_body) + ) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| debug_counters.format_counter(counter)) + .collect::>() + .join(" \n"), + )); + } + if let Some(counter_kind) = &bcb_data.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = bcb_data.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + bcb_data.basic_blocks.last().unwrap(), + term_type(&bcb_data.terminator(mir_body).kind) + )); + sections +} + +/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any +/// values it might hold. +pub(crate) 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", + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs new file mode 100644 index 0000000000000..c2ed2cbb10002 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -0,0 +1,759 @@ +use super::Error; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::graph::dominators::{self, Dominators}; +use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; + +use std::ops::{Index, IndexMut}; + +const ID_SEPARATOR: &str = ","; + +/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s +/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s, plus a +/// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional +/// set of additional counters--if needed--to count incoming edges, if there are more than one. +/// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) +pub(crate) struct CoverageGraph { + bcbs: IndexVec, + bb_to_bcb: IndexVec>, + pub successors: IndexVec>, + pub predecessors: IndexVec>, + dominators: Option>, +} + +impl CoverageGraph { + pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { + let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); + + // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock + // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the + // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a + // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using + // `bb_to_bcb` should work without requiring a deduplication step. + + let successors = IndexVec::from_fn_n( + |bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_successors = + bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) + .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) + .collect::>(); + debug_assert!({ + let mut sorted = bcb_successors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_successors + }, + bcbs.len(), + ); + + let mut predecessors = IndexVec::from_elem_n(Vec::new(), bcbs.len()); + for (bcb, bcb_successors) in successors.iter_enumerated() { + for &successor in bcb_successors { + predecessors[successor].push(bcb); + } + } + + let mut basic_coverage_blocks = + Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; + let dominators = dominators::dominators(&basic_coverage_blocks); + basic_coverage_blocks.dominators = Some(dominators); + basic_coverage_blocks + } + + fn compute_basic_coverage_blocks( + mir_body: &mir::Body<'tcx>, + ) -> ( + IndexVec, + IndexVec>, + ) { + let num_basic_blocks = mir_body.num_nodes(); + let mut bcbs = IndexVec::with_capacity(num_basic_blocks); + let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); + + // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows + // each block terminator's `successors()`. Coverage spans must map to actual source code, + // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal + // intentionally omits unwind paths. + // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and + // `catch_unwind()` handlers. + let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors); + + let mut basic_blocks = Vec::new(); + for (bb, data) in mir_cfg_without_unwind { + if let Some(last) = basic_blocks.last() { + let predecessors = &mir_body.predecessors()[bb]; + if predecessors.len() > 1 || !predecessors.contains(last) { + // The `bb` has more than one _incoming_ edge, and should start its own + // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet + // include `bb`; it contains a sequence of one or more sequential basic_blocks + // with no intermediate branches in or out. Save these as a new + // `BasicCoverageBlockData` before starting the new one.) + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_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) + } + ); + } + } + basic_blocks.push(bb); + + let term = data.terminator(); + + match term.kind { + TerminatorKind::Return { .. } + // FIXME(richkadel): Add test(s) for `Abort` coverage. + | TerminatorKind::Abort + // FIXME(richkadel): Add test(s) for `Assert` coverage. + // Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind + // branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like + // `FalseUnwinds` (which are kind of like `Goto`s)? + | TerminatorKind::Assert { .. } + // FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is + // sensible for code using the `yield` keyword. + | TerminatorKind::Yield { .. } + // FIXME(richkadel): Also add coverage tests using async/await, and threading. + + | TerminatorKind::SwitchInt { .. } => { + // The `bb` has more than one _outgoing_ edge, or exits the function. Save the + // current sequence of `basic_blocks` gathered to this point, as a new + // `BasicCoverageBlockData`. + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + debug!(" because term.kind = {:?}", term.kind); + // Note that this condition is based on `TerminatorKind`, even though it + // theoretically boils down to `successors().len() != 1`; that is, either zero + // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but + // since the BCB CFG ignores things like unwind branches (which exist in the + // `Terminator`s `successors()` list) checking the number of successors won't + // work. + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + } + + if !basic_blocks.is_empty() { + // process any remaining basic_blocks into a final `BasicCoverageBlockData` + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); + debug!(" because the end of the MIR CFG was reached while traversing"); + } + + (bcbs, bb_to_bcb) + } + + fn add_basic_coverage_block( + bcbs: &mut IndexVec, + bb_to_bcb: &mut IndexVec>, + basic_blocks: Vec, + ) { + let bcb = BasicCoverageBlock::from_usize(bcbs.len()); + for &bb in basic_blocks.iter() { + bb_to_bcb[bb] = Some(bcb); + } + let bcb_data = BasicCoverageBlockData::from(basic_blocks); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + bcbs.push(bcb_data); + } + + #[inline(always)] + pub fn iter_enumerated( + &self, + ) -> impl Iterator { + self.bcbs.iter_enumerated() + } + + #[inline(always)] + pub fn iter_enumerated_mut( + &mut self, + ) -> impl Iterator { + self.bcbs.iter_enumerated_mut() + } + + #[inline(always)] + pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { + if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } + } + + #[inline(always)] + pub fn is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.dominators.as_ref().unwrap().is_dominated_by(node, dom) + } + + #[inline(always)] + pub fn dominators(&self) -> &Dominators { + self.dominators.as_ref().unwrap() + } +} + +impl Index for CoverageGraph { + type Output = BasicCoverageBlockData; + + #[inline] + fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.bcbs[index] + } +} + +impl IndexMut for CoverageGraph { + #[inline] + fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.bcbs[index] + } +} + +impl graph::DirectedGraph for CoverageGraph { + type Node = BasicCoverageBlock; +} + +impl graph::WithNumNodes for CoverageGraph { + #[inline] + fn num_nodes(&self) -> usize { + self.bcbs.len() + } +} + +impl graph::WithStartNode for CoverageGraph { + #[inline] + fn start_node(&self) -> Self::Node { + self.bcb_from_bb(mir::START_BLOCK) + .expect("mir::START_BLOCK should be in a BasicCoverageBlock") + } +} + +type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; + +impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::iter::Cloned>; +} + +impl graph::WithSuccessors for CoverageGraph { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.successors[node].iter().cloned() + } +} + +impl graph::GraphPredecessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::vec::IntoIter; +} + +impl graph::WithPredecessors for CoverageGraph { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors[node].clone().into_iter() + } +} + +rustc_index::newtype_index! { + /// A node in the [control-flow graph][CFG] of CoverageGraph. + pub(crate) struct BasicCoverageBlock { + DEBUG_FORMAT = "bcb{}", + } +} + +/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of MIR BasicBlocks without +/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without +/// altering the original MIR CFG. +/// +/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not +/// necessary). The BCB-based CFG is a more aggressive simplification. For example: +/// +/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code, +/// that is injected by the Rust compiler but has no physical source code to count. This also +/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target +/// block, in the same BCB. (But, note: Issue #78544: "MIR InstrumentCoverage: Improve coverage +/// of `#[should_panic]` tests and `catch_unwind()` handlers") +/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are +/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as +/// a `Goto`, and merged with its successor into the same BCB. +/// +/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. +/// In some cases, a BCB's execution count can be computed by `Expression`. Additional +/// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO` +/// to the BCB's primary counter or expression). +/// +/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based +/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) +/// significance. +#[derive(Debug, Clone)] +pub(crate) struct BasicCoverageBlockData { + pub basic_blocks: Vec, + pub counter_kind: Option, + edge_from_bcbs: Option>, +} + +impl BasicCoverageBlockData { + pub fn from(basic_blocks: Vec) -> Self { + assert!(basic_blocks.len() > 0); + Self { basic_blocks, counter_kind: None, edge_from_bcbs: None } + } + + #[inline(always)] + pub fn leader_bb(&self) -> BasicBlock { + self.basic_blocks[0] + } + + #[inline(always)] + pub fn last_bb(&self) -> BasicBlock { + *self.basic_blocks.last().unwrap() + } + + #[inline(always)] + pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> { + &mir_body[self.last_bb()].terminator() + } + + pub fn set_counter( + &mut self, + counter_kind: CoverageKind, + ) -> Result { + debug_assert!( + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). + self.edge_from_bcbs.is_none() || counter_kind.is_expression(), + "attempt to add a `Counter` to a BCB target with existing incoming edge counters" + ); + let operand = counter_kind.as_operand_id(); + if let Some(replaced) = self.counter_kind.replace(counter_kind) { + Error::from_string(format!( + "attempt to set a BasicCoverageBlock coverage counter more than once; \ + {:?} already had counter {:?}", + self, replaced, + )) + } else { + Ok(operand) + } + } + + #[inline(always)] + pub fn counter(&self) -> Option<&CoverageKind> { + self.counter_kind.as_ref() + } + + #[inline(always)] + pub fn take_counter(&mut self) -> Option { + self.counter_kind.take() + } + + pub fn set_edge_counter_from( + &mut self, + from_bcb: BasicCoverageBlock, + counter_kind: CoverageKind, + ) -> Result { + if level_enabled!(tracing::Level::DEBUG) { + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). + if !self.counter_kind.as_ref().map_or(true, |c| c.is_expression()) { + return Error::from_string(format!( + "attempt to add an incoming edge counter from {:?} when the target BCB already \ + has a `Counter`", + from_bcb + )); + } + } + let operand = counter_kind.as_operand_id(); + if let Some(replaced) = self + .edge_from_bcbs + .get_or_insert_with(|| FxHashMap::default()) + .insert(from_bcb, counter_kind) + { + Error::from_string(format!( + "attempt to set an edge counter more than once; from_bcb: \ + {:?} already had counter {:?}", + from_bcb, replaced, + )) + } else { + Ok(operand) + } + } + + #[inline] + pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> { + if let Some(edge_from_bcbs) = &self.edge_from_bcbs { + edge_from_bcbs.get(&from_bcb) + } else { + None + } + } + + #[inline] + pub fn take_edge_counters( + &mut self, + ) -> Option> { + self.edge_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) + } + + pub fn id(&self) -> String { + format!( + "@{}", + self.basic_blocks + .iter() + .map(|bb| bb.index().to_string()) + .collect::>() + .join(ID_SEPARATOR) + ) + } +} + +/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`) +/// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_ +/// the specific branching BCB, representing the edge between the two. The latter case +/// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct BcbBranch { + pub edge_from_bcb: Option, + pub target_bcb: BasicCoverageBlock, +} + +impl BcbBranch { + pub fn from_to( + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + basic_coverage_blocks: &CoverageGraph, + ) -> Self { + let edge_from_bcb = if basic_coverage_blocks.predecessors[to_bcb].len() > 1 { + Some(from_bcb) + } else { + None + }; + Self { edge_from_bcb, target_bcb: to_bcb } + } + + pub fn counter<'a>( + &self, + basic_coverage_blocks: &'a CoverageGraph, + ) -> Option<&'a CoverageKind> { + if let Some(from_bcb) = self.edge_from_bcb { + basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb) + } else { + basic_coverage_blocks[self.target_bcb].counter() + } + } + + pub fn is_only_path_to_target(&self) -> bool { + self.edge_from_bcb.is_none() + } +} + +impl std::fmt::Debug for BcbBranch { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(from_bcb) = self.edge_from_bcb { + write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb) + } else { + write!(fmt, "{:?}", self.target_bcb) + } + } +} + +// Returns the `Terminator`s non-unwind successors. +// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and +// `catch_unwind()` handlers. +fn bcb_filtered_successors<'a, 'tcx>( + body: &'tcx &'a mir::Body<'tcx>, + term_kind: &'tcx TerminatorKind<'tcx>, +) -> Box + 'a> { + let mut successors = term_kind.successors(); + box match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed. + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds. + // NOTE: `chain(&[])` is required to coerce the `option::iter` (from + // `next().into_iter()`) into the `mir::Successors` aliased type. + _ => successors.next().into_iter().chain(&[]), + } + .filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) +} + +/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the +/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that +/// ensures a loop is completely traversed before processing Blocks after the end of the loop. +// FIXME(richkadel): Add unit tests for TraversalContext. +#[derive(Debug)] +pub(crate) struct TraversalContext { + /// From one or more backedges returning to a loop header. + pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, + + /// worklist, to be traversed, of CoverageGraph in the loop with the given loop + /// backedges, such that the loop is the inner inner-most loop containing these + /// CoverageGraph + pub worklist: Vec, +} + +pub(crate) struct TraverseCoverageGraphWithLoops { + pub backedges: IndexVec>, + pub context_stack: Vec, + visited: BitSet, +} + +impl TraverseCoverageGraphWithLoops { + pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self { + let start_bcb = basic_coverage_blocks.start_node(); + let backedges = find_loop_backedges(basic_coverage_blocks); + let mut context_stack = Vec::new(); + context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); + // `context_stack` starts with a `TraversalContext` for the main function context (beginning + // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top + // of the stack as loops are entered, and popped off of the stack when a loop's worklist is + // exhausted. + let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + Self { backedges, context_stack, visited } + } + + pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option { + debug!( + "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", + self.context_stack.iter().rev().collect::>() + ); + while let Some(next_bcb) = { + // Strip contexts with empty worklists from the top of the stack + while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { + self.context_stack.pop(); + } + // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. + self.context_stack.last_mut().map_or(None, |context| context.worklist.pop()) + } { + if !self.visited.insert(next_bcb) { + debug!("Already visited: {:?}", next_bcb); + continue; + } + debug!("Visiting {:?}", next_bcb); + if self.backedges[next_bcb].len() > 0 { + debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb); + self.context_stack.push(TraversalContext { + loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)), + worklist: Vec::new(), + }); + } + self.extend_worklist(basic_coverage_blocks, next_bcb); + return Some(next_bcb); + } + None + } + + pub fn extend_worklist( + &mut self, + basic_coverage_blocks: &CoverageGraph, + bcb: BasicCoverageBlock, + ) { + let successors = &basic_coverage_blocks.successors[bcb]; + debug!("{:?} has {} successors:", bcb, successors.len()); + for &successor in successors { + if successor == bcb { + debug!( + "{:?} has itself as its own successor. (Note, the compiled code will \ + generate an infinite loop.)", + bcb + ); + // Don't re-add this successor to the worklist. We are already processing it. + break; + } + for context in self.context_stack.iter_mut().rev() { + // Add successors of the current BCB to the appropriate context. Successors that + // stay within a loop are added to the BCBs context worklist. Successors that + // exit the loop (they are not dominated by the loop header) must be reachable + // from other BCBs outside the loop, and they will be added to a different + // worklist. + // + // Branching blocks (with more than one successor) must be processed before + // blocks with only one successor, to prevent unnecessarily complicating + // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the + // branching block would have given an `Expression` (or vice versa). + let (some_successor_to_add, some_loop_header) = + if let Some((_, loop_header)) = context.loop_backedges { + if basic_coverage_blocks.is_dominated_by(successor, loop_header) { + (Some(successor), Some(loop_header)) + } else { + (None, None) + } + } else { + (Some(successor), None) + }; + if let Some(successor_to_add) = some_successor_to_add { + if basic_coverage_blocks.successors[successor_to_add].len() > 1 { + debug!( + "{:?} successor is branching. Prioritize it at the beginning of \ + the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.insert(0, successor_to_add); + } else { + debug!( + "{:?} successor is non-branching. Defer it to the end of the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.push(successor_to_add); + } + break; + } + } + } + } + + pub fn is_complete(&self) -> bool { + self.visited.count() == self.visited.domain_size() + } + + pub fn unvisited(&self) -> Vec { + let mut unvisited_set: BitSet = + BitSet::new_filled(self.visited.domain_size()); + unvisited_set.subtract(&self.visited); + unvisited_set.iter().collect::>() + } +} + +fn find_loop_backedges( + basic_coverage_blocks: &CoverageGraph, +) -> IndexVec> { + let num_bcbs = basic_coverage_blocks.num_nodes(); + let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); + + // Identify loops by their backedges. + // + // The computational complexity is bounded by: n(s) x d where `n` is the number of + // `BasicCoverageBlock` nodes (the simplified/reduced representation of the CFG derived from the + // MIR); `s` is the average number of successors per node (which is most likely less than 2, and + // independent of the size of the function, so it can be treated as a constant); + // and `d` is the average number of dominators per node. + // + // The average number of dominators depends on the size and complexity of the function, and + // nodes near the start of the function's control flow graph typically have less dominators + // than nodes near the end of the CFG. Without doing a detailed mathematical analysis, I + // think the resulting complexity has the characteristics of O(n log n). + // + // The overall complexity appears to be comparable to many other MIR transform algorithms, and I + // don't expect that this function is creating a performance hot spot, but if this becomes an + // issue, there may be ways to optimize the `is_dominated_by` algorithm (as indicated by an + // existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps + // by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short + // circuit downstream `is_dominated_by` checks. + // + // For now, that kind of optimization seems unnecessarily complicated. + for (bcb, _) in basic_coverage_blocks.iter_enumerated() { + for &successor in &basic_coverage_blocks.successors[bcb] { + if basic_coverage_blocks.is_dominated_by(bcb, successor) { + let loop_header = successor; + let backedge_from_bcb = bcb; + debug!( + "Found BCB backedge: {:?} -> loop_header: {:?}", + backedge_from_bcb, loop_header + ); + backedges[loop_header].push(backedge_from_bcb); + } + } + } + backedges +} + +pub struct ShortCircuitPreorder< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> { + body: &'tcx &'a mir::Body<'tcx>, + visited: BitSet, + worklist: Vec, + filtered_successors: F, +} + +impl< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> ShortCircuitPreorder<'a, 'tcx, F> +{ + pub fn new( + body: &'tcx &'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 &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> 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)(&self.body, &term.kind)); + } + + return Some((idx, data)); + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.body.basic_blocks().len() - self.visited.count(); + (size, Some(size)) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs new file mode 100644 index 0000000000000..c55349239b034 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -0,0 +1,539 @@ +pub mod query; + +mod counters; +mod debug; +mod graph; +mod spans; + +use counters::CoverageCounters; +use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use spans::{CoverageSpan, CoverageSpans}; + +use crate::transform::MirPass; +use crate::util::pretty; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +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::coverage::*; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; + +/// A simple error message wrapper for `coverage::Error`s. +#[derive(Debug)] +pub(crate) struct Error { + message: String, +} + +impl Error { + pub fn from_string(message: String) -> Result { + Err(Self { message }) + } +} + +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. +pub struct InstrumentCoverage; + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + let mir_source = mir_body.source; + + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. + // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 + if mir_source.promoted.is_some() { + trace!( + "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", + mir_source.def_id() + ); + return; + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); + let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) + if !is_fn_like { + trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); + return; + } + + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + } +} + +struct Instrumentor<'a, 'tcx> { + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + body_span: Span, + basic_coverage_blocks: CoverageGraph, + coverage_counters: CoverageCounters, +} + +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()); + let body_span = hir_body.value.span; + let function_source_hash = hash_mir_source(tcx, hir_body); + let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + Self { + pass_name, + tcx, + mir_body, + body_span, + basic_coverage_blocks, + coverage_counters: CoverageCounters::new(function_source_hash), + } + } + + fn inject_counters(&'a mut self) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let mir_source = self.mir_body.source; + let def_id = mir_source.def_id(); + let body_span = self.body_span; + + debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); + + let mut graphviz_data = debug::GraphvizData::new(); + let mut debug_used_expressions = debug::UsedExpressions::new(); + + let dump_mir = pretty::dump_enabled(tcx, self.pass_name, def_id); + let dump_graphviz = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_graphviz; + let dump_spanview = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_spanview.is_some(); + + if dump_graphviz { + graphviz_data.enable(); + self.coverage_counters.enable_debug(); + } + + if dump_graphviz || level_enabled!(tracing::Level::DEBUG) { + debug_used_expressions.enable(); + } + + //////////////////////////////////////////////////// + // Compute `CoverageSpan`s from the `CoverageGraph`. + let coverage_spans = CoverageSpans::generate_coverage_spans( + &self.mir_body, + body_span, + &self.basic_coverage_blocks, + ); + + if dump_spanview { + debug::dump_coverage_spanview( + tcx, + self.mir_body, + &self.basic_coverage_blocks, + self.pass_name, + &coverage_spans, + ); + } + + //////////////////////////////////////////////////// + // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure + // every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` + // and all `Expression` dependencies (operands) are also generated, for any other + // `BasicCoverageBlock`s not already associated with a `CoverageSpan`. + // + // Intermediate expressions (used to compute other `Expression` values), which have no + // direct associate to any `BasicCoverageBlock`, are returned in the method `Result`. + let intermediate_expressions_or_error = self + .coverage_counters + .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans); + + let (result, intermediate_expressions) = match intermediate_expressions_or_error { + Ok(intermediate_expressions) => { + // If debugging, add any intermediate expressions (which are not associated with any + // BCB) to the `debug_used_expressions` map. + if debug_used_expressions.is_enabled() { + for intermediate_expression in &intermediate_expressions { + debug_used_expressions.add_expression_operands(intermediate_expression); + } + } + + //////////////////////////////////////////////////// + // Remove the counter or edge counter from of each `CoverageSpan`s associated + // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR. + // + // `Coverage` statements injected from `CoverageSpan`s will include the code regions + // (source code start and end positions) to be counted by the associated counter. + // + // These `CoverageSpan`-associated counters are removed from their associated + // `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph` + // are indirect counters (to be injected next, without associated code regions). + self.inject_coverage_span_counters( + coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); + + //////////////////////////////////////////////////// + // For any remaining `BasicCoverageBlock` counters (that were not associated with + // any `CoverageSpan`), inject `Coverage` statements (_without_ code region `Span`s) + // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on + // are in fact counted, even though they don't directly contribute to counting + // their own independent code region's coverage. + self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions); + + // Intermediate expressions will be injected as the final step, after generating + // debug output, if any. + //////////////////////////////////////////////////// + + (Ok(()), intermediate_expressions) + } + Err(e) => (Err(e), Vec::new()), + }; + + if graphviz_data.is_enabled() { + // Even if there was an error, a partial CoverageGraph can still generate a useful + // graphviz output. + debug::dump_coverage_graphviz( + tcx, + self.mir_body, + self.pass_name, + &self.basic_coverage_blocks, + &self.coverage_counters.debug_counters, + &graphviz_data, + &intermediate_expressions, + &debug_used_expressions, + ); + } + + if let Err(e) = result { + bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e) + }; + + // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so + // this check is performed as late as possible, to allow other debug output (logs and dump + // files), which might be helpful in analyzing unused expressions, to still be generated. + debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters); + + //////////////////////////////////////////////////// + // Finally, inject the intermediate expressions collected along the way. + for intermediate_expression in intermediate_expressions { + inject_intermediate_expression(self.mir_body, intermediate_expression); + } + } + + /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given + /// BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` maps each + /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has + /// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to + /// the BCB `Counter` value. + /// + /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the + /// `used_expression_operands` map. + fn inject_coverage_span_counters( + &mut self, + coverage_spans: Vec, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + 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()); + + let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); + for covspan in coverage_spans { + let bcb = covspan.bcb; + let span = covspan.span; + let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { + self.coverage_counters.make_identity_counter(counter_operand) + } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { + bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + debug_used_expressions.add_expression_operands(&counter_kind); + counter_kind + } else { + bug!("Every BasicCoverageBlock should have a Counter or Expression"); + }; + graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind); + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special + // cases? + let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) { + None + } else { + Some(make_code_region(file_name, &source_file, span, body_span)) + }; + inject_statement(self.mir_body, counter_kind, self.bcb_last_bb(bcb), some_code_region); + } + } + + /// Returns true if the type of `BasicCoverageBlock` (specifically, it's `BasicBlock`s + /// `TerminatorKind`) with the given `Span` (relative to the `body_span`) is known to produce + /// a redundant coverage count. + /// + /// There is at least one case for this, and if it's not handled, the last line in a function + /// will be double-counted. + /// + /// If this method returns `true`, the counter (which other `Expressions` may depend on) is + /// still injected, but without an associated code region. + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special cases? + fn is_code_region_redundant( + &self, + bcb: BasicCoverageBlock, + span: Span, + body_span: Span, + ) -> bool { + if span.hi() == body_span.hi() { + // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the + // function returns; but only some functions also _can_ return after a `Goto` block + // that ends on the closing brace of the function (with the `Return`). When this + // happens, the last character is counted 2 (or possibly more) times, when we know + // the function returned only once (of course). By giving all `Goto` terminators at + // the end of a function a `non-reportable` code region, they are still counted + // if appropriate, but they don't increment the line counter, as long as their is + // also a `Return` on that last line. + if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { + return true; + } + } + false + } + + /// `inject_coverage_span_counters()` looped through the `CoverageSpan`s and injected the + /// counter from the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the + /// process (via `take_counter()`). + /// + /// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not + /// associated with a `CoverageSpan`, should only exist if the counter is a `Expression` + /// dependency (one of the expression operands). Collect them, and inject the additional + /// counters into the MIR, without a reportable coverage span. + fn inject_indirect_counters( + &mut self, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { + bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); + } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); + } + } + } + + // If debug is enabled, validate that every BCB or edge counter not directly associated + // with a coverage span is at least indirectly associated (it is a dependency of a BCB + // counter that _is_ associated with a coverage span). + debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans); + + for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans + { + debug_used_expressions.add_unused_expression_if_not_found( + &counter_kind, + edge_from_bcb, + target_bcb, + ); + + match counter_kind { + CoverageKind::Counter { .. } => { + let inject_to_bb = if let Some(from_bcb) = edge_from_bcb { + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in + // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the + // `target_bcb`; also called the `leader_bb`). + let from_bb = self.bcb_last_bb(from_bcb); + let to_bb = self.bcb_leader_bb(target_bcb); + + let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); + graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind); + debug!( + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ + BasicBlock {:?}, for unclaimed edge counter {}", + edge_from_bcb, + from_bb, + target_bcb, + to_bb, + new_bb, + self.format_counter(&counter_kind), + ); + new_bb + } else { + let target_bb = self.bcb_last_bb(target_bcb); + graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind); + debug!( + "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", + target_bcb, + target_bb, + self.format_counter(&counter_kind), + ); + target_bb + }; + + inject_statement(self.mir_body, counter_kind, inject_to_bb, None); + } + CoverageKind::Expression { .. } => { + inject_intermediate_expression(self.mir_body, counter_kind) + } + _ => bug!("CoverageKind should be a counter"), + } + } + } + + #[inline] + fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).leader_bb() + } + + #[inline] + fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).last_bb() + } + + #[inline] + fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { + self.bcb_data(bcb).terminator(self.mir_body) + } + + #[inline] + fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.basic_coverage_blocks[bcb] + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) + } +} + +fn inject_edge_counter_basic_block( + mir_body: &mut mir::Body<'tcx>, + from_bb: BasicBlock, + to_bb: BasicBlock, +) -> BasicBlock { + let span = mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + let edge_ref = mir_body[from_bb] + .terminator_mut() + .successors_mut() + .find(|successor| **successor == to_bb) + .expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + new_bb +} + +fn inject_statement( + mir_body: &mut mir::Body<'tcx>, + counter_kind: CoverageKind, + bb: BasicBlock, + some_code_region: Option, +) { + debug!( + " injecting statement {:?} for {:?} at code region: {:?}", + counter_kind, bb, some_code_region + ); + let data = &mut mir_body[bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { + kind: counter_kind, + code_region: some_code_region, + }), + }; + data.statements.push(statement); +} + +// Non-code expressions are injected into the coverage map, without generating executable code. +fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) { + debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + debug!(" injecting non-code expression {:?}", expression); + let inject_in_bb = mir::START_BLOCK; + let data = &mut mir_body[inject_in_bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), + }; + data.statements.push(statement); +} + +/// Convert the Span into its file name, start line and column, and end line and column +fn make_code_region( + file_name: Symbol, + source_file: &Lrc, + span: Span, + body_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); + // Extend an empty span by one character so the region will be counted. + let CharPos(char_pos) = start_col; + if span.hi() == body_span.hi() { + 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>, +) -> Fingerprint { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(hcx, &mut stable_hasher); + stable_hasher.finish() +} diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs new file mode 100644 index 0000000000000..e86bb96d29c30 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -0,0 +1,125 @@ +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Coverage, CoverageInfo, Location}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; + +/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each +/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). +pub(crate) fn provide(providers: &mut Providers) { + providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); +} + +/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in +/// other words, the number of counter value references injected into the MIR (plus 1 for the +/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected +/// counters have a counter ID from `1..num_counters-1`. +/// +/// `num_expressions` is the number of counter expressions added to the MIR body. +/// +/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend +/// code generate, to lookup counters and expressions by simple u32 indexes. +/// +/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code +/// including injected counters. (It is OK if some counters are optimized out, but those counters +/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the +/// calls may not work; but computing the number of counters or expressions by adding `1` to the +/// highest ID (for a given instrumented function) is valid. +/// +/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum +/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a +/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression +/// IDs referenced by expression operands, if not already seen. +/// +/// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage` +/// statement for the `Counter` or `Expression` with the referenced ID. but since current or future +/// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to +/// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs. +struct CoverageVisitor { + info: CoverageInfo, + add_missing_operands: bool, +} + +impl CoverageVisitor { + /// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the + /// final computed number of counters should be the number of all `CoverageKind::Counter` + /// statements in the MIR *plus one* for the implicit `ZERO` counter. + #[inline(always)] + fn update_num_counters(&mut self, counter_id: u32) { + self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + } + + /// Computes an expression index for each expression ID, and updates `num_expressions` to the + /// maximum encountered index plus 1. + #[inline(always)] + fn update_num_expressions(&mut self, expression_id: u32) { + let expression_index = u32::MAX - expression_id; + self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); + } + + fn update_from_expression_operand(&mut self, operand_id: u32) { + if operand_id >= self.info.num_counters { + let operand_as_expression_index = u32::MAX - operand_id; + if operand_as_expression_index >= self.info.num_expressions { + // The operand ID is outside the known range of counter IDs and also outside the + // known range of expression IDs. In either case, the result of a missing operand + // (if and when used in an expression) will be zero, so from a computation + // perspective, it doesn't matter whether it is interepretted as a counter or an + // expression. + // + // However, the `num_counters` and `num_expressions` query results are used to + // allocate arrays when generating the coverage map (during codegen), so choose + // the type that grows either `num_counters` or `num_expressions` the least. + if operand_id - self.info.num_counters + < operand_as_expression_index - self.info.num_expressions + { + self.update_num_counters(operand_id) + } else { + self.update_num_expressions(operand_id) + } + } + } + } +} + +impl Visitor<'_> for CoverageVisitor { + fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { + if self.add_missing_operands { + match coverage.kind { + CoverageKind::Expression { lhs, rhs, .. } => { + self.update_from_expression_operand(u32::from(lhs)); + self.update_from_expression_operand(u32::from(rhs)); + } + _ => {} + } + } else { + match coverage.kind { + CoverageKind::Counter { id, .. } => { + self.update_num_counters(u32::from(id)); + } + CoverageKind::Expression { id, .. } => { + self.update_num_expressions(u32::from(id)); + } + _ => {} + } + } + } +} + +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 { + // num_counters always has at least the `ZERO` counter. + info: CoverageInfo { num_counters: 1, num_expressions: 0 }, + add_missing_operands: false, + }; + + coverage_visitor.visit_body(mir_body); + + coverage_visitor.add_missing_operands = true; + coverage_visitor.visit_body(mir_body); + + coverage_visitor.info +} diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs new file mode 100644 index 0000000000000..cda4fc125442f --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -0,0 +1,753 @@ +use super::debug::term_type; +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; + +use crate::util::spanview::source_range_no_file; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{ + self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; + +use rustc_span::source_map::original_sp; +use rustc_span::{BytePos, Span, SyntaxContext}; + +use std::cmp::Ordering; + +#[derive(Debug, Copy, Clone)] +pub(crate) 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[bb].statements[stmt_index]; + format!( + "{}: @{}[{}]: {:?}", + source_range_no_file(tcx, &span), + bb.index(), + stmt_index, + stmt + ) + } + Self::Terminator(bb, span) => { + let term = mir_body[bb].terminator(); + format!( + "{}: @{}.{}: {:?}", + 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, + } + } +} + +/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that +/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. +/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent +/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the +/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. +/// +/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that +/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches +/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` +/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. +#[derive(Debug, Clone)] +pub(crate) struct CoverageSpan { + pub span: Span, + pub bcb: BasicCoverageBlock, + pub coverage_statements: Vec, + pub 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, + coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], + is_closure, + } + } + + pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { + Self { + span, + bcb, + 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()); + } + } + + #[inline] + pub fn is_mergeable(&self, other: &Self) -> bool { + self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) + } + + #[inline] + pub fn is_in_same_bcb(&self, other: &Self) -> bool { + self.bcb == other.bcb + } + + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + format!( + "{}\n {}", + source_range_no_file(tcx, &self.span), + self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), + ) + } + + pub fn format_coverage_statements( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &'a mir::Body<'tcx>, + ) -> String { + let mut sorted_coverage_statements = self.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), + }); + sorted_coverage_statements + .iter() + .map(|covstmt| covstmt.format(tcx, mir_body)) + .collect::>() + .join("\n") + } +} + +/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a +/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: +/// +/// * Remove duplicate source code coverage regions +/// * Merge spans that represent continuous (both in source code and control flow), non-branching +/// execution +/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) +pub struct CoverageSpans<'a, 'tcx> { + /// The MIR, used to look up `BasicBlockData`. + mir_body: &'a mir::Body<'tcx>, + + /// A `Span` covering the function body of the MIR (typically from left curly brace to right + /// curly brace). + body_span: Span, + + /// The BasicCoverageBlock Control Flow Graph (BCB CFG). + basic_coverage_blocks: &'a CoverageGraph, + + /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative + /// dominance between the `BasicCoverageBlock`s of equal `Span`s. + sorted_spans_iter: Option>, + + /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the + /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to + /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next + /// iteration. + some_curr: Option, + + /// The original `span` for `curr`, in case the `curr` span is modified. + curr_original_span: Span, + + /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. + /// If that `curr` was discarded, `prev` retains its value from the previous iteration. + some_prev: Option, + + /// Assigned from `curr_original_span` from the previous iteration. + prev_original_span: Span, + + /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and + /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. + /// If a new `curr` span also fits this criteria (compared to an existing list of + /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to + /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` + /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` + /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a + /// `prev` with a matching `Span`) + pending_dups: Vec, + + /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` + /// will also be injected into the MIR for each `CoverageSpan`. + refined_spans: Vec, +} + +impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { + pub(crate) fn generate_coverage_spans( + mir_body: &'a mir::Body<'tcx>, + body_span: Span, + basic_coverage_blocks: &'a CoverageGraph, + ) -> Vec { + let mut coverage_spans = CoverageSpans { + mir_body, + body_span, + basic_coverage_blocks, + sorted_spans_iter: None, + refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), + some_curr: None, + curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + some_prev: None, + prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + pending_dups: Vec::new(), + }; + + let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans(); + + coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter()); + coverage_spans.some_prev = coverage_spans.sorted_spans_iter.as_mut().unwrap().next(); + coverage_spans.prev_original_span = + coverage_spans.some_prev.as_ref().expect("at least one span").span; + + coverage_spans.to_refined_spans() + } + + /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be + /// counted. + /// + /// The basic steps are: + /// + /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each + /// `BasicCoverageBlockData`. + /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position + /// are sorted with longer spans before shorter spans; and equal spans are sorted + /// (deterministically) based on "dominator" relationship (if any). + /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, + /// if another span or spans are already counting the same code region), or should be merged + /// into a broader combined span (because it represents a contiguous, non-branching, and + /// uninterrupted region of source code). + /// + /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since + /// closures have their own MIR, their `Span` in their enclosing function should be left + /// "uncovered". + /// + /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need + /// to be). + fn mir_to_initial_sorted_coverage_spans(&self) -> Vec { + let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); + for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { + for coverage_span in self.bcb_to_initial_coverage_spans(bcb, bcb_data) { + initial_spans.push(coverage_span); + } + } + + if initial_spans.is_empty() { + // This can happen if, for example, the function is unreachable (contains only a + // `BasicBlock`(s) with an `Unreachable` terminator). + 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 { + // Sort equal spans by dominator relationship, in reverse order (so + // dominators always come after the dominated equal spans). When later + // comparing two spans in order, the first will either dominate the second, + // or they will have no dominator relationship. + self.basic_coverage_blocks.dominators().rank_partial_cmp(b.bcb, a.bcb) + } + } else { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This guarantees that, if a `prev` span overlaps, and is not equal to, a + // `curr` span, the prev span either extends further left of the curr span, or + // they start at the same position and the prev span extends further right of + // the end of the curr span. + b.span.hi().partial_cmp(&a.span.hi()) + } + } else { + a.span.lo().partial_cmp(&b.span.lo()) + } + .unwrap() + }); + + initial_spans + } + + /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and + /// de-duplicated `CoverageSpan`s. + fn to_refined_spans(mut self) -> Vec { + 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); + // Note that curr.span may now differ from curr_original_span + } 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.refined_spans.push(prev); + } else if self.prev().is_closure { + // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the + // next iter + 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 { + // Note that this compares the new span to `prev_original_span`, which may not + // be the full `prev.span` (if merged during the previous iteration). + self.hold_pending_dups_unless_dominated(); + } else { + self.cutoff_prev_at_overlapping_curr(); + } + } + + debug!(" AT END, adding last prev={:?}", self.prev()); + let prev = self.take_prev(); + let CoverageSpans { + mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, .. + } = self; + for dup in pending_dups { + debug!(" ...adding at least one pending dup={:?}", dup); + refined_spans.push(dup); + } + refined_spans.push(prev); + + // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at + // least one other non-empty `CoverageSpan`. + let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + for covspan in &refined_spans { + if !covspan.span.is_empty() { + has_coverage.insert(covspan.bcb); + } + } + refined_spans.retain(|covspan| { + !(covspan.span.is_empty() + && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) + && has_coverage.contains(covspan.bcb)) + }); + + // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + // regions for the current function leave room for the closure's own coverage regions + // (injected separately, from the closure's own MIR). + refined_spans.retain(|covspan| !covspan.is_closure); + refined_spans + } + + // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of + // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated + // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will + // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple + // `Statement`s and/or `Terminator`s.) + fn bcb_to_initial_coverage_spans( + &self, + bcb: BasicCoverageBlock, + bcb_data: &'a BasicCoverageBlockData, + ) -> Vec { + bcb_data + .basic_blocks + .iter() + .flat_map(|&bb| { + let data = &self.mir_body[bb]; + data.statements + .iter() + .enumerate() + .filter_map(move |(index, statement)| { + filtered_statement_span(statement, self.body_span).map(|span| { + CoverageSpan::for_statement(statement, span, bcb, bb, index) + }) + }) + .chain( + filtered_terminator_span(data.terminator(), self.body_span) + .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), + ) + }) + .collect() + } + + 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")) + } + + /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the + /// `pending_dups` spans), then one of the following two things happened during the previous + /// iteration: + /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups + /// (in which case there should be at least two spans in `pending_dups`); or + /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case + /// `pending_dups` could have as few as one span) + /// In either case, no more spans will match the span of `pending_dups`, so + /// add the `pending_dups` if they don't overlap `curr`, and clear the list. + 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.refined_spans.push(dup); + } + } else { + self.pending_dups.clear(); + } + } + } + } + + /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. + 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.as_mut().unwrap().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 { + // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed + // by `self.curr_mut().merge_from(prev)`. + self.curr_original_span = curr.span; + self.some_curr.replace(curr); + self.check_pending_dups(); + return true; + } + } + false + } + + /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the + /// `curr` coverage span. + fn discard_curr(&mut self) { + self.some_curr = None; + } + + /// Returns true if the curr span should be skipped because prev has already advanced beyond the + /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region + /// of code, such as skipping past a closure. + fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { + self.prev().span.lo() > next_curr.span.lo() + } + + /// Returns true if the curr span starts past the end of the prev span, which means they don't + /// overlap, so we now know the prev can be added to the refined coverage spans. + fn prev_ends_before_curr(&self) -> bool { + self.prev().span.hi() <= self.curr().span.lo() + } + + /// If `prev`s span extends left of the closure (`curr`), carve out the closure's + /// span from `prev`'s span. (The closure's coverage counters will be injected when + /// processing the closure's own MIR.) Add the portion of the span to the left of the + /// closure; and if the span extends to the right of the closure, update `prev` to + /// that portion of the span. For any `pending_dups`, repeat the same process. + 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.refined_spans.push(dup); + } + } + self.refined_spans.push(pre_closure); + } + if has_post_closure_span { + // Update prev.span to start after the closure (and discard curr) + 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(); // since self.prev() was already updated + } else { + pending_dups.clear(); + } + } + + /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all + /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that + /// is less than (further left of) `prev_original_span.lo()`. + /// + /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if + /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, + /// until their disposition is determined. In this latter case, the `prev` dup is moved into + /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. + fn hold_pending_dups_unless_dominated(&mut self) { + // Equal coverage spans are ordered by dominators before dominated (if any), so it should be + // impossible for `curr` to dominate any previous `CoverageSpan`. + debug_assert!(!self.span_bcb_is_dominated_by(self.prev(), self.curr())); + + let initial_pending_count = self.pending_dups.len(); + if initial_pending_count > 0 { + let mut pending_dups = self.pending_dups.split_off(0); + pending_dups.retain(|dup| !self.span_bcb_is_dominated_by(self.curr(), dup)); + self.pending_dups.append(&mut pending_dups); + if self.pending_dups.len() < initial_pending_count { + debug!( + " discarded {} of {} pending_dups that dominated curr", + initial_pending_count - self.pending_dups.len(), + initial_pending_count + ); + } + } + + if self.span_bcb_is_dominated_by(self.curr(), self.prev()) { + debug!( + " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", + self.prev() + ); + self.cutoff_prev_at_overlapping_curr(); + // If one span dominates the other, assocate the span with the code from the dominated + // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note + // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still + // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) + // + // For example: + // match somenum { + // x if x < 1 => { ... } + // }... + // + // The span for the first `x` is referenced by both the pattern block (every time it is + // evaluated) and the arm code (only when matched). The counter will be applied only to + // the dominated block. This allows coverage to track and highlight things like the + // assignment of `x` above, if the branch is matched, making `x` available to the arm + // code; and to track and highlight the question mark `?` "try" operator at the end of + // a function call returning a `Result`, so the `?` is covered when the function returns + // an `Err`, and not counted as covered if the function always returns `Ok`. + } else { + // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) + // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as + // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. + 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); + } + } + + /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ + /// statements that end before `curr.lo()` (if any), and add the portion of the + /// combined span for those statements. Any other statements have overlapping spans + /// that can be ignored because `curr` and/or other upcoming statements/spans inside + /// the overlap area will produce their own counters. This disambiguation process + /// avoids injecting multiple counters for overlapping spans, and the potential for + /// double-counting. + 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; prev={:?}", + 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.refined_spans.push(prev); + } + } else { + // with `pending_dups`, `prev` cannot have any statements that don't overlap + self.pending_dups.clear(); + } + } + + fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool { + self.basic_coverage_blocks.is_dominated_by(covspan.bcb, dom_covspan.bcb) + } +} + +fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { + match statement.kind { + // These statements have spans that are often outside the scope of the executed source code + // for their parent `BasicBlock`. + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + // Coverage should not be encountered, but don't inject coverage coverage + | StatementKind::Coverage(_) + // Ignore `Nop`s + | StatementKind::Nop => None, + + // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` + // statements be more consistent? + // + // FakeReadCause::ForGuardBinding, in this example: + // match somenum { + // x if x < 1 => { ... } + // }... + // The BasicBlock within the match arm code included one of these statements, but the span + // for it covered the `1` in this source. The actual statements have nothing to do with that + // source span: + // FakeRead(ForGuardBinding, _4); + // where `_4` is: + // _4 = &_1; (at the span for the first `x`) + // and `_1` is the `Place` for `somenum`. + // + // If and when the Issue is resolved, remove this special case match pattern: + StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, + + // Retain spans from all other statements + StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` + | StatementKind::Assign(_) + | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm(_) + | StatementKind::Retag(_, _) + | StatementKind::AscribeUserType(_, _) => { + Some(function_source_span(statement.source_info.span, body_span)) + } + } +} + +fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { + match terminator.kind { + // These terminators have spans that don't positively contribute to computing a reasonable + // span of actually executed source code. (For example, SwitchInt terminators extracted from + // an `if condition { block }` has a span that includes the executed block, if true, + // but for coverage, the code region executed, up to *and* through the SwitchInt, + // actually stops before the if's block.) + TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG + | TerminatorKind::Assert { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::SwitchInt { .. } + // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. + // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons + // described below. Add tests to confirm whether or not similar cases also apply to + // `FalseEdge`. + | TerminatorKind::FalseEdge { .. } => None, + + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special cases? + // + // `Goto`s are often the targets of `SwitchInt` branches, and certain important + // optimizations to replace some `Counter`s with `Expression`s require a separate + // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed. + // + // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s, + // provided useful context for coverage, such as to count and show when `if` blocks + // _without_ `else` blocks execute the `false` case (counting when the body of the `if` + // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan` + // of 1 character, at the end of it's original `Span`. + // + // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` + // block must still be counted (for example, to contribute its count to an `Expression` + // that reports the execution count for some other block). In these cases, the code region + // is set to `None`. (See `Instrumentor::is_code_region_redundant()`.) + TerminatorKind::Goto { .. } => { + Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) + } + + // Retain spans from all other terminators + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Call { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + Some(function_source_span(terminator.source_info.span, body_span)) + } + } +} + +#[inline] +fn function_source_span(span: Span, body_span: Span) -> Span { + let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); + if body_span.contains(span) { span } else { body_span } +} + +#[inline(always)] +fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool { + match term_kind { + TerminatorKind::Goto { .. } => true, + _ => false, + } +} diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs deleted file mode 100644 index 6824c73ab60a0..0000000000000 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ /dev/null @@ -1,1271 +0,0 @@ -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 = ","; - -/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected -/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen -/// to construct the coverage map. -pub struct InstrumentCoverage; - -/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each -/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). -pub(crate) fn provide(providers: &mut Providers) { - providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); -} - -/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in -/// other words, the number of counter value references injected into the MIR (plus 1 for the -/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected -/// counters have a counter ID from `1..num_counters-1`. -/// -/// `num_expressions` is the number of counter expressions added to the MIR body. -/// -/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend -/// code generate, to lookup counters and expressions by simple u32 indexes. -/// -/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code -/// including injected counters. (It is OK if some counters are optimized out, but those counters -/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the -/// calls may not work; but computing the number of counters or expressions by adding `1` to the -/// highest ID (for a given instrumented function) is valid. -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 the InstrumentCoverage pass is called on promoted MIRs, skip them. - // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - 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(); - - // Only instrument functions, methods, and closures (not constants since they are evaluated - // at compile time by Miri). - // FIXME(#73156): Handle source code coverage in const eval - if !is_fn_like { - trace!( - "InstrumentCoverage skipped for {:?} (not an FnLikeNode)", - mir_body.source.def_id(), - ); - return; - } - // FIXME(richkadel): By comparison, the MIR pass `ConstProp` includes associated constants, - // with functions, methods, and closures. I assume Miri is used for associated constants as - // well. If not, we may need to include them here too. - - 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()); - } -} - -/// A BasicCoverageBlock (BCB) represents the maximal-length sequence of CFG (MIR) BasicBlocks -/// without conditional branches. -/// -/// The BCB allows coverage analysis to be performed on a simplified projection of the underlying -/// MIR CFG, without altering the original CFG. Note that running the MIR `SimplifyCfg` transform, -/// is not sufficient, and therefore not necessary, since the BCB-based CFG projection is a more -/// aggressive simplification. For example: -/// -/// * The BCB CFG projection ignores (trims) branches not relevant to coverage, such as unwind- -/// related code that is injected by the Rust compiler but has no physical source code to -/// count. This also means a BasicBlock with a `Call` terminator can be merged into its -/// primary successor target block, in the same BCB. -/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are -/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as -/// a `Goto`, and merged with its successor into the same BCB. -/// -/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. -/// In some cases, a BCB's execution count can be computed by `CounterExpression`. Additional -/// disjoint `CoverageSpan`s in a BCB can also be counted by `CounterExpression` (by adding `ZERO` -/// to the BCB's primary counter or expression). -/// -/// Dominator/dominated relationships (which are fundamental to the coverage analysis algorithm) -/// between two BCBs can be computed using the `mir::Body` `dominators()` with any `BasicBlock` -/// member of each BCB. (For consistency, BCB's use the first `BasicBlock`, also referred to as the -/// `bcb_leader_bb`.) -/// -/// The BCB CFG projection is critical to simplifying the coverage analysis by ensuring graph -/// path-based queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch -/// (control flow) significance. -#[derive(Debug, Clone)] -struct BasicCoverageBlock { - pub blocks: Vec, -} - -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::>() - .join(ID_SEPARATOR) - ) - } -} - -struct BasicCoverageBlocks { - vec: IndexVec>, -} - -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 { - self.vec.iter().filter_map(|option| option.as_ref()) - } - - fn extract_from_mir(&mut self, mir_body: &mir::Body<'tcx>) { - // Traverse the CFG but ignore anything following an `unwind` - let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { - let mut successors = term_kind.successors(); - match &term_kind { - // SwitchInt successors are never unwind, and all of them should be traversed. - - // NOTE: TerminatorKind::FalseEdge targets from SwitchInt don't appear to be - // helpful in identifying unreachable code. I did test the theory, but the following - // changes were not beneficial. (I assumed that replacing some constants with - // non-deterministic variables might effect which blocks were targeted by a - // `FalseEdge` `imaginary_target`. It did not.) - // - // Also note that, if there is a way to identify BasicBlocks that are part of the - // MIR CFG, but not actually reachable, here are some other things to consider: - // - // Injecting unreachable code regions will probably require computing the set - // difference between the basic blocks found without filtering out unreachable - // blocks, and the basic blocks found with the filter; then computing the - // `CoverageSpans` without the filter; and then injecting `Counter`s or - // `CounterExpression`s for blocks that are not unreachable, or injecting - // `Unreachable` code regions otherwise. This seems straightforward, but not - // trivial. - // - // Alternatively, we might instead want to leave the unreachable blocks in - // (bypass the filter here), and inject the counters. This will result in counter - // values of zero (0) for unreachable code (and, notably, the code will be displayed - // with a red background by `llvm-cov show`). - // - // TerminatorKind::SwitchInt { .. } => { - // let some_imaginary_target = successors.clone().find_map(|&successor| { - // let term = mir_body.basic_blocks()[successor].terminator(); - // if let TerminatorKind::FalseEdge { imaginary_target, .. } = term.kind { - // if mir_body.predecessors()[imaginary_target].len() == 1 { - // return Some(imaginary_target); - // } - // } - // None - // }); - // if let Some(imaginary_target) = some_imaginary_target { - // box successors.filter(move |&&successor| successor != imaginary_target) - // } else { - // box successors - // } - // } - // - // Note this also required changing the closure signature for the - // `ShortCurcuitPreorder` to: - // - // F: Fn(&'tcx TerminatorKind<'tcx>) -> Box + 'a>, - TerminatorKind::SwitchInt { .. } => successors, - - // For all other kinds, return only the first successor, if any, and ignore unwinds - _ => successors.next().into_iter().chain(&[]), - } - }); - - // Walk the CFG using a Preorder traversal, which starts from `START_BLOCK` and follows - // each block terminator's `successors()`. Coverage spans must map to actual source code, - // so compiler generated blocks and paths can be ignored. To that end the CFG traversal - // intentionally omits unwind paths. - 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) { - // The `bb` has more than one _incoming_ edge, and should start its own - // `BasicCoverageBlock`. (Note, the `blocks` vector does not yet include `bb`; - // it contains a sequence of one or more sequential blocks with no intermediate - // branches in or out. Save these as a new `BasicCoverageBlock` before starting - // the new one.) - 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 { .. } => { - // The `bb` has more than one _outgoing_ edge, or exits the function. Save the - // current sequence of `blocks` gathered to this point, as a new - // `BasicCoverageBlock`. - self.add_basic_coverage_block(blocks.split_off(0)); - debug!(" because term.kind = {:?}", term.kind); - // Note that this condition is based on `TerminatorKind`, even though it - // theoretically boils down to `successors().len() != 1`; that is, either zero - // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but - // since the Coverage graph (the BCB CFG projection) ignores things like unwind - // branches (which exist in the `Terminator`s `successors()` list) checking the - // number of successors won't work. - } - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} - } - } - - if !blocks.is_empty() { - // process any remaining blocks into a final `BasicCoverageBlock` - 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) { - let leader_bb = blocks[0]; - let bcb = BasicCoverageBlock { blocks }; - debug!("adding BCB: {:?}", bcb); - self.vec[leader_bb] = Some(bcb); - } -} - -impl std::ops::Index 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", - } -} - -/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that -/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. -/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent -/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the -/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. -/// -/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that -/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches -/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` -/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. -#[derive(Debug, Clone)] -struct CoverageSpan { - span: Span, - bcb_leader_bb: BasicBlock, - coverage_statements: Vec, - 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, - ) -> 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>, - basic_coverage_blocks: Option, - function_source_hash: Option, - 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, - } - } - - /// Counter IDs start from one and go up. - 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) - } - - /// Expression IDs start from u32::MAX and go down because a CounterExpression can reference - /// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter - /// expression operand IDs must be unique across both types. - 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 { - 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 - }; - - // Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a - // given BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` - // maps each `bcb_leader_bb` to its `Counter`, when injected. Subsequent `CoverageSpan`s - // for a BCB that already has a `Counter` will inject a `CounterExpression` instead, and - // compute its value by adding `ZERO` to the BCB `Counter` value. - 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, - 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); - } - - /// Converts the computed `BasicCoverageBlock`s into `SpanViewable`s. - fn span_viewables(&self, coverage_spans: &Vec) -> Vec { - 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::>() - .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 - } - - // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of - // the `BasicBlock`(s) in the given `BasicCoverageBlock`. One `CoverageSpan` is generated for - // each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will - // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple - // `Statement`s and/or `Terminator`s.) - fn extract_spans(&self, bcb: &'a BasicCoverageBlock) -> Vec { - 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() - } - - /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be - /// counted. - /// - /// The basic steps are: - /// - /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each - /// `BasicCoverageBlock`. - /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position - /// are sorted with longer spans before shorter spans; and equal spans are sorted - /// (deterministically) based on "dominator" relationship (if any). - /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, - /// if another span or spans are already counting the same code region), or should be merged - /// into a broader combined span (because it represents a contiguous, non-branching, and - /// uninterrupted region of source code). - /// - /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since - /// closures have their own MIR, their `Span` in their enclosing function should be left - /// "uncovered". - /// - /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need - /// to be). - fn coverage_spans(&self) -> Vec { - let mut initial_spans = - Vec::::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() { - // This can happen if, for example, the function is unreachable (contains only a - // `BasicBlock`(s) with an `Unreachable` terminator). - 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 { - // Sort equal spans by dominator relationship, in reverse order (so - // dominators always come after the dominated equal spans). When later - // comparing two spans in order, the first will either dominate the second, - // or they will have no dominator relationship. - self.dominators().rank_partial_cmp(b.bcb_leader_bb, a.bcb_leader_bb) - } - } else { - // Sort hi() in reverse order so shorter spans are attempted after longer spans. - // This guarantees that, if a `prev` span overlaps, and is not equal to, a `curr` - // span, the prev span either extends further left of the curr span, or they - // start at the same position and the prev span extends further right of the end - // of the curr span. - 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, - dominators: &'a Dominators, - some_curr: Option, - curr_original_span: Span, - some_prev: Option, - prev_original_span: Span, - pending_dups: Vec, - refined_spans: Vec, -} - -impl<'a> CoverageSpanRefinery<'a> { - fn from_sorted_spans( - sorted_spans: Vec, - dominators: &'a Dominators, - ) -> 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(), - } - } - - /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and - /// de-duplicated `CoverageSpan`s. - fn to_refined_spans(mut self) -> Vec { - 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); - // Note that curr.span may now differ from curr_original_span - } 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 { - // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the - // next iter - 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); - - // FIXME(richkadel): Replace some counters with expressions if they can be calculated based - // on branching. (For example, one branch of a SwitchInt can be computed from the counter - // for the CoverageSpan just prior to the SwitchInt minus the sum of the counters of all - // other branches). - - self.to_refined_spans_without_closures() - } - - fn add_refined_span(&mut self, coverage_span: CoverageSpan) { - self.refined_spans.push(coverage_span); - } - - /// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage - /// regions for the current function leave room for the closure's own coverage regions - /// (injected separately, from the closure's own MIR). - fn to_refined_spans_without_closures(mut self) -> Vec { - 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")) - } - - /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the - /// `pending_dups` spans), then one of the following two things happened during the previous - /// iteration: - /// * the `span` of prev was modified (by `curr_mut().merge_from(prev)`); or - /// * the `span` of prev advanced past the end of the span of pending_dups - /// (`prev().span.hi() <= curr().span.lo()`) - /// In either case, no more spans will match the span of `pending_dups`, so - /// add the `pending_dups` if they don't overlap `curr`, and clear the list. - 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(); - } - } - } - } - - /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. - 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 { - // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed - // by `self.curr_mut().merge_from(prev)`. - self.curr_original_span = curr.span; - self.some_curr.replace(curr); - self.check_pending_dups(); - return true; - } - } - false - } - - /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the - /// `curr` coverage span. - fn discard_curr(&mut self) { - self.some_curr = None; - } - - /// Returns true if the curr span should be skipped because prev has already advanced beyond the - /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region - /// of code, such as skipping past a closure. - fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { - self.prev().span.lo() > next_curr.span.lo() - } - - /// Returns true if the curr span starts past the end of the prev span, which means they don't - /// overlap, so we now know the prev can be added to the refined coverage spans. - fn prev_ends_before_curr(&self) -> bool { - self.prev().span.hi() <= self.curr().span.lo() - } - - /// If `prev`s span extends left of the closure (`curr`), carve out the closure's - /// span from `prev`'s span. (The closure's coverage counters will be injected when - /// processing the closure's own MIR.) Add the portion of the span to the left of the - /// closure; and if the span extends to the right of the closure, update `prev` to - /// that portion of the span. For any `pending_dups`, repeat the same process. - 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 { - // Update prev.span to start after the closure (and discard curr) - 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(); // since self.prev() was already updated - } else { - pending_dups.clear(); - } - } - - /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if - /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, - /// until their disposition is determined. In this latter case, the `prev` dup is moved into - /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. - fn hold_pending_dups_unless_dominated(&mut self) { - // equal coverage spans are ordered by dominators before dominated (if any) - debug_assert!(!self.prev().is_dominated_by(self.curr(), self.dominators)); - - if self.curr().is_dominated_by(&self.prev(), self.dominators) { - // If one span dominates the other, assocate the span with the dominator only. - // - // For example: - // match somenum { - // x if x < 1 => { ... } - // }... - // The span for the first `x` is referenced by both the pattern block (every - // time it is evaluated) and the arm code (only when matched). The counter - // will be applied only to the dominator block. - // - // The dominator's (`prev`) execution count may be higher than the dominated - // block's execution count, so drop `curr`. - 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 { - // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) - // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as - // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. - 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); - } - } - - /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ - /// statements that end before `curr.lo()` (if any), and add the portion of the - /// combined span for those statements. Any other statements have overlapping spans - /// that can be ignored because `curr` and/or other upcoming statements/spans inside - /// the overlap area will produce their own counters. This disambiguation process - /// avoids injecting multiple counters for overlapping spans, and the potential for - /// double-counting. - 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 { - // with `pending_dups`, `prev` cannot have any statements that don't overlap - self.pending_dups.clear(); - } - } -} - -fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { - match statement.kind { - // These statements have spans that are often outside the scope of the executed source code - // for their parent `BasicBlock`. - StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - // Coverage should not be encountered, but don't inject coverage coverage - | StatementKind::Coverage(_) - // Ignore `Nop`s - | StatementKind::Nop => None, - - // FIXME(richkadel): Look into a possible issue assigning the span to a - // FakeReadCause::ForGuardBinding, in this example: - // match somenum { - // x if x < 1 => { ... } - // }... - // The BasicBlock within the match arm code included one of these statements, but the span - // for it covered the `1` in this source. The actual statements have nothing to do with that - // source span: - // FakeRead(ForGuardBinding, _4); - // where `_4` is: - // _4 = &_1; (at the span for the first `x`) - // and `_1` is the `Place` for `somenum`. - // - // The arm code BasicBlock already has its own assignment for `x` itself, `_3 = 1`, and I've - // decided it's reasonable for that span (even though outside the arm code) to be part of - // the counted coverage of the arm code execution, but I can't justify including the literal - // `1` in the arm code. I'm pretty sure that, if the `FakeRead(ForGuardBinding)` has a - // purpose in codegen, it's probably in the right BasicBlock, but if so, the `Statement`s - // `source_info.span` can't be right. - // - // Consider correcting the span assignment, assuming there is a better solution, and see if - // the following pattern can be removed here: - StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, - - // Retain spans from all other statements - StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` - | 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 { - match terminator.kind { - // These terminators have spans that don't positively contribute to computing a reasonable - // span of actually executed source code. (For example, SwitchInt terminators extracted from - // an `if condition { block }` has a span that includes the executed block, if true, - // but for coverage, the code region executed, up to *and* through the SwitchInt, - // actually stops before the if's block.) - TerminatorKind::Unreachable // Unreachable blocks are not connected to the CFG - | TerminatorKind::Assert { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::Goto { .. } - // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. - | TerminatorKind::FalseEdge { .. } => None, - - // Retain spans from all other terminators - 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 } -} - -/// Convert the Span into its file name, start line and column, and end line and column -fn make_code_region(file_name: Symbol, source_file: &Lrc, 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); - // Extend an empty span by one character so the region will be counted. - 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>, -) -> 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, - worklist: Vec, - 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) { - let size = self.body.basic_blocks().len() - self.visited.count(); - (size, Some(size)) - } -} diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 89db6bb13cad3..e3fea2d2701e5 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -22,6 +22,7 @@ pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; pub mod const_prop; +pub mod coverage; pub mod deaggregator; pub mod dest_prop; pub mod dump_mir; @@ -31,7 +32,6 @@ pub mod function_item_references; pub mod generator; pub mod inline; pub mod instcombine; -pub mod instrument_coverage; pub mod match_branches; pub mod multiple_return_terminators; pub mod no_landing_pads; @@ -85,7 +85,7 @@ pub(crate) fn provide(providers: &mut Providers) { }, ..*providers }; - instrument_coverage::provide(providers); + coverage::query::provide(providers); } fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -306,7 +306,7 @@ fn mir_promoted( ]; let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage { - &[&instrument_coverage::InstrumentCoverage] + &[&coverage::InstrumentCoverage] } else { &[] }; diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs new file mode 100644 index 0000000000000..91499bb61c287 --- /dev/null +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -0,0 +1,185 @@ +use rustc_data_structures::graph::{self, iterate}; +use rustc_graphviz as dot; +use rustc_middle::ty::TyCtxt; +use std::io::{self, Write}; + +pub struct GraphvizWriter< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, +> { + graph: &'a G, + is_subgraph: bool, + graphviz_name: String, + graph_label: Option, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, +} + +impl< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, +> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> +{ + pub fn new( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { + Self { + graph, + is_subgraph: false, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn new_subgraph( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { + Self { + graph, + is_subgraph: true, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn set_graph_label(&mut self, graph_label: &str) { + self.graph_label = Some(graph_label.to_owned()); + } + + /// Write a graphviz DOT of the graph + pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> + where + W: Write, + { + let kind = if self.is_subgraph { "subgraph" } else { "digraph" }; + let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph + // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation, + // prepend "Mir_" to the graphviz_safe_def_name(def_id) + writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?; + + // Global graph properties + let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let mut graph_attrs = vec![&font[..]]; + let mut content_attrs = vec![&font[..]]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; + + // Graph label + if let Some(graph_label) = &self.graph_label { + self.write_graph_label(graph_label, w)?; + } + + // Nodes + for node in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_node(node, dark_mode, w)?; + } + + // Edges + for source in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_edges(source, w)?; + } + writeln!(w, "}}") + } + + /// Write a graphviz DOT node for the given node. + pub fn write_node(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()> + where + W: Write, + { + // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. + write!(w, r#" {} [shape="none", label=<"#, self.node(node))?; + + write!(w, r#""#)?; + + // FIXME(richkadel): Need generic way to know if node header should have a different color + // let (blk, bgcolor) = if data.is_cleanup { + // (format!("{:?} (cleanup)", node), "lightblue") + // } else { + // let color = if dark_mode { "dimgray" } else { "gray" }; + // (format!("{:?}", node), color) + // }; + let color = if dark_mode { "dimgray" } else { "gray" }; + let (blk, bgcolor) = (format!("{:?}", node), color); + write!( + w, + r#""#, + attrs = r#"align="center""#, + colspan = 1, + blk = blk, + bgcolor = bgcolor + )?; + + for section in (self.node_content_fn)(node) { + write!( + w, + r#""#, + dot::escape_html(§ion).replace("\n", "
") + )?; + } + + // Close the table + write!(w, "
{blk}
{}
")?; + + // Close the node label and the node itself. + writeln!(w, ">];") + } + + /// Write graphviz DOT edges with labels between the given node and all of its successors. + fn write_edges(&self, source: G::Node, w: &mut W) -> io::Result<()> + where + W: Write, + { + let edge_labels = (self.edge_labels_fn)(source); + for (index, target) in self.graph.successors(source).enumerate() { + let src = self.node(source); + let trg = self.node(target); + let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) { + dot::escape_html(edge_label).replace("\n", r#"
"#) + } else { + "".to_owned() + }; + writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?; + } + Ok(()) + } + + /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that + /// will appear below the graph. + fn write_graph_label(&self, label: &str, w: &mut W) -> io::Result<()> + where + W: Write, + { + let lines = label.split("\n").map(|s| dot::escape_html(s)).collect::>(); + let escaped_label = lines.join(r#"
"#); + writeln!(w, r#" label=<

{}



>;"#, escaped_label) + } + + fn node(&self, node: G::Node) -> String { + format!("{:?}__{}", node, self.graphviz_name) + } +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index e04f07774ed0b..625f1a3e6844e 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -62,6 +62,7 @@ where let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; if dark_mode { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } @@ -112,7 +113,8 @@ where // Basic block number at the top. let (blk, bgcolor) = if data.is_cleanup { - (format!("{} (cleanup)", block.index()), "lightblue") + let color = if dark_mode { "royalblue" } else { "lightblue" }; + (format!("{} (cleanup)", block.index()), color) } else { let color = if dark_mode { "dimgray" } else { "gray" }; (format!("{}", block.index()), color) diff --git a/compiler/rustc_mir/src/util/mod.rs b/compiler/rustc_mir/src/util/mod.rs index 7da2f4ffe0889..aaee0bc526db5 100644 --- a/compiler/rustc_mir/src/util/mod.rs +++ b/compiler/rustc_mir/src/util/mod.rs @@ -7,6 +7,7 @@ pub mod storage; mod alignment; pub mod collect_writes; mod find_self_call; +pub(crate) mod generic_graphviz; mod graphviz; pub(crate) mod pretty; pub(crate) mod spanview; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4c00361dd31df..ceed730e25b08 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -887,12 +887,15 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], "exclude the pass number when dumping MIR (used in tests) (default: no)"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + "in addition to `.mir` files, create graphviz `.dot` files (and with \ + `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived \ + coverage graph) (default: no)"), dump_mir_spanview: Option = (None, parse_mir_spanview, [UNTRACKED], "in addition to `.mir` files, create `.html` files to view spans for \ all `statement`s (including terminators), only `terminator` spans, or \ computed `block` spans (one span encompassing a block's terminator and \ - all statements)."), + all statements). If `-Z instrument-coverage` is also enabled, create \ + an additional `.html` file showing the computed coverage spans."), emit_future_incompat_report: bool = (false, parse_bool, [UNTRACKED], "emits a future-incompatibility report for lints (RFC 2834)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], diff --git a/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot new file mode 100644 index 0000000000000..efc06bdea57a6 --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot @@ -0,0 +1,6 @@ +digraph Cov_0_4 { + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; + bcb0__Cov_0_4 [shape="none", label=<
bcb0
Counter(bcb0) at 19:5-20:2
19:5-19:9: @0[0]: _0 = const true
20:2-20:2: @0.Return: return
bb0: Return
>]; +} diff --git a/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot new file mode 100644 index 0000000000000..5ddd112fe62e6 --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot @@ -0,0 +1,11 @@ +digraph Cov_0_3 { + graph [fontname="Courier, monospace"]; + node [fontname="Courier, monospace"]; + edge [fontname="Courier, monospace"]; + bcb2__Cov_0_3 [shape="none", label=<
bcb2
Expression(bcb0 - bcb1) at 14:6-14:6
14:6-14:6: @4.Goto: goto -> bb0
bb4: Goto
>]; + bcb1__Cov_0_3 [shape="none", label=<
bcb1
Counter(bcb1) at 12:13-12:18
12:13-12:18: @5[0]: _0 = const ()
Expression(bcb1 + 0) at 15:2-15:2
15:2-15:2: @5.Return: return
bb3: FalseEdge
bb5: Return
>]; + bcb0__Cov_0_3 [shape="none", label=<
bcb0
Counter(bcb0) at 11:12-11:17
11:12-11:17: @1.Call: _2 = bar() -> [return: bb2, unwind: bb6]
11:12-11:17: @2[0]: FakeRead(ForMatchedPlace, _2)
bb0: FalseUnwind
bb1: Call
bb2: SwitchInt
>]; + bcb2__Cov_0_3 -> bcb0__Cov_0_3 [label=<>]; + bcb0__Cov_0_3 -> bcb2__Cov_0_3 [label=]; + bcb0__Cov_0_3 -> bcb1__Cov_0_3 [label=]; +} diff --git a/src/test/mir-opt/coverage_graphviz.rs b/src/test/mir-opt/coverage_graphviz.rs new file mode 100644 index 0000000000000..b3c90c528377d --- /dev/null +++ b/src/test/mir-opt/coverage_graphviz.rs @@ -0,0 +1,20 @@ +// Test that `-Z instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file) +// rendering of the `BasicCoverageBlock` coverage control flow graph, with counters and +// expressions. + +// needs-profiler-support +// compile-flags: -Z instrument-coverage -Z dump-mir-graphviz +// EMIT_MIR coverage_graphviz.main.InstrumentCoverage.0.dot +// EMIT_MIR coverage_graphviz.bar.InstrumentCoverage.0.dot +fn main() { + loop { + if bar() { + break; + } + } +} + +#[inline(never)] +fn bar() -> bool { + true +} diff --git a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff index 598727e677ca2..c67d0e2ffe656 100644 --- a/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff +++ b/src/test/mir-opt/instrument_coverage.main.InstrumentCoverage.diff @@ -8,7 +8,6 @@ let mut _3: !; // in scope 0 at /the/src/instrument_coverage.rs:12:18: 14:10 bb0: { -+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:12:12 - 12:17; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 falseUnwind -> [real: bb1, cleanup: bb6]; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 } @@ -22,23 +21,26 @@ bb2: { FakeRead(ForMatchedPlace, _2); // scope 0 at /the/src/instrument_coverage.rs:12:12: 12:17 ++ Coverage::Counter(1) for /the/src/instrument_coverage.rs:12:12 - 12:17; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 switchInt(_2) -> [false: bb4, otherwise: bb3]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 } bb3: { -+ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 falseEdge -> [real: bb5, imaginary: bb4]; // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 } bb4: { _1 = const (); // scope 0 at /the/src/instrument_coverage.rs:12:9: 14:10 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 ++ Coverage::Expression(4294967295) = 1 - 2 for /the/src/instrument_coverage.rs:15:6 - 15:7; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 goto -> bb0; // scope 0 at /the/src/instrument_coverage.rs:11:5: 15:6 } bb5: { _0 = const (); // scope 0 at /the/src/instrument_coverage.rs:13:13: 13:18 StorageDead(_2); // scope 0 at /the/src/instrument_coverage.rs:15:5: 15:6 ++ Coverage::Counter(2) for /the/src/instrument_coverage.rs:13:13 - 13:18; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 ++ Coverage::Expression(4294967294) = 2 + 0 for /the/src/instrument_coverage.rs:16:1 - 16:2; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 return; // scope 0 at /the/src/instrument_coverage.rs:16:2: 16:2 } diff --git a/src/test/mir-opt/instrument_coverage.rs b/src/test/mir-opt/instrument_coverage.rs index d9c8e8d43a469..18863edac97e4 100644 --- a/src/test/mir-opt/instrument_coverage.rs +++ b/src/test/mir-opt/instrument_coverage.rs @@ -1,10 +1,10 @@ -// Test that the initial version of Rust coverage injects Coverage statements at the top of each -// function. The Coverage Counter statements are later converted into LLVM instrprof.increment -// intrinsics, during codegen. +// Test that `-Z instrument-coverage` injects Coverage statements. The Coverage Counter statements +// are later converted into LLVM instrprof.increment intrinsics, during codegen. // needs-profiler-support // ignore-windows -// compile-flags: -Zinstrument-coverage --remap-path-prefix={{src-base}}=/the/src +// compile-flags: -Z instrument-coverage --remap-path-prefix={{src-base}}=/the/src + // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff // EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff fn main() { diff --git a/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt b/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt index 0a3c4aedd5569..bd2a2475d9e10 100644 --- a/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt +++ b/src/test/run-make-fulldeps/coverage-llvmir-base/filecheck.testprog.txt @@ -34,7 +34,7 @@ CHECK-SAME: section "llvm.metadata" CHECK: [[DEFINE_INTERNAL]] { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} { CHECK-NEXT: start: -CHECK-NOT: bb{{[0-9]+}}: +CHECK-NOT: [[DEFINE_INTERNAL]] CHECK: %pgocount = load i64, i64* getelementptr inbounds CHECK-SAME: * @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/Makefile b/src/test/run-make-fulldeps/coverage-reports-base/Makefile index 880d7fdb1b040..b175768e19962 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/Makefile +++ b/src/test/run-make-fulldeps/coverage-reports-base/Makefile @@ -13,6 +13,25 @@ BASEDIR=../coverage-reports-base SOURCEDIR=../coverage +ifeq ($(UNAME),Darwin) +# FIXME(richkadel): It appears that --debug is not available on MacOS even when not running +# under CI. +NO_LLVM_ASSERTIONS=1 +endif + +# The `llvm-cov show` flag `--debug`, used to generate the `counters` output files, is only enabled +# if LLVM assertions are enabled. Some CI builds disable debug assertions. +ifndef NO_LLVM_ASSERTIONS +DEBUG_FLAG=--debug +endif + +# When generating `expected_*` results (using `x.py test --bless`), the `--debug` flag is forced. +# If assertions are disabled, the command will fail with an error, rather than attempt to generate +# only partial results. +ifdef RUSTC_BLESS_TEST +DEBUG_FLAG=--debug +endif + all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) # Ensure there are no `expected` results for tests that may have been removed or renamed @@ -21,6 +40,7 @@ clear_expected_if_blessed: ifdef RUSTC_BLESS_TEST rm -f expected_export_coverage.*.json rm -f expected_show_coverage.*.txt + rm -f expected_show_coverage_counters.*.txt endif -include clear_expected_if_blessed @@ -49,19 +69,33 @@ endif "$(TMPDIR)"/$@.profraw \ -o "$(TMPDIR)"/$@.profdata - # Generate a coverage report using `llvm-cov show`. The output ordering - # can be non-deterministic, so ignore the return status. If the test fails - # when comparing the JSON `export`, the `show` output may be useful when - # debugging. + # Generate a coverage report using `llvm-cov show`. "$(LLVM_BIN_DIR)"/llvm-cov show \ + $(DEBUG_FLAG) \ --Xdemangler="$(RUST_DEMANGLER)" \ --show-line-counts-or-regions \ --instr-profile="$(TMPDIR)"/$@.profdata \ $(call BIN,"$(TMPDIR)"/$@) \ - > "$(TMPDIR)"/actual_show_coverage.$@.txt + > "$(TMPDIR)"/actual_show_coverage.$@.txt \ + 2> "$(TMPDIR)"/show_coverage_stderr.$@.txt || \ + ( status=$$? ; \ + >&2 cat "$(TMPDIR)"/show_coverage_stderr.$@.txt ; \ + exit $$status \ + ) + +ifdef DEBUG_FLAG + # The first line (beginning with "Args:" contains hard-coded, build-specific + # file paths. Strip that line and keep the remaining lines with counter debug + # data. + tail -n +2 "$(TMPDIR)"/show_coverage_stderr.$@.txt \ + > "$(TMPDIR)"/actual_show_coverage_counters.$@.txt +endif ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/actual_show_coverage.$@.txt expected_show_coverage.$@.txt + cp "$(TMPDIR)"/actual_show_coverage.$@.txt \ + expected_show_coverage.$@.txt + cp "$(TMPDIR)"/actual_show_coverage_counters.$@.txt \ + expected_show_coverage_counters.$@.txt else # Compare the show coverage output (`--bless` refreshes `typical` files) # Note `llvm-cov show` output for some programs can vary, but can be ignored @@ -75,6 +109,21 @@ else false \ ) +ifdef DEBUG_FLAG + $(DIFF) expected_show_coverage_counters.$@.txt "$(TMPDIR)"/actual_show_coverage_counters.$@.txt || \ + ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \ + >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \ + ) || \ + ( >&2 echo 'diff failed, and not suppressed without `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs'; \ + >&2 echo '(Ignore anyway until mangled function names in "counters" files are demangled.)' \ + ) + + # FIXME(richkadel): Apply the demangler to the `*_show_coverage_counters.*.txt` files, + # so the crate disambiguator differences will be stripped away. At that point, these files + # will be less likely to vary, and the last `echo` above (starting with "Ignore anyway") + # can be replaced with `false` to fail the test. +endif + endif # Generate a coverage report in JSON, using `llvm-cov export`, and fail if diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json index 8c6edae280397..bff55300b3ca3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json @@ -17,14 +17,14 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } @@ -42,14 +42,14 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.conditions.json similarity index 76% rename from src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json rename to src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.conditions.json index 410821ea33592..ed937a1b13f38 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.conditions.json @@ -3,7 +3,7 @@ { "files": [ { - "filename": "../coverage/various_conditions.rs", + "filename": "../coverage/conditions.rs", "summary": { "functions": { "count": 1, @@ -21,10 +21,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 69, + "covered": 18, + "notcovered": 51, + "percent": 26.08695652173913 } } } @@ -46,10 +46,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 69, + "covered": 18, + "notcovered": 51, + "percent": 26.08695652173913 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json index 2ff53ad33fa99..84dcc251f3f4b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json index a24e6a33a3397..c178e7f93476f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json index 585346dc32a60..5a953b90b423f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 30, + "percent": 75 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 30, + "percent": 75 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_branches.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_branches.json new file mode 100644 index 0000000000000..2dca41df9d24e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_branches.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/loops_branches.rs", + "summary": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "totals": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json new file mode 100644 index 0000000000000..68163eb763619 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/nested_loops.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq.json new file mode 100644 index 0000000000000..a7b98247b6631 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/partial_eq.rs", + "summary": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json index 38bc96898ea36..ada6bb062dd1e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json index f9d91d66f1db2..63d1ae74c5f5d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 24, + "covered": 24, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 15, + "covered": 14, + "notcovered": 1, + "percent": 93.33333333333333 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 24, + "covered": 24, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 15, + "covered": 14, + "notcovered": 1, + "percent": 93.33333333333333 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_inf_loop.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_inf_loop.json new file mode 100644 index 0000000000000..872560384eb90 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_inf_loop.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/tight_inf_loop.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json index e6ef2c1ab899e..78b935b15689a 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 19, + "covered": 14, + "notcovered": 5, + "percent": 73.68421052631578 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 19, + "covered": 14, + "notcovered": 5, + "percent": 73.68421052631578 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json new file mode 100644 index 0000000000000..339c65556682a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/while.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_ret.json similarity index 81% rename from src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json rename to src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_ret.json index 865b705fa2007..ad43f5d992630 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_ret.json @@ -3,7 +3,7 @@ { "files": [ { - "filename": "../coverage/while_early_return.rs", + "filename": "../coverage/while_early_ret.rs", "summary": { "functions": { "count": 1, @@ -16,9 +16,9 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { "count": 9, @@ -41,9 +41,9 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { "count": 9, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt index 17054490e9b3c..aef26a62e25fb 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt @@ -62,7 +62,7 @@ 62| 1| let mut countdown = 0; 63| 1| if is_false { 64| 0| countdown = 10; - 65| 0| } + 65| 1| } 66| 1| "alt string 3".to_owned() 67| 1| } 68| 1| ) @@ -77,7 +77,7 @@ 77| 1| let mut countdown = 0; 78| 1| if is_false { 79| 0| countdown = 10; - 80| 0| } + 80| 1| } 81| 1| "alt string 4".to_owned() 82| 1| }; 83| 1| println!( diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.conditions.txt similarity index 100% rename from src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt rename to src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.conditions.txt diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt index bc2f9b108b2f3..85e6440ab3729 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt @@ -25,5 +25,6 @@ 25| 1| 10 26| 1| ; 27| 1| } + ^0 28| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt index b13ca83d018f9..4a51f842a4bb2 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt @@ -10,6 +10,7 @@ 10| 1| if is_true { 11| 1| countdown = 10; 12| 1| } + ^0 13| | 14| | mod in_mod { 15| | const IN_MOD_CONST: u32 = 1000; @@ -48,6 +49,7 @@ 48| 1| if is_true { 49| 1| in_func(countdown); 50| 1| } + ^0 51| | 52| 1| let mut val = InStruct { 53| 1| in_struct_field: 101, diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt index ded4369751587..1b503033911c5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt @@ -12,12 +12,14 @@ 12| 1| b = 10; 13| 1| c = 100; 14| 1| } + ^0 15| | let 16| 1| somebool 17| | = 18| 1| a < b 19| | || - 20| 0| b < c + 20| 1| b < c + ^0 21| | ; 22| | let 23| 1| somebool @@ -26,19 +28,38 @@ 26| | || 27| 1| b < c 28| | ; - 29| | let - 30| 1| somebool - 31| | = - 32| 1| a < b - 33| | && - 34| 1| b < c - 35| | ; - 36| | let - 37| 1| somebool - 38| | = - 39| 1| b < a - 40| | && - 41| 0| b < c - 42| | ; - 43| 1|} + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_branches.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_branches.txt new file mode 100644 index 0000000000000..3a969a6b89869 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_branches.txt @@ -0,0 +1,38 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 4| |// structure of this `fmt` function. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| | while true { + 13| | } + 14| 1| } + 15| 1| write!(f, "error")?; + ^0 + 16| | } else { + 17| 1| } + 18| 1| Ok(()) + 19| 1| } + 20| |} + 21| | + 22| 1|fn main() { + 23| 1| let debug_test = DebugTest; + 24| 1| println!("{:?}", debug_test); + 25| 1|} + 26| | + 27| |/* + 28| | + 29| |This is the error message generated, before the issue was fixed: + 30| | + 31| |error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: + 32| |Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): + 33| |Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: + 34| |[bcb6, bcb7, bcb9]" } + 35| | + 36| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt new file mode 100644 index 0000000000000..c9f373bf6a7c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt @@ -0,0 +1,26 @@ + 1| |fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| | } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| | } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| 2| } + 23| 0| countdown -= 1; + 24| 0| } + 25| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq.txt new file mode 100644 index 0000000000000..310bf13a695af --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq.txt @@ -0,0 +1,101 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 + 5| |pub struct Version { + 6| | major: usize, + 7| 1| minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + 47| | + 48| |// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces + 49| |// the following results: + 50| | + 51| |/* + 52| | + 53| |1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + 54| | + 55| |2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + 56| | line count with a different instance? Or was it really only called once? + 57| | + 58| |3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + 59| | its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + 60| | problem computing the file position here, for some reason. + 61| | + 62| | + 63| | 16| | + 64| | 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 65| | ^1 ^1 + 66| |------------------ + 67| ||Unexecuted instantiation: ::gt + 68| |------------------ + 69| ||Unexecuted instantiation: ::le + 70| |------------------ + 71| ||Unexecuted instantiation: ::ge + 72| |------------------ + 73| ||::lt: + 74| || 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 75| |------------------ + 76| | 18| |pub struct Version { + 77| | 19| | major: usize, + 78| | 20| 1| minor: usize, + 79| | 21| | patch: usize, + 80| | 22| |} + 81| | 23| | + 82| | 24| |impl Version { + 83| | 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 84| | 26| 2| Version { + 85| | 27| 2| major, + 86| | 28| 2| minor, + 87| | 29| 2| patch, + 88| | 30| 2| } + 89| | 31| 2| } + 90| | 32| |} + 91| | 33| | + 92| | 34| 1|fn main() { + 93| | 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 94| | 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 95| | 37| 1| + 96| | 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version + 97| |_3_3_0); + 98| | 39| 1|} + 99| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt index f1acb7c545940..064930dd75c93 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt @@ -16,6 +16,7 @@ 16| 1| 10 17| 1| ; 18| 1| } + ^0 19| | 20| | loop 21| | { @@ -31,6 +32,6 @@ 31| 10| -= 32| 10| 1 33| | ; - 34| | } + 34| 1| } 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt index e42f22cd047fc..1f7e71d3eb0e7 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt @@ -10,22 +10,24 @@ 10| 1| if is_true { 11| 1| countdown = 0; 12| 1| } + ^0 13| | - 14| 3| for - 15| 3| _ + 14| | for + 15| 2| _ 16| | in - 17| 1| 0..2 + 17| 3| 0..2 18| | { 19| | let z 20| | ; 21| | match 22| 2| countdown - 23| 2| { - 24| 2| x - 25| 2| if + 23| | { + 24| 1| x + 25| | if 26| 2| x 27| 2| < 28| 2| 1 + ^1 29| | => 30| 1| { 31| 1| z = countdown @@ -39,6 +41,6 @@ 39| | => 40| 1| {} 41| | } - 42| | } + 42| 3| } 43| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_inf_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_inf_loop.txt new file mode 100644 index 0000000000000..e02eac03a6b15 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_inf_loop.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| if false { + 3| | loop {} + 4| | } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt index ae288d7d7a000..6b3a8c39c6338 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt @@ -13,24 +13,26 @@ 13| 1| let mut 14| 1| countdown = 10 15| | ; - 16| 6| for + 16| | for 17| 6| _ 18| | in - 19| 1| 0..10 + 19| 6| 0..10 20| | { 21| 6| countdown 22| 6| -= 1 - 23| | ; - 24| | if + 23| 6| ; + 24| 6| if 25| 6| countdown < 5 26| | { 27| 1| call(/*return_error=*/ true)?; - 28| | } - 29| | else - 30| | { - 31| 5| call(/*return_error=*/ false)?; - 32| | } - 33| | } - 34| 0| Ok(()) - 35| 1|} + 28| 0| call(/*return_error=*/ false)?; + 29| | } + 30| | else + 31| | { + 32| 5| call(/*return_error=*/ false)?; + ^0 + 33| 5| } + 34| 5| } + 35| 0| Ok(()) + 36| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt new file mode 100644 index 0000000000000..194325b6b9eca --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_ret.txt similarity index 98% rename from src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt rename to src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_ret.txt index 7dce94f25f304..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_ret.txt @@ -3,7 +3,7 @@ 3| | 4| |fn main() -> Result<(),u8> { 5| 1| let mut countdown = 10; - 6| 7| while + 6| | while 7| 7| countdown 8| 7| > 9| 7| 0 diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt new file mode 100644 index 0000000000000..fb797796e4e7d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt @@ -0,0 +1,94 @@ +Counter in file 0 20:21 -> 20:38, #1 +Counter in file 0 21:20 -> 21:28, (#1 + 0) +Counter in file 0 21:29 -> 23:18, #2 +Counter in file 0 23:18 -> 23:19, (#1 - #2) +Counter in file 0 24:17 -> 25:14, (#2 + (#1 - #2)) +Counter in file 0 3:11 -> 18:13, #1 +Counter in file 0 25:14 -> 33:9, (#1 + 0) +Counter in file 0 40:6 -> 60:13, (#1 + 0) +Counter in file 0 67:14 -> 75:9, (#1 + 0) +Counter in file 0 82:6 -> 93:2, (#1 + 0) +Counter in file 0 77:13 -> 77:30, #1 +Counter in file 0 78:12 -> 78:20, (#1 + 0) +Counter in file 0 78:21 -> 80:10, #2 +Counter in file 0 80:10 -> 80:11, (#1 - #2) +Counter in file 0 81:9 -> 82:6, (#2 + (#1 - #2)) +Counter in file 0 62:21 -> 62:38, #1 +Counter in file 0 63:20 -> 63:28, (#1 + 0) +Counter in file 0 63:29 -> 65:18, #2 +Counter in file 0 65:18 -> 65:19, (#1 - #2) +Counter in file 0 66:17 -> 67:14, (#2 + (#1 - #2)) +Counter in file 0 35:13 -> 35:30, #1 +Counter in file 0 36:12 -> 36:20, (#1 + 0) +Counter in file 0 36:21 -> 38:10, #2 +Counter in file 0 38:10 -> 38:11, (#1 - #2) +Counter in file 0 39:9 -> 40:6, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/closure.rs +Combined regions: + 3:11 -> 18:13 (count=1) + 20:21 -> 20:38 (count=0) + 21:20 -> 21:28 (count=0) + 21:29 -> 23:18 (count=0) + 23:18 -> 23:19 (count=0) + 24:17 -> 25:14 (count=0) + 25:14 -> 33:9 (count=1) + 35:13 -> 35:30 (count=0) + 36:12 -> 36:20 (count=0) + 36:21 -> 38:10 (count=0) + 38:10 -> 38:11 (count=0) + 39:9 -> 40:6 (count=0) + 40:6 -> 60:13 (count=1) + 62:21 -> 62:38 (count=1) + 63:20 -> 63:28 (count=1) + 63:29 -> 65:18 (count=0) + 65:18 -> 65:19 (count=1) + 66:17 -> 67:14 (count=1) + 67:14 -> 75:9 (count=1) + 77:13 -> 77:30 (count=1) + 78:12 -> 78:20 (count=1) + 78:21 -> 80:10 (count=0) + 80:10 -> 80:11 (count=1) + 81:9 -> 82:6 (count=1) + 82:6 -> 93:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 18:13 (count = 0), Skipped +Segment at 20:21 (count = 0), RegionEntry +Segment at 20:38 (count = 0), Skipped +Segment at 21:20 (count = 0), RegionEntry +Segment at 21:28 (count = 0), Skipped +Segment at 21:29 (count = 0), RegionEntry +Segment at 23:18 (count = 0), RegionEntry +Segment at 23:19 (count = 0), Skipped +Segment at 24:17 (count = 0), RegionEntry +Segment at 25:14 (count = 1), RegionEntry +Segment at 33:9 (count = 0), Skipped +Segment at 35:13 (count = 0), RegionEntry +Segment at 35:30 (count = 0), Skipped +Segment at 36:12 (count = 0), RegionEntry +Segment at 36:20 (count = 0), Skipped +Segment at 36:21 (count = 0), RegionEntry +Segment at 38:10 (count = 0), RegionEntry +Segment at 38:11 (count = 0), Skipped +Segment at 39:9 (count = 0), RegionEntry +Segment at 40:6 (count = 1), RegionEntry +Segment at 60:13 (count = 0), Skipped +Segment at 62:21 (count = 1), RegionEntry +Segment at 62:38 (count = 0), Skipped +Segment at 63:20 (count = 1), RegionEntry +Segment at 63:28 (count = 0), Skipped +Segment at 63:29 (count = 0), RegionEntry +Segment at 65:18 (count = 1), RegionEntry +Segment at 65:19 (count = 0), Skipped +Segment at 66:17 (count = 1), RegionEntry +Segment at 67:14 (count = 1), RegionEntry +Segment at 75:9 (count = 0), Skipped +Segment at 77:13 (count = 1), RegionEntry +Segment at 77:30 (count = 0), Skipped +Segment at 78:12 (count = 1), RegionEntry +Segment at 78:20 (count = 0), Skipped +Segment at 78:21 (count = 0), RegionEntry +Segment at 80:10 (count = 1), RegionEntry +Segment at 80:11 (count = 0), Skipped +Segment at 81:9 (count = 1), RegionEntry +Segment at 82:6 (count = 1), RegionEntry +Segment at 93:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.conditions.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.conditions.txt new file mode 100644 index 0000000000000..d48cd8074bebb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.conditions.txt @@ -0,0 +1,238 @@ +Counter in file 0 4:9 -> 4:26, #1 +Counter in file 0 5:8 -> 5:12, (#1 + 0) +Counter in file 0 5:13 -> 7:6, #2 +Counter in file 0 10:9 -> 10:10, (#4 + #11) +Counter in file 0 10:16 -> 10:29, (#2 + 0) +Counter in file 0 11:9 -> 12:10, #4 +Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) +Counter in file 0 14:12 -> 14:25, #5 +Counter in file 0 14:29 -> 14:42, (#5 - #13) +Counter in file 0 14:42 -> 14:43, (#13 + #14) +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) +Counter in file 0 14:46 -> 14:60, #21 +Counter in file 0 14:60 -> 14:61, (#17 + #18) +Counter in file 0 14:60 -> 14:61, (#21 - #18) +Counter in file 0 14:61 -> 16:10, #22 +Counter in file 0 16:10 -> 16:11, #23 +Counter in file 0 17:9 -> 18:18, #11 +Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) +Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) +Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) +Counter in file 0 24:13 -> 26:6, #12 +Counter in file 0 28:8 -> 28:21, (#12 + 0) +Counter in file 0 29:9 -> 29:23, #16 +Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) +Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) +Counter in file 0 31:42 -> 31:43, (#24 + #25) +Counter in file 0 31:46 -> 31:60, #32 +Counter in file 0 31:60 -> 31:61, (#28 + #29) +Counter in file 0 31:60 -> 31:61, (#32 - #29) +Counter in file 0 31:61 -> 33:10, #33 +Counter in file 0 33:10 -> 33:11, #34 +Counter in file 0 34:9 -> 34:23, #19 +Counter in file 0 36:9 -> 36:15, #8 +Counter in file 0 39:9 -> 39:26, (#16 + #19) +Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) +Counter in file 0 40:13 -> 42:6, #20 +Counter in file 0 44:9 -> 44:10, (#27 + #30) +Counter in file 0 44:16 -> 44:29, (#20 + 0) +Counter in file 0 45:9 -> 45:23, #27 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) +Counter in file 0 47:42 -> 47:43, (#35 + #36) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) +Counter in file 0 47:46 -> 47:60, #41 +Counter in file 0 47:60 -> 47:61, (#37 + #38) +Counter in file 0 47:60 -> 47:61, (#41 - #38) +Counter in file 0 47:61 -> 49:10, #42 +Counter in file 0 49:10 -> 49:11, #43 +Counter in file 0 50:9 -> 50:23, #30 +Counter in file 0 52:13 -> 54:15, #7 +Counter in file 0 57:9 -> 57:10, (#9 + #10) +Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) +Counter in file 0 58:9 -> 58:23, #9 +Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) +Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) +Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) +Counter in file 0 60:42 -> 60:43, (#39 + #40) +Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) +Counter in file 0 60:46 -> 60:60, #46 +Counter in file 0 60:60 -> 60:61, (#46 - #45) +Counter in file 0 60:60 -> 60:61, (#44 + #45) +Counter in file 0 60:61 -> 62:10, #47 +Counter in file 0 62:10 -> 62:11, #48 +Counter in file 0 63:9 -> 63:23, #10 +Counter in file 0 65:9 -> 65:15, #6 +Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) +Emitting segments for file: ../coverage/conditions.rs +Combined regions: + 4:9 -> 4:26 (count=1) + 5:8 -> 5:12 (count=1) + 5:13 -> 7:6 (count=1) + 10:9 -> 10:10 (count=1) + 10:16 -> 10:29 (count=1) + 11:9 -> 12:10 (count=1) + 13:15 -> 13:28 (count=0) + 14:12 -> 14:25 (count=0) + 14:29 -> 14:42 (count=0) + 14:42 -> 14:43 (count=0) + 14:46 -> 14:60 (count=0) + 14:60 -> 14:61 (count=0) + 14:61 -> 16:10 (count=0) + 16:10 -> 16:11 (count=0) + 17:9 -> 18:18 (count=0) + 20:9 -> 20:15 (count=0) + 23:9 -> 23:26 (count=1) + 24:8 -> 24:12 (count=1) + 24:13 -> 26:6 (count=1) + 28:8 -> 28:21 (count=1) + 29:9 -> 29:23 (count=1) + 30:15 -> 30:28 (count=0) + 31:12 -> 31:25 (count=0) + 31:29 -> 31:42 (count=0) + 31:42 -> 31:43 (count=0) + 31:46 -> 31:60 (count=0) + 31:60 -> 31:61 (count=0) + 31:61 -> 33:10 (count=0) + 33:10 -> 33:11 (count=0) + 34:9 -> 34:23 (count=0) + 36:9 -> 36:15 (count=0) + 39:9 -> 39:26 (count=1) + 40:8 -> 40:12 (count=1) + 40:13 -> 42:6 (count=1) + 44:9 -> 44:10 (count=0) + 44:16 -> 44:29 (count=1) + 45:9 -> 45:23 (count=0) + 46:15 -> 46:28 (count=1) + 47:12 -> 47:25 (count=0) + 47:29 -> 47:42 (count=0) + 47:42 -> 47:43 (count=0) + 47:46 -> 47:60 (count=0) + 47:60 -> 47:61 (count=0) + 47:61 -> 49:10 (count=0) + 49:10 -> 49:11 (count=0) + 50:9 -> 50:23 (count=0) + 52:13 -> 54:15 (count=1) + 57:9 -> 57:10 (count=0) + 57:16 -> 57:29 (count=0) + 58:9 -> 58:23 (count=0) + 59:15 -> 59:28 (count=0) + 60:12 -> 60:25 (count=0) + 60:29 -> 60:42 (count=0) + 60:42 -> 60:43 (count=0) + 60:46 -> 60:60 (count=0) + 60:60 -> 60:61 (count=0) + 60:61 -> 62:10 (count=0) + 62:10 -> 62:11 (count=0) + 63:9 -> 63:23 (count=0) + 65:9 -> 65:15 (count=0) + 67:1 -> 67:2 (count=1) +Segment at 4:9 (count = 1), RegionEntry +Segment at 4:26 (count = 0), Skipped +Segment at 5:8 (count = 1), RegionEntry +Segment at 5:12 (count = 0), Skipped +Segment at 5:13 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 10:9 (count = 1), RegionEntry +Segment at 10:10 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 10:29 (count = 0), Skipped +Segment at 11:9 (count = 1), RegionEntry +Segment at 12:10 (count = 0), Skipped +Segment at 13:15 (count = 0), RegionEntry +Segment at 13:28 (count = 0), Skipped +Segment at 14:12 (count = 0), RegionEntry +Segment at 14:25 (count = 0), Skipped +Segment at 14:29 (count = 0), RegionEntry +Segment at 14:42 (count = 0), RegionEntry +Segment at 14:43 (count = 0), Skipped +Segment at 14:46 (count = 0), RegionEntry +Segment at 14:60 (count = 0), RegionEntry +Segment at 14:61 (count = 0), RegionEntry +Segment at 16:10 (count = 0), RegionEntry +Segment at 16:11 (count = 0), Skipped +Segment at 17:9 (count = 0), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 20:15 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:26 (count = 0), Skipped +Segment at 24:8 (count = 1), RegionEntry +Segment at 24:12 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 26:6 (count = 0), Skipped +Segment at 28:8 (count = 1), RegionEntry +Segment at 28:21 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:23 (count = 0), Skipped +Segment at 30:15 (count = 0), RegionEntry +Segment at 30:28 (count = 0), Skipped +Segment at 31:12 (count = 0), RegionEntry +Segment at 31:25 (count = 0), Skipped +Segment at 31:29 (count = 0), RegionEntry +Segment at 31:42 (count = 0), RegionEntry +Segment at 31:43 (count = 0), Skipped +Segment at 31:46 (count = 0), RegionEntry +Segment at 31:60 (count = 0), RegionEntry +Segment at 31:61 (count = 0), RegionEntry +Segment at 33:10 (count = 0), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:9 (count = 0), RegionEntry +Segment at 34:23 (count = 0), Skipped +Segment at 36:9 (count = 0), RegionEntry +Segment at 36:15 (count = 0), Skipped +Segment at 39:9 (count = 1), RegionEntry +Segment at 39:26 (count = 0), Skipped +Segment at 40:8 (count = 1), RegionEntry +Segment at 40:12 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 42:6 (count = 0), Skipped +Segment at 44:9 (count = 0), RegionEntry +Segment at 44:10 (count = 0), Skipped +Segment at 44:16 (count = 1), RegionEntry +Segment at 44:29 (count = 0), Skipped +Segment at 45:9 (count = 0), RegionEntry +Segment at 45:23 (count = 0), Skipped +Segment at 46:15 (count = 1), RegionEntry +Segment at 46:28 (count = 0), Skipped +Segment at 47:12 (count = 0), RegionEntry +Segment at 47:25 (count = 0), Skipped +Segment at 47:29 (count = 0), RegionEntry +Segment at 47:42 (count = 0), RegionEntry +Segment at 47:43 (count = 0), Skipped +Segment at 47:46 (count = 0), RegionEntry +Segment at 47:60 (count = 0), RegionEntry +Segment at 47:61 (count = 0), RegionEntry +Segment at 49:10 (count = 0), RegionEntry +Segment at 49:11 (count = 0), Skipped +Segment at 50:9 (count = 0), RegionEntry +Segment at 50:23 (count = 0), Skipped +Segment at 52:13 (count = 1), RegionEntry +Segment at 54:15 (count = 0), Skipped +Segment at 57:9 (count = 0), RegionEntry +Segment at 57:10 (count = 0), Skipped +Segment at 57:16 (count = 0), RegionEntry +Segment at 57:29 (count = 0), Skipped +Segment at 58:9 (count = 0), RegionEntry +Segment at 58:23 (count = 0), Skipped +Segment at 59:15 (count = 0), RegionEntry +Segment at 59:28 (count = 0), Skipped +Segment at 60:12 (count = 0), RegionEntry +Segment at 60:25 (count = 0), Skipped +Segment at 60:29 (count = 0), RegionEntry +Segment at 60:42 (count = 0), RegionEntry +Segment at 60:43 (count = 0), Skipped +Segment at 60:46 (count = 0), RegionEntry +Segment at 60:60 (count = 0), RegionEntry +Segment at 60:61 (count = 0), RegionEntry +Segment at 62:10 (count = 0), RegionEntry +Segment at 62:11 (count = 0), Skipped +Segment at 63:9 (count = 0), RegionEntry +Segment at 63:23 (count = 0), Skipped +Segment at 65:9 (count = 0), RegionEntry +Segment at 65:15 (count = 0), Skipped +Segment at 67:1 (count = 1), RegionEntry +Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt new file mode 100644 index 0000000000000..375025fe8bcc2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt @@ -0,0 +1,22 @@ +Counter in file 0 9:24 -> 11:6, #1 +Counter in file 0 15:9 -> 17:42, #1 +Counter in file 0 19:8 -> 19:12, (#1 + 0) +Counter in file 0 20:9 -> 21:22, #2 +Counter in file 0 27:1 -> 27:2, (#2 + 0) +Emitting segments for file: ../coverage/drop_trait.rs +Combined regions: + 9:24 -> 11:6 (count=2) + 15:9 -> 17:42 (count=1) + 19:8 -> 19:12 (count=1) + 20:9 -> 21:22 (count=1) + 27:1 -> 27:2 (count=1) +Segment at 9:24 (count = 2), RegionEntry +Segment at 11:6 (count = 0), Skipped +Segment at 15:9 (count = 1), RegionEntry +Segment at 17:42 (count = 0), Skipped +Segment at 19:8 (count = 1), RegionEntry +Segment at 19:12 (count = 0), Skipped +Segment at 20:9 (count = 1), RegionEntry +Segment at 21:22 (count = 0), Skipped +Segment at 27:1 (count = 1), RegionEntry +Segment at 27:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt new file mode 100644 index 0000000000000..013a69ed3983a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt @@ -0,0 +1,48 @@ +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 23:9 -> 28:28, #1 +Counter in file 0 30:8 -> 30:12, (#1 + 0) +Counter in file 0 31:9 -> 32:22, #2 +Counter in file 0 38:1 -> 38:2, (#2 + 0) +Counter in file 0 10:49 -> 12:6, #1 +Counter in file 0 10:49 -> 12:6, #1 +Emitting segments for file: ../coverage/generics.rs +Combined regions: + 10:49 -> 12:6 (count=3) + 17:24 -> 19:6 (count=2) + 23:9 -> 28:28 (count=1) + 30:8 -> 30:12 (count=1) + 31:9 -> 32:22 (count=1) + 38:1 -> 38:2 (count=1) +Segment at 10:49 (count = 3), RegionEntry +Segment at 12:6 (count = 0), Skipped +Segment at 17:24 (count = 2), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 28:28 (count = 0), Skipped +Segment at 30:8 (count = 1), RegionEntry +Segment at 30:12 (count = 0), Skipped +Segment at 31:9 (count = 1), RegionEntry +Segment at 32:22 (count = 0), Skipped +Segment at 38:1 (count = 1), RegionEntry +Segment at 38:2 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworkdE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=2) +Segment at 10:49 (count = 2), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworklE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=1) +Segment at 10:49 (count = 1), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworklENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworkdENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt new file mode 100644 index 0000000000000..c2bef365ea9d8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt @@ -0,0 +1,21 @@ +Counter in file 0 8:5 -> 18:10, #1 +Counter in file 0 21:9 -> 21:16, (#1 + 0) +Counter in file 0 22:5 -> 27:6, #2 +Counter in file 0 27:6 -> 27:7, (#1 - #2) +Counter in file 0 28:1 -> 28:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/if.rs +Combined regions: + 8:5 -> 18:10 (count=1) + 21:9 -> 21:16 (count=1) + 22:5 -> 27:6 (count=1) + 27:6 -> 27:7 (count=0) + 28:1 -> 28:2 (count=1) +Segment at 8:5 (count = 1), RegionEntry +Segment at 18:10 (count = 0), Skipped +Segment at 21:9 (count = 1), RegionEntry +Segment at 21:16 (count = 0), Skipped +Segment at 22:5 (count = 1), RegionEntry +Segment at 27:6 (count = 0), RegionEntry +Segment at 27:7 (count = 0), Skipped +Segment at 28:1 (count = 1), RegionEntry +Segment at 28:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt new file mode 100644 index 0000000000000..faf5c094bbaaa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt @@ -0,0 +1,30 @@ +Counter in file 0 7:9 -> 11:16, #1 +Counter in file 0 12:5 -> 17:6, #2 +Counter in file 0 20:9 -> 22:16, (#1 - #2) +Counter in file 0 26:9 -> 26:16, (#2 + (#1 - #2)) +Counter in file 0 27:5 -> 32:6, #3 +Counter in file 0 34:5 -> 39:6, ((#2 + (#1 - #2)) - #3) +Counter in file 0 40:1 -> 40:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Emitting segments for file: ../coverage/if_else.rs +Combined regions: + 7:9 -> 11:16 (count=1) + 12:5 -> 17:6 (count=1) + 20:9 -> 22:16 (count=0) + 26:9 -> 26:16 (count=1) + 27:5 -> 32:6 (count=1) + 34:5 -> 39:6 (count=0) + 40:1 -> 40:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 11:16 (count = 0), Skipped +Segment at 12:5 (count = 1), RegionEntry +Segment at 17:6 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 22:16 (count = 0), Skipped +Segment at 26:9 (count = 1), RegionEntry +Segment at 26:16 (count = 0), Skipped +Segment at 27:5 (count = 1), RegionEntry +Segment at 32:6 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 39:6 (count = 0), Skipped +Segment at 40:1 (count = 1), RegionEntry +Segment at 40:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt new file mode 100644 index 0000000000000..65cd6481af4cc --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt @@ -0,0 +1,60 @@ +Counter in file 0 19:13 -> 19:18, #1 +Counter in file 0 20:13 -> 20:14, #2 +Counter in file 0 20:17 -> 20:22, (#1 + 0) +Counter in file 0 21:9 -> 22:6, (#2 + 0) +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 48:8 -> 48:15, (#2 + (#1 - #2)) +Counter in file 0 48:16 -> 50:6, #3 +Counter in file 0 50:6 -> 50:7, ((#2 + (#1 - #2)) - #3) +Counter in file 0 52:9 -> 57:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Counter in file 0 33:42 -> 36:10, #1 +Counter in file 0 41:37 -> 41:41, #1 +Counter in file 0 42:13 -> 43:10, #2 +Emitting segments for file: ../coverage/inner_items.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 19:13 -> 19:18 (count=3) + 20:13 -> 20:14 (count=3) + 20:17 -> 20:22 (count=3) + 21:9 -> 22:6 (count=3) + 33:42 -> 36:10 (count=1) + 41:37 -> 41:41 (count=1) + 42:13 -> 43:10 (count=1) + 48:8 -> 48:15 (count=1) + 48:16 -> 50:6 (count=1) + 50:6 -> 50:7 (count=0) + 52:9 -> 57:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 19:13 (count = 3), RegionEntry +Segment at 19:18 (count = 0), Skipped +Segment at 20:13 (count = 3), RegionEntry +Segment at 20:14 (count = 0), Skipped +Segment at 20:17 (count = 3), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 21:9 (count = 3), RegionEntry +Segment at 22:6 (count = 0), Skipped +Segment at 33:42 (count = 1), RegionEntry +Segment at 36:10 (count = 0), Skipped +Segment at 41:37 (count = 1), RegionEntry +Segment at 41:41 (count = 0), Skipped +Segment at 42:13 (count = 1), RegionEntry +Segment at 43:10 (count = 0), Skipped +Segment at 48:8 (count = 1), RegionEntry +Segment at 48:15 (count = 0), Skipped +Segment at 48:16 (count = 1), RegionEntry +Segment at 50:6 (count = 0), RegionEntry +Segment at 50:7 (count = 0), Skipped +Segment at 52:9 (count = 1), RegionEntry +Segment at 57:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt new file mode 100644 index 0000000000000..8e56d79d9d2aa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt @@ -0,0 +1,131 @@ +Counter in file 0 7:9 -> 9:42, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 14:6, #2 +Counter in file 0 14:6 -> 14:7, (#1 - #2) +Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) +Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) +Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) +Counter in file 0 20:18 -> 20:19, (#3 + #4) +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) +Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) +Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) +Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) +Counter in file 0 27:18 -> 27:19, (#5 + #6) +Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) +Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) +Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) +Counter in file 0 29:29 -> 29:34, #7 +Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) +Counter in file 0 29:34 -> 29:35, (#7 - #8) +Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) +Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) +Counter in file 0 30:29 -> 30:34, #9 +Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) +Counter in file 0 30:34 -> 30:35, (#9 - #10) +Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) +Counter in file 0 35:5 -> 38:6, #11 +Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) +Counter in file 0 41:9 -> 41:16, (#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) +Counter in file 0 42:5 -> 45:6, #12 +Counter in file 0 47:5 -> 50:6, ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12) +Counter in file 0 52:8 -> 52:16, (#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) +Counter in file 0 52:17 -> 54:6, #13 +Counter in file 0 54:6 -> 54:7, ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13) +Counter in file 0 56:8 -> 56:15, (#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) +Counter in file 0 56:16 -> 58:6, #14 +Counter in file 0 58:12 -> 60:6, ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14) +Counter in file 0 61:1 -> 61:2, (#14 + ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14)) +Emitting segments for file: ../coverage/lazy_boolean.rs +Combined regions: + 7:9 -> 9:42 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 14:6 (count=1) + 14:6 -> 14:7 (count=0) + 16:9 -> 16:17 (count=1) + 18:13 -> 18:18 (count=1) + 20:13 -> 20:18 (count=0) + 20:18 -> 20:19 (count=1) + 23:9 -> 23:17 (count=1) + 25:13 -> 25:18 (count=1) + 27:13 -> 27:18 (count=1) + 27:18 -> 27:19 (count=1) + 29:9 -> 29:17 (count=1) + 29:20 -> 29:25 (count=1) + 29:29 -> 29:34 (count=1) + 29:34 -> 29:35 (count=1) + 30:9 -> 30:17 (count=1) + 30:20 -> 30:25 (count=1) + 30:29 -> 30:34 (count=0) + 30:34 -> 30:35 (count=1) + 33:9 -> 34:16 (count=1) + 35:5 -> 38:6 (count=0) + 38:6 -> 38:7 (count=1) + 41:9 -> 41:16 (count=1) + 42:5 -> 45:6 (count=1) + 47:5 -> 50:6 (count=0) + 52:8 -> 52:16 (count=1) + 52:17 -> 54:6 (count=0) + 54:6 -> 54:7 (count=1) + 56:8 -> 56:15 (count=1) + 56:16 -> 58:6 (count=1) + 58:12 -> 60:6 (count=0) + 61:1 -> 61:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:42 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 14:6 (count = 0), RegionEntry +Segment at 14:7 (count = 0), Skipped +Segment at 16:9 (count = 1), RegionEntry +Segment at 16:17 (count = 0), Skipped +Segment at 18:13 (count = 1), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:13 (count = 0), RegionEntry +Segment at 20:18 (count = 1), RegionEntry +Segment at 20:19 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:17 (count = 0), Skipped +Segment at 25:13 (count = 1), RegionEntry +Segment at 25:18 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 1), RegionEntry +Segment at 27:19 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:17 (count = 0), Skipped +Segment at 29:20 (count = 1), RegionEntry +Segment at 29:25 (count = 0), Skipped +Segment at 29:29 (count = 1), RegionEntry +Segment at 29:34 (count = 1), RegionEntry +Segment at 29:35 (count = 0), Skipped +Segment at 30:9 (count = 1), RegionEntry +Segment at 30:17 (count = 0), Skipped +Segment at 30:20 (count = 1), RegionEntry +Segment at 30:25 (count = 0), Skipped +Segment at 30:29 (count = 0), RegionEntry +Segment at 30:34 (count = 1), RegionEntry +Segment at 30:35 (count = 0), Skipped +Segment at 33:9 (count = 1), RegionEntry +Segment at 34:16 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 38:6 (count = 1), RegionEntry +Segment at 38:7 (count = 0), Skipped +Segment at 41:9 (count = 1), RegionEntry +Segment at 41:16 (count = 0), Skipped +Segment at 42:5 (count = 1), RegionEntry +Segment at 45:6 (count = 0), Skipped +Segment at 47:5 (count = 0), RegionEntry +Segment at 50:6 (count = 0), Skipped +Segment at 52:8 (count = 1), RegionEntry +Segment at 52:16 (count = 0), Skipped +Segment at 52:17 (count = 0), RegionEntry +Segment at 54:6 (count = 1), RegionEntry +Segment at 54:7 (count = 0), Skipped +Segment at 56:8 (count = 1), RegionEntry +Segment at 56:15 (count = 0), Skipped +Segment at 56:16 (count = 1), RegionEntry +Segment at 58:6 (count = 0), Skipped +Segment at 58:12 (count = 0), RegionEntry +Segment at 60:6 (count = 0), Skipped +Segment at 61:1 (count = 1), RegionEntry +Segment at 61:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt new file mode 100644 index 0000000000000..a6144b8072ace --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt @@ -0,0 +1,6 @@ +Counter in file 0 3:11 -> 13:2, #1 +Emitting segments for file: ../coverage/loop_break_value.rs +Combined regions: + 3:11 -> 13:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 13:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_branches.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_branches.txt new file mode 100644 index 0000000000000..d8af6998964cf --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_branches.txt @@ -0,0 +1,37 @@ +Counter in file 0 10:12 -> 10:16, #1 +Counter in file 0 11:16 -> 11:21, #2 +Counter in file 0 14:14 -> 14:15, (#2 - #5) +Counter in file 0 15:13 -> 15:31, (0 + (#2 - #5)) +Counter in file 0 15:31 -> 15:32, #4 +Counter in file 0 17:10 -> 17:11, #3 +Counter in file 0 18:9 -> 18:15, (#3 + 0) +Counter in file 0 19:5 -> 19:6, (#4 + (#3 + 0)) +Counter in file 0 22:11 -> 25:2, #1 +Emitting segments for file: ../coverage/loops_branches.rs +Combined regions: + 10:12 -> 10:16 (count=1) + 11:16 -> 11:21 (count=1) + 14:14 -> 14:15 (count=1) + 15:13 -> 15:31 (count=1) + 15:31 -> 15:32 (count=0) + 17:10 -> 17:11 (count=1) + 18:9 -> 18:15 (count=1) + 19:5 -> 19:6 (count=1) + 22:11 -> 25:2 (count=1) +Segment at 10:12 (count = 1), RegionEntry +Segment at 10:16 (count = 0), Skipped +Segment at 11:16 (count = 1), RegionEntry +Segment at 11:21 (count = 0), Skipped +Segment at 14:14 (count = 1), RegionEntry +Segment at 14:15 (count = 0), Skipped +Segment at 15:13 (count = 1), RegionEntry +Segment at 15:31 (count = 0), RegionEntry +Segment at 15:32 (count = 0), Skipped +Segment at 17:10 (count = 1), RegionEntry +Segment at 17:11 (count = 0), Skipped +Segment at 18:9 (count = 1), RegionEntry +Segment at 18:15 (count = 0), Skipped +Segment at 19:5 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 22:11 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt new file mode 100644 index 0000000000000..f503007353319 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt @@ -0,0 +1,73 @@ +Counter in file 0 2:9 -> 3:27, #1 +Counter in file 0 5:19 -> 5:32, (#1 + #2) +Counter in file 0 6:13 -> 7:24, ((#1 + #2) - #3) +Counter in file 0 8:13 -> 8:14, ((((#1 + #2) - #3) + (#5 + #6)) - #7) +Counter in file 0 8:18 -> 8:23, (((#1 + #2) - #3) + (#5 + #6)) +Counter in file 0 9:16 -> 9:22, (((((#1 + #2) - #3) + (#5 + #6)) - #7) + 0) +Counter in file 0 10:17 -> 10:22, #8 +Counter in file 0 12:13 -> 12:19, #9 +Counter in file 0 13:13 -> 13:19, #10 +Counter in file 0 14:16 -> 14:22, (#10 + 0) +Counter in file 0 15:17 -> 16:27, #11 +Counter in file 0 17:21 -> 17:33, #4 +Counter in file 0 19:21 -> 21:14, #5 +Counter in file 0 21:14 -> 21:15, #6 +Counter in file 0 22:10 -> 22:11, (#5 + #6) +Counter in file 0 23:9 -> 23:23, #2 +Counter in file 0 24:6 -> 24:7, #3 +Counter in file 0 25:1 -> 25:2, (#4 + #3) +Emitting segments for file: ../coverage/nested_loops.rs +Combined regions: + 2:9 -> 3:27 (count=1) + 5:19 -> 5:32 (count=1) + 6:13 -> 7:24 (count=1) + 8:13 -> 8:14 (count=3) + 8:18 -> 8:23 (count=3) + 9:16 -> 9:22 (count=3) + 10:17 -> 10:22 (count=0) + 12:13 -> 12:19 (count=3) + 13:13 -> 13:19 (count=3) + 14:16 -> 14:22 (count=3) + 15:17 -> 16:27 (count=1) + 17:21 -> 17:33 (count=1) + 19:21 -> 21:14 (count=0) + 21:14 -> 21:15 (count=2) + 22:10 -> 22:11 (count=2) + 23:9 -> 23:23 (count=0) + 24:6 -> 24:7 (count=0) + 25:1 -> 25:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 3:27 (count = 0), Skipped +Segment at 5:19 (count = 1), RegionEntry +Segment at 5:32 (count = 0), Skipped +Segment at 6:13 (count = 1), RegionEntry +Segment at 7:24 (count = 0), Skipped +Segment at 8:13 (count = 3), RegionEntry +Segment at 8:14 (count = 0), Skipped +Segment at 8:18 (count = 3), RegionEntry +Segment at 8:23 (count = 0), Skipped +Segment at 9:16 (count = 3), RegionEntry +Segment at 9:22 (count = 0), Skipped +Segment at 10:17 (count = 0), RegionEntry +Segment at 10:22 (count = 0), Skipped +Segment at 12:13 (count = 3), RegionEntry +Segment at 12:19 (count = 0), Skipped +Segment at 13:13 (count = 3), RegionEntry +Segment at 13:19 (count = 0), Skipped +Segment at 14:16 (count = 3), RegionEntry +Segment at 14:22 (count = 0), Skipped +Segment at 15:17 (count = 1), RegionEntry +Segment at 16:27 (count = 0), Skipped +Segment at 17:21 (count = 1), RegionEntry +Segment at 17:33 (count = 0), Skipped +Segment at 19:21 (count = 0), RegionEntry +Segment at 21:14 (count = 2), RegionEntry +Segment at 21:15 (count = 0), Skipped +Segment at 22:10 (count = 2), RegionEntry +Segment at 22:11 (count = 0), Skipped +Segment at 23:9 (count = 0), RegionEntry +Segment at 23:23 (count = 0), Skipped +Segment at 24:6 (count = 0), RegionEntry +Segment at 24:7 (count = 0), Skipped +Segment at 25:1 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq.txt new file mode 100644 index 0000000000000..cdef821c0aef2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq.txt @@ -0,0 +1,27 @@ +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 21:11 -> 26:2, #1 +Counter in file 0 4:17 -> 4:22, #1 +Counter in file 0 13:9 -> 18:6, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 8:5 -> 8:17, #1 +Emitting segments for file: ../coverage/partial_eq.rs +Combined regions: + 4:17 -> 4:22 (count=2) + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) + 7:5 -> 7:6 (count=1) + 13:9 -> 18:6 (count=2) + 21:11 -> 26:2 (count=1) +Segment at 4:17 (count = 2), RegionEntry +Segment at 4:22 (count = 0), Skipped +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped +Segment at 7:5 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 13:9 (count = 2), RegionEntry +Segment at 18:6 (count = 0), Skipped +Segment at 21:11 (count = 1), RegionEntry +Segment at 26:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt new file mode 100644 index 0000000000000..255173e5534d1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt @@ -0,0 +1,37 @@ +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 12:9 -> 12:16, (#1 + 0) +Counter in file 0 13:5 -> 18:6, #2 +Counter in file 0 18:6 -> 18:7, (#1 - #2) +Counter in file 0 23:13 -> 25:14, ((#2 + (#1 - #2)) + #3) +Counter in file 0 27:13 -> 27:18, #4 +Counter in file 0 30:9 -> 32:10, #3 +Counter in file 0 34:6 -> 34:7, (#2 + (#1 - #2)) +Counter in file 0 35:1 -> 35:2, (#4 + 0) +Emitting segments for file: ../coverage/simple_loop.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 12:9 -> 12:16 (count=1) + 13:5 -> 18:6 (count=1) + 18:6 -> 18:7 (count=0) + 23:13 -> 25:14 (count=11) + 27:13 -> 27:18 (count=1) + 30:9 -> 32:10 (count=10) + 34:6 -> 34:7 (count=1) + 35:1 -> 35:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 12:9 (count = 1), RegionEntry +Segment at 12:16 (count = 0), Skipped +Segment at 13:5 (count = 1), RegionEntry +Segment at 18:6 (count = 0), RegionEntry +Segment at 18:7 (count = 0), Skipped +Segment at 23:13 (count = 11), RegionEntry +Segment at 25:14 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 0), Skipped +Segment at 30:9 (count = 10), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 34:6 (count = 1), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:1 (count = 1), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt new file mode 100644 index 0000000000000..1682a379bc0ff --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt @@ -0,0 +1,57 @@ +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - #5) +Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 24:13 -> 24:14, #3 +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) - #3) +Counter in file 0 30:13 -> 37:14, (#3 + 0) +Counter in file 0 40:13 -> 40:15, #4 +Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) +Counter in file 0 42:6 -> 42:7, (#3 + #4) +Counter in file 0 43:1 -> 43:2, #5 +Emitting segments for file: ../coverage/simple_match.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 15:9 -> 15:10 (count=2) + 17:9 -> 17:13 (count=3) + 22:13 -> 22:22 (count=2) + 24:13 -> 24:14 (count=1) + 26:17 -> 28:18 (count=2) + 28:18 -> 28:19 (count=1) + 30:13 -> 37:14 (count=1) + 40:13 -> 40:15 (count=1) + 42:6 -> 42:7 (count=3) + 43:1 -> 43:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 15:9 (count = 2), RegionEntry +Segment at 15:10 (count = 0), Skipped +Segment at 17:9 (count = 3), RegionEntry +Segment at 17:13 (count = 0), Skipped +Segment at 22:13 (count = 2), RegionEntry +Segment at 22:22 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 24:14 (count = 0), Skipped +Segment at 26:17 (count = 2), RegionEntry +Segment at 28:18 (count = 1), RegionEntry +Segment at 28:19 (count = 0), Skipped +Segment at 30:13 (count = 1), RegionEntry +Segment at 37:14 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 40:15 (count = 0), Skipped +Segment at 42:6 (count = 3), RegionEntry +Segment at 42:7 (count = 0), Skipped +Segment at 43:1 (count = 1), RegionEntry +Segment at 43:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_inf_loop.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_inf_loop.txt new file mode 100644 index 0000000000000..5887658fe67a2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_inf_loop.txt @@ -0,0 +1,10 @@ +Counter in file 0 2:8 -> 2:13, #1 +Counter in file 0 5:1 -> 5:2, (#1 - #2) +Emitting segments for file: ../coverage/tight_inf_loop.rs +Combined regions: + 2:8 -> 2:13 (count=1) + 5:1 -> 5:2 (count=1) +Segment at 2:8 (count = 1), RegionEntry +Segment at 2:13 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt new file mode 100644 index 0000000000000..a317cd792910d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt @@ -0,0 +1,72 @@ +Counter in file 0 13:9 -> 14:23, #1 +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - #4) +Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) +Counter in file 0 21:9 -> 25:26, #8 +Counter in file 0 27:13 -> 27:41, #9 +Counter in file 0 27:41 -> 27:42, #5 +Counter in file 0 28:13 -> 28:42, (#9 - #5) +Counter in file 0 28:42 -> 28:43, #6 +Counter in file 0 32:13 -> 32:42, (#8 - #9) +Counter in file 0 32:42 -> 32:43, #7 +Counter in file 0 33:10 -> 33:11, #2 +Counter in file 0 33:10 -> 33:11, #3 +Counter in file 0 34:6 -> 34:7, (#2 + #3) +Counter in file 0 35:5 -> 35:11, #4 +Counter in file 0 36:1 -> 36:2, ((#5 + (#6 + #7)) + #4) +Counter in file 0 5:8 -> 5:20, #1 +Counter in file 0 6:9 -> 6:16, #2 +Counter in file 0 8:9 -> 8:15, (#1 - #2) +Counter in file 0 10:1 -> 10:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/try_error_result.rs +Combined regions: + 5:8 -> 5:20 (count=6) + 6:9 -> 6:16 (count=1) + 8:9 -> 8:15 (count=5) + 10:1 -> 10:2 (count=6) + 13:9 -> 14:23 (count=1) + 17:9 -> 17:10 (count=6) + 19:9 -> 19:14 (count=6) + 21:9 -> 25:26 (count=6) + 27:13 -> 27:41 (count=1) + 27:41 -> 27:42 (count=1) + 28:13 -> 28:42 (count=0) + 28:42 -> 28:43 (count=0) + 32:13 -> 32:42 (count=5) + 32:42 -> 32:43 (count=0) + 33:10 -> 33:11 (count=5) + 34:6 -> 34:7 (count=5) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=1) +Segment at 5:8 (count = 6), RegionEntry +Segment at 5:20 (count = 0), Skipped +Segment at 6:9 (count = 1), RegionEntry +Segment at 6:16 (count = 0), Skipped +Segment at 8:9 (count = 5), RegionEntry +Segment at 8:15 (count = 0), Skipped +Segment at 10:1 (count = 6), RegionEntry +Segment at 10:2 (count = 0), Skipped +Segment at 13:9 (count = 1), RegionEntry +Segment at 14:23 (count = 0), Skipped +Segment at 17:9 (count = 6), RegionEntry +Segment at 17:10 (count = 0), Skipped +Segment at 19:9 (count = 6), RegionEntry +Segment at 19:14 (count = 0), Skipped +Segment at 21:9 (count = 6), RegionEntry +Segment at 25:26 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:41 (count = 1), RegionEntry +Segment at 27:42 (count = 0), Skipped +Segment at 28:13 (count = 0), RegionEntry +Segment at 28:42 (count = 0), RegionEntry +Segment at 28:43 (count = 0), Skipped +Segment at 32:13 (count = 5), RegionEntry +Segment at 32:42 (count = 0), RegionEntry +Segment at 32:43 (count = 0), Skipped +Segment at 33:10 (count = 5), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:6 (count = 5), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 1), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt new file mode 100644 index 0000000000000..b0e881da7c8cb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt @@ -0,0 +1,18 @@ +Counter in file 0 2:9 -> 2:16, #1 +Counter in file 0 3:11 -> 3:20, (#1 + #2) +Counter in file 0 3:21 -> 4:6, #2 +Counter in file 0 5:1 -> 5:2, ((#1 + #2) - #2) +Emitting segments for file: ../coverage/while.rs +Combined regions: + 2:9 -> 2:16 (count=1) + 3:11 -> 3:20 (count=1) + 3:21 -> 4:6 (count=0) + 5:1 -> 5:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 2:16 (count = 0), Skipped +Segment at 3:11 (count = 1), RegionEntry +Segment at 3:20 (count = 0), Skipped +Segment at 3:21 (count = 0), RegionEntry +Segment at 4:6 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_ret.txt b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_ret.txt new file mode 100644 index 0000000000000..f541baec50c0b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_ret.txt @@ -0,0 +1,38 @@ +Counter in file 0 5:9 -> 5:27, #1 +Counter in file 0 7:9 -> 9:10, (#1 + #2) +Counter in file 0 12:13 -> 14:14, ((#1 + #2) - #3) +Counter in file 0 18:21 -> 20:22, #6 +Counter in file 0 22:21 -> 22:27, #4 +Counter in file 0 26:21 -> 26:27, #5 +Counter in file 0 30:9 -> 32:10, #2 +Counter in file 0 35:5 -> 35:11, #3 +Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Emitting segments for file: ../coverage/while_early_ret.rs +Combined regions: + 5:9 -> 5:27 (count=1) + 7:9 -> 9:10 (count=7) + 12:13 -> 14:14 (count=7) + 18:21 -> 20:22 (count=1) + 22:21 -> 22:27 (count=0) + 26:21 -> 26:27 (count=1) + 30:9 -> 32:10 (count=6) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=1) +Segment at 5:9 (count = 1), RegionEntry +Segment at 5:27 (count = 0), Skipped +Segment at 7:9 (count = 7), RegionEntry +Segment at 9:10 (count = 0), Skipped +Segment at 12:13 (count = 7), RegionEntry +Segment at 14:14 (count = 0), Skipped +Segment at 18:21 (count = 1), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 22:21 (count = 0), RegionEntry +Segment at 22:27 (count = 0), Skipped +Segment at 26:21 (count = 1), RegionEntry +Segment at 26:27 (count = 0), Skipped +Segment at 30:9 (count = 6), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 1), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json index 8c6edae280397..bff55300b3ca3 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json @@ -17,14 +17,14 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } @@ -42,14 +42,14 @@ }, "lines": { "count": 91, - "covered": 75, - "percent": 82.41758241758241 + "covered": 77, + "percent": 84.61538461538461 }, "regions": { - "count": 21, - "covered": 11, - "notcovered": 10, - "percent": 52.38095238095239 + "count": 25, + "covered": 13, + "notcovered": 12, + "percent": 52 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.conditions.json similarity index 76% rename from src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json rename to src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.conditions.json index 410821ea33592..ed937a1b13f38 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.conditions.json @@ -3,7 +3,7 @@ { "files": [ { - "filename": "../coverage/various_conditions.rs", + "filename": "../coverage/conditions.rs", "summary": { "functions": { "count": 1, @@ -21,10 +21,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 69, + "covered": 18, + "notcovered": 51, + "percent": 26.08695652173913 } } } @@ -46,10 +46,10 @@ "percent": 46.93877551020408 }, "regions": { - "count": 51, - "covered": 19, - "notcovered": 32, - "percent": 37.254901960784316 + "count": 69, + "covered": 18, + "notcovered": 51, + "percent": 26.08695652173913 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json index 2ff53ad33fa99..84dcc251f3f4b 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 4, + "count": 5, "covered": 4, - "notcovered": 0, - "percent": 100 + "notcovered": 1, + "percent": 80 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json index a24e6a33a3397..c178e7f93476f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json @@ -21,10 +21,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } @@ -46,10 +46,10 @@ "percent": 100 }, "regions": { - "count": 13, + "count": 15, "covered": 13, - "notcovered": 0, - "percent": 100 + "notcovered": 2, + "percent": 86.66666666666667 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json index 585346dc32a60..5a953b90b423f 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 30, + "percent": 75 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 21, - "covered": 19, - "percent": 90.47619047619048 + "count": 40, + "covered": 30, + "percent": 75 }, "regions": { - "count": 16, - "covered": 14, - "notcovered": 2, - "percent": 87.5 + "count": 37, + "covered": 26, + "notcovered": 11, + "percent": 70.27027027027027 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_branches.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_branches.json new file mode 100644 index 0000000000000..2dca41df9d24e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_branches.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/loops_branches.rs", + "summary": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "totals": { + "functions": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "instantiations": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "lines": { + "count": 11, + "covered": 11, + "percent": 100 + }, + "regions": { + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json new file mode 100644 index 0000000000000..68163eb763619 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/nested_loops.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 21, + "covered": 16, + "percent": 76.19047619047619 + }, + "regions": { + "count": 18, + "covered": 14, + "notcovered": 4, + "percent": 77.77777777777779 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq.json new file mode 100644 index 0000000000000..bc0d0088041a8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/partial_eq.rs", + "summary": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 8, + "covered": 5, + "percent": 62.5 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 5, + "covered": 5, + "percent": 100 + }, + "instantiations": { + "count": 8, + "covered": 5, + "percent": 62.5 + }, + "lines": { + "count": 15, + "covered": 15, + "percent": 100 + }, + "regions": { + "count": 6, + "covered": 6, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json index 38bc96898ea36..ada6bb062dd1e 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 18, + "count": 19, + "covered": 19, "percent": 100 }, "regions": { - "count": 7, - "covered": 7, - "notcovered": 0, - "percent": 100 + "count": 9, + "covered": 8, + "notcovered": 1, + "percent": 88.88888888888889 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json index f9d91d66f1db2..63d1ae74c5f5d 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 24, + "covered": 24, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 15, + "covered": 14, + "notcovered": 1, + "percent": 93.33333333333333 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 26, - "covered": 26, + "count": 24, + "covered": 24, "percent": 100 }, "regions": { - "count": 9, - "covered": 9, - "notcovered": 0, - "percent": 100 + "count": 15, + "covered": 14, + "notcovered": 1, + "percent": 93.33333333333333 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_inf_loop.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_inf_loop.json new file mode 100644 index 0000000000000..872560384eb90 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_inf_loop.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/tight_inf_loop.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 2, + "covered": 2, + "percent": 100 + }, + "regions": { + "count": 2, + "covered": 2, + "notcovered": 0, + "percent": 100 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json index e6ef2c1ab899e..78b935b15689a 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json @@ -16,15 +16,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 19, + "covered": 14, + "notcovered": 5, + "percent": 73.68421052631578 } } } @@ -41,15 +41,15 @@ "percent": 100 }, "lines": { - "count": 16, - "covered": 15, - "percent": 93.75 + "count": 20, + "covered": 18, + "percent": 90 }, "regions": { - "count": 13, - "covered": 12, - "notcovered": 1, - "percent": 92.3076923076923 + "count": 19, + "covered": 14, + "notcovered": 5, + "percent": 73.68421052631578 } } } diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json new file mode 100644 index 0000000000000..339c65556682a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json @@ -0,0 +1,59 @@ +{ + "data": [ + { + "files": [ + { + "filename": "../coverage/while.rs", + "summary": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "totals": { + "functions": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "instantiations": { + "count": 1, + "covered": 1, + "percent": 100 + }, + "lines": { + "count": 4, + "covered": 3, + "percent": 75 + }, + "regions": { + "count": 4, + "covered": 3, + "notcovered": 1, + "percent": 75 + } + } + } + ], + "type": "llvm.coverage.json.export", + "version": "2.0.1" +} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_ret.json similarity index 81% rename from src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json rename to src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_ret.json index 865b705fa2007..ad43f5d992630 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_return.json +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_ret.json @@ -3,7 +3,7 @@ { "files": [ { - "filename": "../coverage/while_early_return.rs", + "filename": "../coverage/while_early_ret.rs", "summary": { "functions": { "count": 1, @@ -16,9 +16,9 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { "count": 9, @@ -41,9 +41,9 @@ "percent": 100 }, "lines": { - "count": 18, - "covered": 16, - "percent": 88.88888888888889 + "count": 17, + "covered": 15, + "percent": 88.23529411764706 }, "regions": { "count": 9, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt index 17054490e9b3c..aef26a62e25fb 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt @@ -62,7 +62,7 @@ 62| 1| let mut countdown = 0; 63| 1| if is_false { 64| 0| countdown = 10; - 65| 0| } + 65| 1| } 66| 1| "alt string 3".to_owned() 67| 1| } 68| 1| ) @@ -77,7 +77,7 @@ 77| 1| let mut countdown = 0; 78| 1| if is_false { 79| 0| countdown = 10; - 80| 0| } + 80| 1| } 81| 1| "alt string 4".to_owned() 82| 1| }; 83| 1| println!( diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.conditions.txt similarity index 100% rename from src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.various_conditions.txt rename to src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.conditions.txt diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt index bc2f9b108b2f3..85e6440ab3729 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt @@ -25,5 +25,6 @@ 25| 1| 10 26| 1| ; 27| 1| } + ^0 28| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt index b13ca83d018f9..4a51f842a4bb2 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt @@ -10,6 +10,7 @@ 10| 1| if is_true { 11| 1| countdown = 10; 12| 1| } + ^0 13| | 14| | mod in_mod { 15| | const IN_MOD_CONST: u32 = 1000; @@ -48,6 +49,7 @@ 48| 1| if is_true { 49| 1| in_func(countdown); 50| 1| } + ^0 51| | 52| 1| let mut val = InStruct { 53| 1| in_struct_field: 101, diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt index ded4369751587..1b503033911c5 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt @@ -12,12 +12,14 @@ 12| 1| b = 10; 13| 1| c = 100; 14| 1| } + ^0 15| | let 16| 1| somebool 17| | = 18| 1| a < b 19| | || - 20| 0| b < c + 20| 1| b < c + ^0 21| | ; 22| | let 23| 1| somebool @@ -26,19 +28,38 @@ 26| | || 27| 1| b < c 28| | ; - 29| | let - 30| 1| somebool - 31| | = - 32| 1| a < b - 33| | && - 34| 1| b < c - 35| | ; - 36| | let - 37| 1| somebool - 38| | = - 39| 1| b < a - 40| | && - 41| 0| b < c - 42| | ; - 43| 1|} + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_branches.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_branches.txt new file mode 100644 index 0000000000000..3a969a6b89869 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_branches.txt @@ -0,0 +1,38 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 4| |// structure of this `fmt` function. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| | while true { + 13| | } + 14| 1| } + 15| 1| write!(f, "error")?; + ^0 + 16| | } else { + 17| 1| } + 18| 1| Ok(()) + 19| 1| } + 20| |} + 21| | + 22| 1|fn main() { + 23| 1| let debug_test = DebugTest; + 24| 1| println!("{:?}", debug_test); + 25| 1|} + 26| | + 27| |/* + 28| | + 29| |This is the error message generated, before the issue was fixed: + 30| | + 31| |error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: + 32| |Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): + 33| |Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: + 34| |[bcb6, bcb7, bcb9]" } + 35| | + 36| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt new file mode 100644 index 0000000000000..c9f373bf6a7c0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt @@ -0,0 +1,26 @@ + 1| |fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| | } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| | } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| 2| } + 23| 0| countdown -= 1; + 24| 0| } + 25| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq.txt new file mode 100644 index 0000000000000..d16a0a9c4c8c4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq.txt @@ -0,0 +1,111 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 + ------------------ + | Unexecuted instantiation: ::gt + ------------------ + | Unexecuted instantiation: ::le + ------------------ + | Unexecuted instantiation: ::ge + ------------------ + | ::lt: + | 4| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ------------------ + 5| |pub struct Version { + 6| | major: usize, + 7| 1| minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + 47| | + 48| |// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces + 49| |// the following results: + 50| | + 51| |/* + 52| | + 53| |1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + 54| | + 55| |2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + 56| | line count with a different instance? Or was it really only called once? + 57| | + 58| |3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + 59| | its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + 60| | problem computing the file position here, for some reason. + 61| | + 62| | + 63| | 16| | + 64| | 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 65| | ^1 ^1 + 66| |------------------ + 67| ||Unexecuted instantiation: ::gt + 68| |------------------ + 69| ||Unexecuted instantiation: ::le + 70| |------------------ + 71| ||Unexecuted instantiation: ::ge + 72| |------------------ + 73| ||::lt: + 74| || 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + 75| |------------------ + 76| | 18| |pub struct Version { + 77| | 19| | major: usize, + 78| | 20| 1| minor: usize, + 79| | 21| | patch: usize, + 80| | 22| |} + 81| | 23| | + 82| | 24| |impl Version { + 83| | 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 84| | 26| 2| Version { + 85| | 27| 2| major, + 86| | 28| 2| minor, + 87| | 29| 2| patch, + 88| | 30| 2| } + 89| | 31| 2| } + 90| | 32| |} + 91| | 33| | + 92| | 34| 1|fn main() { + 93| | 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 94| | 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 95| | 37| 1| + 96| | 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version + 97| |_3_3_0); + 98| | 39| 1|} + 99| |*/ + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt index f1acb7c545940..064930dd75c93 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt @@ -16,6 +16,7 @@ 16| 1| 10 17| 1| ; 18| 1| } + ^0 19| | 20| | loop 21| | { @@ -31,6 +32,6 @@ 31| 10| -= 32| 10| 1 33| | ; - 34| | } + 34| 1| } 35| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt index e42f22cd047fc..1f7e71d3eb0e7 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt @@ -10,22 +10,24 @@ 10| 1| if is_true { 11| 1| countdown = 0; 12| 1| } + ^0 13| | - 14| 3| for - 15| 3| _ + 14| | for + 15| 2| _ 16| | in - 17| 1| 0..2 + 17| 3| 0..2 18| | { 19| | let z 20| | ; 21| | match 22| 2| countdown - 23| 2| { - 24| 2| x - 25| 2| if + 23| | { + 24| 1| x + 25| | if 26| 2| x 27| 2| < 28| 2| 1 + ^1 29| | => 30| 1| { 31| 1| z = countdown @@ -39,6 +41,6 @@ 39| | => 40| 1| {} 41| | } - 42| | } + 42| 3| } 43| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_inf_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_inf_loop.txt new file mode 100644 index 0000000000000..e02eac03a6b15 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_inf_loop.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| if false { + 3| | loop {} + 4| | } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt index ae288d7d7a000..6b3a8c39c6338 100644 --- a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt @@ -13,24 +13,26 @@ 13| 1| let mut 14| 1| countdown = 10 15| | ; - 16| 6| for + 16| | for 17| 6| _ 18| | in - 19| 1| 0..10 + 19| 6| 0..10 20| | { 21| 6| countdown 22| 6| -= 1 - 23| | ; - 24| | if + 23| 6| ; + 24| 6| if 25| 6| countdown < 5 26| | { 27| 1| call(/*return_error=*/ true)?; - 28| | } - 29| | else - 30| | { - 31| 5| call(/*return_error=*/ false)?; - 32| | } - 33| | } - 34| 0| Ok(()) - 35| 1|} + 28| 0| call(/*return_error=*/ false)?; + 29| | } + 30| | else + 31| | { + 32| 5| call(/*return_error=*/ false)?; + ^0 + 33| 5| } + 34| 5| } + 35| 0| Ok(()) + 36| 1|} diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt new file mode 100644 index 0000000000000..194325b6b9eca --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt @@ -0,0 +1,6 @@ + 1| |fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_ret.txt similarity index 98% rename from src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt rename to src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_ret.txt index 7dce94f25f304..26041136d2f4c 100644 --- a/src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_return.txt +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_ret.txt @@ -3,7 +3,7 @@ 3| | 4| |fn main() -> Result<(),u8> { 5| 1| let mut countdown = 10; - 6| 7| while + 6| | while 7| 7| countdown 8| 7| > 9| 7| 0 diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt new file mode 100644 index 0000000000000..fb797796e4e7d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt @@ -0,0 +1,94 @@ +Counter in file 0 20:21 -> 20:38, #1 +Counter in file 0 21:20 -> 21:28, (#1 + 0) +Counter in file 0 21:29 -> 23:18, #2 +Counter in file 0 23:18 -> 23:19, (#1 - #2) +Counter in file 0 24:17 -> 25:14, (#2 + (#1 - #2)) +Counter in file 0 3:11 -> 18:13, #1 +Counter in file 0 25:14 -> 33:9, (#1 + 0) +Counter in file 0 40:6 -> 60:13, (#1 + 0) +Counter in file 0 67:14 -> 75:9, (#1 + 0) +Counter in file 0 82:6 -> 93:2, (#1 + 0) +Counter in file 0 77:13 -> 77:30, #1 +Counter in file 0 78:12 -> 78:20, (#1 + 0) +Counter in file 0 78:21 -> 80:10, #2 +Counter in file 0 80:10 -> 80:11, (#1 - #2) +Counter in file 0 81:9 -> 82:6, (#2 + (#1 - #2)) +Counter in file 0 62:21 -> 62:38, #1 +Counter in file 0 63:20 -> 63:28, (#1 + 0) +Counter in file 0 63:29 -> 65:18, #2 +Counter in file 0 65:18 -> 65:19, (#1 - #2) +Counter in file 0 66:17 -> 67:14, (#2 + (#1 - #2)) +Counter in file 0 35:13 -> 35:30, #1 +Counter in file 0 36:12 -> 36:20, (#1 + 0) +Counter in file 0 36:21 -> 38:10, #2 +Counter in file 0 38:10 -> 38:11, (#1 - #2) +Counter in file 0 39:9 -> 40:6, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/closure.rs +Combined regions: + 3:11 -> 18:13 (count=1) + 20:21 -> 20:38 (count=0) + 21:20 -> 21:28 (count=0) + 21:29 -> 23:18 (count=0) + 23:18 -> 23:19 (count=0) + 24:17 -> 25:14 (count=0) + 25:14 -> 33:9 (count=1) + 35:13 -> 35:30 (count=0) + 36:12 -> 36:20 (count=0) + 36:21 -> 38:10 (count=0) + 38:10 -> 38:11 (count=0) + 39:9 -> 40:6 (count=0) + 40:6 -> 60:13 (count=1) + 62:21 -> 62:38 (count=1) + 63:20 -> 63:28 (count=1) + 63:29 -> 65:18 (count=0) + 65:18 -> 65:19 (count=1) + 66:17 -> 67:14 (count=1) + 67:14 -> 75:9 (count=1) + 77:13 -> 77:30 (count=1) + 78:12 -> 78:20 (count=1) + 78:21 -> 80:10 (count=0) + 80:10 -> 80:11 (count=1) + 81:9 -> 82:6 (count=1) + 82:6 -> 93:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 18:13 (count = 0), Skipped +Segment at 20:21 (count = 0), RegionEntry +Segment at 20:38 (count = 0), Skipped +Segment at 21:20 (count = 0), RegionEntry +Segment at 21:28 (count = 0), Skipped +Segment at 21:29 (count = 0), RegionEntry +Segment at 23:18 (count = 0), RegionEntry +Segment at 23:19 (count = 0), Skipped +Segment at 24:17 (count = 0), RegionEntry +Segment at 25:14 (count = 1), RegionEntry +Segment at 33:9 (count = 0), Skipped +Segment at 35:13 (count = 0), RegionEntry +Segment at 35:30 (count = 0), Skipped +Segment at 36:12 (count = 0), RegionEntry +Segment at 36:20 (count = 0), Skipped +Segment at 36:21 (count = 0), RegionEntry +Segment at 38:10 (count = 0), RegionEntry +Segment at 38:11 (count = 0), Skipped +Segment at 39:9 (count = 0), RegionEntry +Segment at 40:6 (count = 1), RegionEntry +Segment at 60:13 (count = 0), Skipped +Segment at 62:21 (count = 1), RegionEntry +Segment at 62:38 (count = 0), Skipped +Segment at 63:20 (count = 1), RegionEntry +Segment at 63:28 (count = 0), Skipped +Segment at 63:29 (count = 0), RegionEntry +Segment at 65:18 (count = 1), RegionEntry +Segment at 65:19 (count = 0), Skipped +Segment at 66:17 (count = 1), RegionEntry +Segment at 67:14 (count = 1), RegionEntry +Segment at 75:9 (count = 0), Skipped +Segment at 77:13 (count = 1), RegionEntry +Segment at 77:30 (count = 0), Skipped +Segment at 78:12 (count = 1), RegionEntry +Segment at 78:20 (count = 0), Skipped +Segment at 78:21 (count = 0), RegionEntry +Segment at 80:10 (count = 1), RegionEntry +Segment at 80:11 (count = 0), Skipped +Segment at 81:9 (count = 1), RegionEntry +Segment at 82:6 (count = 1), RegionEntry +Segment at 93:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.conditions.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.conditions.txt new file mode 100644 index 0000000000000..d48cd8074bebb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.conditions.txt @@ -0,0 +1,238 @@ +Counter in file 0 4:9 -> 4:26, #1 +Counter in file 0 5:8 -> 5:12, (#1 + 0) +Counter in file 0 5:13 -> 7:6, #2 +Counter in file 0 10:9 -> 10:10, (#4 + #11) +Counter in file 0 10:16 -> 10:29, (#2 + 0) +Counter in file 0 11:9 -> 12:10, #4 +Counter in file 0 13:15 -> 13:28, ((#2 + 0) - #3) +Counter in file 0 14:12 -> 14:25, #5 +Counter in file 0 14:29 -> 14:42, (#5 - #13) +Counter in file 0 14:42 -> 14:43, (#13 + #14) +Counter in file 0 14:42 -> 14:43, ((#5 - #13) - #14) +Counter in file 0 14:46 -> 14:60, #21 +Counter in file 0 14:60 -> 14:61, (#17 + #18) +Counter in file 0 14:60 -> 14:61, (#21 - #18) +Counter in file 0 14:61 -> 16:10, #22 +Counter in file 0 16:10 -> 16:11, #23 +Counter in file 0 17:9 -> 18:18, #11 +Counter in file 0 20:9 -> 20:15, (((#2 + 0) - #3) - #5) +Counter in file 0 23:9 -> 23:26, ((#4 + #11) + 0) +Counter in file 0 24:8 -> 24:12, ((#4 + #11) + 0) +Counter in file 0 24:13 -> 26:6, #12 +Counter in file 0 28:8 -> 28:21, (#12 + 0) +Counter in file 0 29:9 -> 29:23, #16 +Counter in file 0 30:15 -> 30:28, ((#12 + 0) - #15) +Counter in file 0 31:12 -> 31:25, (((#12 + 0) - #15) - #8) +Counter in file 0 31:29 -> 31:42, ((((#12 + 0) - #15) - #8) - #24) +Counter in file 0 31:42 -> 31:43, (((((#12 + 0) - #15) - #8) - #24) - #25) +Counter in file 0 31:42 -> 31:43, (#24 + #25) +Counter in file 0 31:46 -> 31:60, #32 +Counter in file 0 31:60 -> 31:61, (#28 + #29) +Counter in file 0 31:60 -> 31:61, (#32 - #29) +Counter in file 0 31:61 -> 33:10, #33 +Counter in file 0 33:10 -> 33:11, #34 +Counter in file 0 34:9 -> 34:23, #19 +Counter in file 0 36:9 -> 36:15, #8 +Counter in file 0 39:9 -> 39:26, (#16 + #19) +Counter in file 0 40:8 -> 40:12, ((#16 + #19) + 0) +Counter in file 0 40:13 -> 42:6, #20 +Counter in file 0 44:9 -> 44:10, (#27 + #30) +Counter in file 0 44:16 -> 44:29, (#20 + 0) +Counter in file 0 45:9 -> 45:23, #27 +Counter in file 0 46:15 -> 46:28, ((#20 + 0) - #26) +Counter in file 0 47:12 -> 47:25, (((#20 + 0) - #26) - #7) +Counter in file 0 47:29 -> 47:42, ((((#20 + 0) - #26) - #7) - #35) +Counter in file 0 47:42 -> 47:43, (#35 + #36) +Counter in file 0 47:42 -> 47:43, (((((#20 + 0) - #26) - #7) - #35) - #36) +Counter in file 0 47:46 -> 47:60, #41 +Counter in file 0 47:60 -> 47:61, (#37 + #38) +Counter in file 0 47:60 -> 47:61, (#41 - #38) +Counter in file 0 47:61 -> 49:10, #42 +Counter in file 0 49:10 -> 49:11, #43 +Counter in file 0 50:9 -> 50:23, #30 +Counter in file 0 52:13 -> 54:15, #7 +Counter in file 0 57:9 -> 57:10, (#9 + #10) +Counter in file 0 57:16 -> 57:29, ((#27 + #30) + 0) +Counter in file 0 58:9 -> 58:23, #9 +Counter in file 0 59:15 -> 59:28, ((#27 + #30) - #31) +Counter in file 0 60:12 -> 60:25, (((#27 + #30) - #31) - #6) +Counter in file 0 60:29 -> 60:42, ((((#27 + #30) - #31) - #6) - #39) +Counter in file 0 60:42 -> 60:43, (#39 + #40) +Counter in file 0 60:42 -> 60:43, (((((#27 + #30) - #31) - #6) - #39) - #40) +Counter in file 0 60:46 -> 60:60, #46 +Counter in file 0 60:60 -> 60:61, (#46 - #45) +Counter in file 0 60:60 -> 60:61, (#44 + #45) +Counter in file 0 60:61 -> 62:10, #47 +Counter in file 0 62:10 -> 62:11, #48 +Counter in file 0 63:9 -> 63:23, #10 +Counter in file 0 65:9 -> 65:15, #6 +Counter in file 0 67:1 -> 67:2, ((#9 + #10) + (((#6 + #7) + #8) + (((#2 + 0) - #3) - #5))) +Emitting segments for file: ../coverage/conditions.rs +Combined regions: + 4:9 -> 4:26 (count=1) + 5:8 -> 5:12 (count=1) + 5:13 -> 7:6 (count=1) + 10:9 -> 10:10 (count=1) + 10:16 -> 10:29 (count=1) + 11:9 -> 12:10 (count=1) + 13:15 -> 13:28 (count=0) + 14:12 -> 14:25 (count=0) + 14:29 -> 14:42 (count=0) + 14:42 -> 14:43 (count=0) + 14:46 -> 14:60 (count=0) + 14:60 -> 14:61 (count=0) + 14:61 -> 16:10 (count=0) + 16:10 -> 16:11 (count=0) + 17:9 -> 18:18 (count=0) + 20:9 -> 20:15 (count=0) + 23:9 -> 23:26 (count=1) + 24:8 -> 24:12 (count=1) + 24:13 -> 26:6 (count=1) + 28:8 -> 28:21 (count=1) + 29:9 -> 29:23 (count=1) + 30:15 -> 30:28 (count=0) + 31:12 -> 31:25 (count=0) + 31:29 -> 31:42 (count=0) + 31:42 -> 31:43 (count=0) + 31:46 -> 31:60 (count=0) + 31:60 -> 31:61 (count=0) + 31:61 -> 33:10 (count=0) + 33:10 -> 33:11 (count=0) + 34:9 -> 34:23 (count=0) + 36:9 -> 36:15 (count=0) + 39:9 -> 39:26 (count=1) + 40:8 -> 40:12 (count=1) + 40:13 -> 42:6 (count=1) + 44:9 -> 44:10 (count=0) + 44:16 -> 44:29 (count=1) + 45:9 -> 45:23 (count=0) + 46:15 -> 46:28 (count=1) + 47:12 -> 47:25 (count=0) + 47:29 -> 47:42 (count=0) + 47:42 -> 47:43 (count=0) + 47:46 -> 47:60 (count=0) + 47:60 -> 47:61 (count=0) + 47:61 -> 49:10 (count=0) + 49:10 -> 49:11 (count=0) + 50:9 -> 50:23 (count=0) + 52:13 -> 54:15 (count=1) + 57:9 -> 57:10 (count=0) + 57:16 -> 57:29 (count=0) + 58:9 -> 58:23 (count=0) + 59:15 -> 59:28 (count=0) + 60:12 -> 60:25 (count=0) + 60:29 -> 60:42 (count=0) + 60:42 -> 60:43 (count=0) + 60:46 -> 60:60 (count=0) + 60:60 -> 60:61 (count=0) + 60:61 -> 62:10 (count=0) + 62:10 -> 62:11 (count=0) + 63:9 -> 63:23 (count=0) + 65:9 -> 65:15 (count=0) + 67:1 -> 67:2 (count=1) +Segment at 4:9 (count = 1), RegionEntry +Segment at 4:26 (count = 0), Skipped +Segment at 5:8 (count = 1), RegionEntry +Segment at 5:12 (count = 0), Skipped +Segment at 5:13 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 10:9 (count = 1), RegionEntry +Segment at 10:10 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 10:29 (count = 0), Skipped +Segment at 11:9 (count = 1), RegionEntry +Segment at 12:10 (count = 0), Skipped +Segment at 13:15 (count = 0), RegionEntry +Segment at 13:28 (count = 0), Skipped +Segment at 14:12 (count = 0), RegionEntry +Segment at 14:25 (count = 0), Skipped +Segment at 14:29 (count = 0), RegionEntry +Segment at 14:42 (count = 0), RegionEntry +Segment at 14:43 (count = 0), Skipped +Segment at 14:46 (count = 0), RegionEntry +Segment at 14:60 (count = 0), RegionEntry +Segment at 14:61 (count = 0), RegionEntry +Segment at 16:10 (count = 0), RegionEntry +Segment at 16:11 (count = 0), Skipped +Segment at 17:9 (count = 0), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 20:15 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:26 (count = 0), Skipped +Segment at 24:8 (count = 1), RegionEntry +Segment at 24:12 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 26:6 (count = 0), Skipped +Segment at 28:8 (count = 1), RegionEntry +Segment at 28:21 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:23 (count = 0), Skipped +Segment at 30:15 (count = 0), RegionEntry +Segment at 30:28 (count = 0), Skipped +Segment at 31:12 (count = 0), RegionEntry +Segment at 31:25 (count = 0), Skipped +Segment at 31:29 (count = 0), RegionEntry +Segment at 31:42 (count = 0), RegionEntry +Segment at 31:43 (count = 0), Skipped +Segment at 31:46 (count = 0), RegionEntry +Segment at 31:60 (count = 0), RegionEntry +Segment at 31:61 (count = 0), RegionEntry +Segment at 33:10 (count = 0), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:9 (count = 0), RegionEntry +Segment at 34:23 (count = 0), Skipped +Segment at 36:9 (count = 0), RegionEntry +Segment at 36:15 (count = 0), Skipped +Segment at 39:9 (count = 1), RegionEntry +Segment at 39:26 (count = 0), Skipped +Segment at 40:8 (count = 1), RegionEntry +Segment at 40:12 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 42:6 (count = 0), Skipped +Segment at 44:9 (count = 0), RegionEntry +Segment at 44:10 (count = 0), Skipped +Segment at 44:16 (count = 1), RegionEntry +Segment at 44:29 (count = 0), Skipped +Segment at 45:9 (count = 0), RegionEntry +Segment at 45:23 (count = 0), Skipped +Segment at 46:15 (count = 1), RegionEntry +Segment at 46:28 (count = 0), Skipped +Segment at 47:12 (count = 0), RegionEntry +Segment at 47:25 (count = 0), Skipped +Segment at 47:29 (count = 0), RegionEntry +Segment at 47:42 (count = 0), RegionEntry +Segment at 47:43 (count = 0), Skipped +Segment at 47:46 (count = 0), RegionEntry +Segment at 47:60 (count = 0), RegionEntry +Segment at 47:61 (count = 0), RegionEntry +Segment at 49:10 (count = 0), RegionEntry +Segment at 49:11 (count = 0), Skipped +Segment at 50:9 (count = 0), RegionEntry +Segment at 50:23 (count = 0), Skipped +Segment at 52:13 (count = 1), RegionEntry +Segment at 54:15 (count = 0), Skipped +Segment at 57:9 (count = 0), RegionEntry +Segment at 57:10 (count = 0), Skipped +Segment at 57:16 (count = 0), RegionEntry +Segment at 57:29 (count = 0), Skipped +Segment at 58:9 (count = 0), RegionEntry +Segment at 58:23 (count = 0), Skipped +Segment at 59:15 (count = 0), RegionEntry +Segment at 59:28 (count = 0), Skipped +Segment at 60:12 (count = 0), RegionEntry +Segment at 60:25 (count = 0), Skipped +Segment at 60:29 (count = 0), RegionEntry +Segment at 60:42 (count = 0), RegionEntry +Segment at 60:43 (count = 0), Skipped +Segment at 60:46 (count = 0), RegionEntry +Segment at 60:60 (count = 0), RegionEntry +Segment at 60:61 (count = 0), RegionEntry +Segment at 62:10 (count = 0), RegionEntry +Segment at 62:11 (count = 0), Skipped +Segment at 63:9 (count = 0), RegionEntry +Segment at 63:23 (count = 0), Skipped +Segment at 65:9 (count = 0), RegionEntry +Segment at 65:15 (count = 0), Skipped +Segment at 67:1 (count = 1), RegionEntry +Segment at 67:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt new file mode 100644 index 0000000000000..375025fe8bcc2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt @@ -0,0 +1,22 @@ +Counter in file 0 9:24 -> 11:6, #1 +Counter in file 0 15:9 -> 17:42, #1 +Counter in file 0 19:8 -> 19:12, (#1 + 0) +Counter in file 0 20:9 -> 21:22, #2 +Counter in file 0 27:1 -> 27:2, (#2 + 0) +Emitting segments for file: ../coverage/drop_trait.rs +Combined regions: + 9:24 -> 11:6 (count=2) + 15:9 -> 17:42 (count=1) + 19:8 -> 19:12 (count=1) + 20:9 -> 21:22 (count=1) + 27:1 -> 27:2 (count=1) +Segment at 9:24 (count = 2), RegionEntry +Segment at 11:6 (count = 0), Skipped +Segment at 15:9 (count = 1), RegionEntry +Segment at 17:42 (count = 0), Skipped +Segment at 19:8 (count = 1), RegionEntry +Segment at 19:12 (count = 0), Skipped +Segment at 20:9 (count = 1), RegionEntry +Segment at 21:22 (count = 0), Skipped +Segment at 27:1 (count = 1), RegionEntry +Segment at 27:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt new file mode 100644 index 0000000000000..013a69ed3983a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt @@ -0,0 +1,48 @@ +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 17:24 -> 19:6, #1 +Counter in file 0 23:9 -> 28:28, #1 +Counter in file 0 30:8 -> 30:12, (#1 + 0) +Counter in file 0 31:9 -> 32:22, #2 +Counter in file 0 38:1 -> 38:2, (#2 + 0) +Counter in file 0 10:49 -> 12:6, #1 +Counter in file 0 10:49 -> 12:6, #1 +Emitting segments for file: ../coverage/generics.rs +Combined regions: + 10:49 -> 12:6 (count=3) + 17:24 -> 19:6 (count=2) + 23:9 -> 28:28 (count=1) + 30:8 -> 30:12 (count=1) + 31:9 -> 32:22 (count=1) + 38:1 -> 38:2 (count=1) +Segment at 10:49 (count = 3), RegionEntry +Segment at 12:6 (count = 0), Skipped +Segment at 17:24 (count = 2), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 28:28 (count = 0), Skipped +Segment at 30:8 (count = 1), RegionEntry +Segment at 30:12 (count = 0), Skipped +Segment at 31:9 (count = 1), RegionEntry +Segment at 32:22 (count = 0), Skipped +Segment at 38:1 (count = 1), RegionEntry +Segment at 38:2 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworkdE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=2) +Segment at 10:49 (count = 2), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvMCs4fqI2P2rA04_8genericsINtB2_8FireworklE12set_strengthB2_ +Combined regions: + 10:49 -> 12:6 (count=1) +Segment at 10:49 (count = 1), RegionEntry +Segment at 12:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworklENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Emitting segments for function: _RNvXs_Cs4fqI2P2rA04_8genericsINtB4_8FireworkdENtNtNtCs7f2nZg1zwMz_4core3ops4drop4Drop4dropB4_ +Combined regions: + 17:24 -> 19:6 (count=1) +Segment at 17:24 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt new file mode 100644 index 0000000000000..c2bef365ea9d8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt @@ -0,0 +1,21 @@ +Counter in file 0 8:5 -> 18:10, #1 +Counter in file 0 21:9 -> 21:16, (#1 + 0) +Counter in file 0 22:5 -> 27:6, #2 +Counter in file 0 27:6 -> 27:7, (#1 - #2) +Counter in file 0 28:1 -> 28:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/if.rs +Combined regions: + 8:5 -> 18:10 (count=1) + 21:9 -> 21:16 (count=1) + 22:5 -> 27:6 (count=1) + 27:6 -> 27:7 (count=0) + 28:1 -> 28:2 (count=1) +Segment at 8:5 (count = 1), RegionEntry +Segment at 18:10 (count = 0), Skipped +Segment at 21:9 (count = 1), RegionEntry +Segment at 21:16 (count = 0), Skipped +Segment at 22:5 (count = 1), RegionEntry +Segment at 27:6 (count = 0), RegionEntry +Segment at 27:7 (count = 0), Skipped +Segment at 28:1 (count = 1), RegionEntry +Segment at 28:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt new file mode 100644 index 0000000000000..faf5c094bbaaa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt @@ -0,0 +1,30 @@ +Counter in file 0 7:9 -> 11:16, #1 +Counter in file 0 12:5 -> 17:6, #2 +Counter in file 0 20:9 -> 22:16, (#1 - #2) +Counter in file 0 26:9 -> 26:16, (#2 + (#1 - #2)) +Counter in file 0 27:5 -> 32:6, #3 +Counter in file 0 34:5 -> 39:6, ((#2 + (#1 - #2)) - #3) +Counter in file 0 40:1 -> 40:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Emitting segments for file: ../coverage/if_else.rs +Combined regions: + 7:9 -> 11:16 (count=1) + 12:5 -> 17:6 (count=1) + 20:9 -> 22:16 (count=0) + 26:9 -> 26:16 (count=1) + 27:5 -> 32:6 (count=1) + 34:5 -> 39:6 (count=0) + 40:1 -> 40:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 11:16 (count = 0), Skipped +Segment at 12:5 (count = 1), RegionEntry +Segment at 17:6 (count = 0), Skipped +Segment at 20:9 (count = 0), RegionEntry +Segment at 22:16 (count = 0), Skipped +Segment at 26:9 (count = 1), RegionEntry +Segment at 26:16 (count = 0), Skipped +Segment at 27:5 (count = 1), RegionEntry +Segment at 32:6 (count = 0), Skipped +Segment at 34:5 (count = 0), RegionEntry +Segment at 39:6 (count = 0), Skipped +Segment at 40:1 (count = 1), RegionEntry +Segment at 40:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt new file mode 100644 index 0000000000000..65cd6481af4cc --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt @@ -0,0 +1,60 @@ +Counter in file 0 19:13 -> 19:18, #1 +Counter in file 0 20:13 -> 20:14, #2 +Counter in file 0 20:17 -> 20:22, (#1 + 0) +Counter in file 0 21:9 -> 22:6, (#2 + 0) +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 48:8 -> 48:15, (#2 + (#1 - #2)) +Counter in file 0 48:16 -> 50:6, #3 +Counter in file 0 50:6 -> 50:7, ((#2 + (#1 - #2)) - #3) +Counter in file 0 52:9 -> 57:2, (#3 + ((#2 + (#1 - #2)) - #3)) +Counter in file 0 33:42 -> 36:10, #1 +Counter in file 0 41:37 -> 41:41, #1 +Counter in file 0 42:13 -> 43:10, #2 +Emitting segments for file: ../coverage/inner_items.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 19:13 -> 19:18 (count=3) + 20:13 -> 20:14 (count=3) + 20:17 -> 20:22 (count=3) + 21:9 -> 22:6 (count=3) + 33:42 -> 36:10 (count=1) + 41:37 -> 41:41 (count=1) + 42:13 -> 43:10 (count=1) + 48:8 -> 48:15 (count=1) + 48:16 -> 50:6 (count=1) + 50:6 -> 50:7 (count=0) + 52:9 -> 57:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 19:13 (count = 3), RegionEntry +Segment at 19:18 (count = 0), Skipped +Segment at 20:13 (count = 3), RegionEntry +Segment at 20:14 (count = 0), Skipped +Segment at 20:17 (count = 3), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 21:9 (count = 3), RegionEntry +Segment at 22:6 (count = 0), Skipped +Segment at 33:42 (count = 1), RegionEntry +Segment at 36:10 (count = 0), Skipped +Segment at 41:37 (count = 1), RegionEntry +Segment at 41:41 (count = 0), Skipped +Segment at 42:13 (count = 1), RegionEntry +Segment at 43:10 (count = 0), Skipped +Segment at 48:8 (count = 1), RegionEntry +Segment at 48:15 (count = 0), Skipped +Segment at 48:16 (count = 1), RegionEntry +Segment at 50:6 (count = 0), RegionEntry +Segment at 50:7 (count = 0), Skipped +Segment at 52:9 (count = 1), RegionEntry +Segment at 57:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt new file mode 100644 index 0000000000000..8e56d79d9d2aa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt @@ -0,0 +1,131 @@ +Counter in file 0 7:9 -> 9:42, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 14:6, #2 +Counter in file 0 14:6 -> 14:7, (#1 - #2) +Counter in file 0 16:9 -> 16:17, ((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) +Counter in file 0 18:13 -> 18:18, (#2 + (#1 - #2)) +Counter in file 0 20:13 -> 20:18, ((#2 + (#1 - #2)) - #3) +Counter in file 0 20:18 -> 20:19, (#3 + #4) +Counter in file 0 20:18 -> 20:19, (((#2 + (#1 - #2)) - #3) - #4) +Counter in file 0 23:9 -> 23:17, ((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) +Counter in file 0 25:13 -> 25:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) + 0) +Counter in file 0 27:13 -> 27:18, (((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) +Counter in file 0 27:18 -> 27:19, (#5 + #6) +Counter in file 0 27:18 -> 27:19, ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6) +Counter in file 0 29:9 -> 29:17, ((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) +Counter in file 0 29:20 -> 29:25, (((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) + 0) +Counter in file 0 29:29 -> 29:34, #7 +Counter in file 0 29:34 -> 29:35, ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8) +Counter in file 0 29:34 -> 29:35, (#7 - #8) +Counter in file 0 30:9 -> 30:17, ((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) +Counter in file 0 30:20 -> 30:25, (((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) + 0) +Counter in file 0 30:29 -> 30:34, #9 +Counter in file 0 30:34 -> 30:35, ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10) +Counter in file 0 30:34 -> 30:35, (#9 - #10) +Counter in file 0 33:9 -> 34:16, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) + 0) +Counter in file 0 35:5 -> 38:6, #11 +Counter in file 0 38:6 -> 38:7, (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11) +Counter in file 0 41:9 -> 41:16, (#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) +Counter in file 0 42:5 -> 45:6, #12 +Counter in file 0 47:5 -> 50:6, ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12) +Counter in file 0 52:8 -> 52:16, (#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) +Counter in file 0 52:17 -> 54:6, #13 +Counter in file 0 54:6 -> 54:7, ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13) +Counter in file 0 56:8 -> 56:15, (#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) +Counter in file 0 56:16 -> 58:6, #14 +Counter in file 0 58:12 -> 60:6, ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14) +Counter in file 0 61:1 -> 61:2, (#14 + ((#13 + ((#12 + ((#11 + (((#9 - #10) + ((((#7 - #8) + ((((#5 + #6) + ((((#3 + #4) + (((#2 + (#1 - #2)) - #3) - #4)) - #5) - #6)) - #7) + #8)) - #9) + #10)) - #11)) - #12)) - #13)) - #14)) +Emitting segments for file: ../coverage/lazy_boolean.rs +Combined regions: + 7:9 -> 9:42 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 14:6 (count=1) + 14:6 -> 14:7 (count=0) + 16:9 -> 16:17 (count=1) + 18:13 -> 18:18 (count=1) + 20:13 -> 20:18 (count=0) + 20:18 -> 20:19 (count=1) + 23:9 -> 23:17 (count=1) + 25:13 -> 25:18 (count=1) + 27:13 -> 27:18 (count=1) + 27:18 -> 27:19 (count=1) + 29:9 -> 29:17 (count=1) + 29:20 -> 29:25 (count=1) + 29:29 -> 29:34 (count=1) + 29:34 -> 29:35 (count=1) + 30:9 -> 30:17 (count=1) + 30:20 -> 30:25 (count=1) + 30:29 -> 30:34 (count=0) + 30:34 -> 30:35 (count=1) + 33:9 -> 34:16 (count=1) + 35:5 -> 38:6 (count=0) + 38:6 -> 38:7 (count=1) + 41:9 -> 41:16 (count=1) + 42:5 -> 45:6 (count=1) + 47:5 -> 50:6 (count=0) + 52:8 -> 52:16 (count=1) + 52:17 -> 54:6 (count=0) + 54:6 -> 54:7 (count=1) + 56:8 -> 56:15 (count=1) + 56:16 -> 58:6 (count=1) + 58:12 -> 60:6 (count=0) + 61:1 -> 61:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:42 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 14:6 (count = 0), RegionEntry +Segment at 14:7 (count = 0), Skipped +Segment at 16:9 (count = 1), RegionEntry +Segment at 16:17 (count = 0), Skipped +Segment at 18:13 (count = 1), RegionEntry +Segment at 18:18 (count = 0), Skipped +Segment at 20:13 (count = 0), RegionEntry +Segment at 20:18 (count = 1), RegionEntry +Segment at 20:19 (count = 0), Skipped +Segment at 23:9 (count = 1), RegionEntry +Segment at 23:17 (count = 0), Skipped +Segment at 25:13 (count = 1), RegionEntry +Segment at 25:18 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 1), RegionEntry +Segment at 27:19 (count = 0), Skipped +Segment at 29:9 (count = 1), RegionEntry +Segment at 29:17 (count = 0), Skipped +Segment at 29:20 (count = 1), RegionEntry +Segment at 29:25 (count = 0), Skipped +Segment at 29:29 (count = 1), RegionEntry +Segment at 29:34 (count = 1), RegionEntry +Segment at 29:35 (count = 0), Skipped +Segment at 30:9 (count = 1), RegionEntry +Segment at 30:17 (count = 0), Skipped +Segment at 30:20 (count = 1), RegionEntry +Segment at 30:25 (count = 0), Skipped +Segment at 30:29 (count = 0), RegionEntry +Segment at 30:34 (count = 1), RegionEntry +Segment at 30:35 (count = 0), Skipped +Segment at 33:9 (count = 1), RegionEntry +Segment at 34:16 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 38:6 (count = 1), RegionEntry +Segment at 38:7 (count = 0), Skipped +Segment at 41:9 (count = 1), RegionEntry +Segment at 41:16 (count = 0), Skipped +Segment at 42:5 (count = 1), RegionEntry +Segment at 45:6 (count = 0), Skipped +Segment at 47:5 (count = 0), RegionEntry +Segment at 50:6 (count = 0), Skipped +Segment at 52:8 (count = 1), RegionEntry +Segment at 52:16 (count = 0), Skipped +Segment at 52:17 (count = 0), RegionEntry +Segment at 54:6 (count = 1), RegionEntry +Segment at 54:7 (count = 0), Skipped +Segment at 56:8 (count = 1), RegionEntry +Segment at 56:15 (count = 0), Skipped +Segment at 56:16 (count = 1), RegionEntry +Segment at 58:6 (count = 0), Skipped +Segment at 58:12 (count = 0), RegionEntry +Segment at 60:6 (count = 0), Skipped +Segment at 61:1 (count = 1), RegionEntry +Segment at 61:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt new file mode 100644 index 0000000000000..a6144b8072ace --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt @@ -0,0 +1,6 @@ +Counter in file 0 3:11 -> 13:2, #1 +Emitting segments for file: ../coverage/loop_break_value.rs +Combined regions: + 3:11 -> 13:2 (count=1) +Segment at 3:11 (count = 1), RegionEntry +Segment at 13:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_branches.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_branches.txt new file mode 100644 index 0000000000000..d8af6998964cf --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_branches.txt @@ -0,0 +1,37 @@ +Counter in file 0 10:12 -> 10:16, #1 +Counter in file 0 11:16 -> 11:21, #2 +Counter in file 0 14:14 -> 14:15, (#2 - #5) +Counter in file 0 15:13 -> 15:31, (0 + (#2 - #5)) +Counter in file 0 15:31 -> 15:32, #4 +Counter in file 0 17:10 -> 17:11, #3 +Counter in file 0 18:9 -> 18:15, (#3 + 0) +Counter in file 0 19:5 -> 19:6, (#4 + (#3 + 0)) +Counter in file 0 22:11 -> 25:2, #1 +Emitting segments for file: ../coverage/loops_branches.rs +Combined regions: + 10:12 -> 10:16 (count=1) + 11:16 -> 11:21 (count=1) + 14:14 -> 14:15 (count=1) + 15:13 -> 15:31 (count=1) + 15:31 -> 15:32 (count=0) + 17:10 -> 17:11 (count=1) + 18:9 -> 18:15 (count=1) + 19:5 -> 19:6 (count=1) + 22:11 -> 25:2 (count=1) +Segment at 10:12 (count = 1), RegionEntry +Segment at 10:16 (count = 0), Skipped +Segment at 11:16 (count = 1), RegionEntry +Segment at 11:21 (count = 0), Skipped +Segment at 14:14 (count = 1), RegionEntry +Segment at 14:15 (count = 0), Skipped +Segment at 15:13 (count = 1), RegionEntry +Segment at 15:31 (count = 0), RegionEntry +Segment at 15:32 (count = 0), Skipped +Segment at 17:10 (count = 1), RegionEntry +Segment at 17:11 (count = 0), Skipped +Segment at 18:9 (count = 1), RegionEntry +Segment at 18:15 (count = 0), Skipped +Segment at 19:5 (count = 1), RegionEntry +Segment at 19:6 (count = 0), Skipped +Segment at 22:11 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt new file mode 100644 index 0000000000000..f503007353319 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt @@ -0,0 +1,73 @@ +Counter in file 0 2:9 -> 3:27, #1 +Counter in file 0 5:19 -> 5:32, (#1 + #2) +Counter in file 0 6:13 -> 7:24, ((#1 + #2) - #3) +Counter in file 0 8:13 -> 8:14, ((((#1 + #2) - #3) + (#5 + #6)) - #7) +Counter in file 0 8:18 -> 8:23, (((#1 + #2) - #3) + (#5 + #6)) +Counter in file 0 9:16 -> 9:22, (((((#1 + #2) - #3) + (#5 + #6)) - #7) + 0) +Counter in file 0 10:17 -> 10:22, #8 +Counter in file 0 12:13 -> 12:19, #9 +Counter in file 0 13:13 -> 13:19, #10 +Counter in file 0 14:16 -> 14:22, (#10 + 0) +Counter in file 0 15:17 -> 16:27, #11 +Counter in file 0 17:21 -> 17:33, #4 +Counter in file 0 19:21 -> 21:14, #5 +Counter in file 0 21:14 -> 21:15, #6 +Counter in file 0 22:10 -> 22:11, (#5 + #6) +Counter in file 0 23:9 -> 23:23, #2 +Counter in file 0 24:6 -> 24:7, #3 +Counter in file 0 25:1 -> 25:2, (#4 + #3) +Emitting segments for file: ../coverage/nested_loops.rs +Combined regions: + 2:9 -> 3:27 (count=1) + 5:19 -> 5:32 (count=1) + 6:13 -> 7:24 (count=1) + 8:13 -> 8:14 (count=3) + 8:18 -> 8:23 (count=3) + 9:16 -> 9:22 (count=3) + 10:17 -> 10:22 (count=0) + 12:13 -> 12:19 (count=3) + 13:13 -> 13:19 (count=3) + 14:16 -> 14:22 (count=3) + 15:17 -> 16:27 (count=1) + 17:21 -> 17:33 (count=1) + 19:21 -> 21:14 (count=0) + 21:14 -> 21:15 (count=2) + 22:10 -> 22:11 (count=2) + 23:9 -> 23:23 (count=0) + 24:6 -> 24:7 (count=0) + 25:1 -> 25:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 3:27 (count = 0), Skipped +Segment at 5:19 (count = 1), RegionEntry +Segment at 5:32 (count = 0), Skipped +Segment at 6:13 (count = 1), RegionEntry +Segment at 7:24 (count = 0), Skipped +Segment at 8:13 (count = 3), RegionEntry +Segment at 8:14 (count = 0), Skipped +Segment at 8:18 (count = 3), RegionEntry +Segment at 8:23 (count = 0), Skipped +Segment at 9:16 (count = 3), RegionEntry +Segment at 9:22 (count = 0), Skipped +Segment at 10:17 (count = 0), RegionEntry +Segment at 10:22 (count = 0), Skipped +Segment at 12:13 (count = 3), RegionEntry +Segment at 12:19 (count = 0), Skipped +Segment at 13:13 (count = 3), RegionEntry +Segment at 13:19 (count = 0), Skipped +Segment at 14:16 (count = 3), RegionEntry +Segment at 14:22 (count = 0), Skipped +Segment at 15:17 (count = 1), RegionEntry +Segment at 16:27 (count = 0), Skipped +Segment at 17:21 (count = 1), RegionEntry +Segment at 17:33 (count = 0), Skipped +Segment at 19:21 (count = 0), RegionEntry +Segment at 21:14 (count = 2), RegionEntry +Segment at 21:15 (count = 0), Skipped +Segment at 22:10 (count = 2), RegionEntry +Segment at 22:11 (count = 0), Skipped +Segment at 23:9 (count = 0), RegionEntry +Segment at 23:23 (count = 0), Skipped +Segment at 24:6 (count = 0), RegionEntry +Segment at 24:7 (count = 0), Skipped +Segment at 25:1 (count = 1), RegionEntry +Segment at 25:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq.txt new file mode 100644 index 0000000000000..2b9285202f97f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq.txt @@ -0,0 +1,53 @@ +Counter in file 0 4:32 -> 4:33, (#3 + (#1 + #2)) +Counter in file 0 4:48 -> 4:49, ((#1 + #2) + ((#3 + #4) + ((#5 + #6) + #7))) +Counter in file 0 21:11 -> 26:2, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:32 -> 4:33, ((#4 + #5) + #6) +Counter in file 0 4:53 -> 4:54, (#1 + (#2 + (#3 + #4))) +Counter in file 0 13:9 -> 18:6, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 4:10 -> 4:15, #1 +Counter in file 0 4:35 -> 4:37, #1 +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 7:5 -> 7:6, #1 +Counter in file 0 4:17 -> 4:22, #1 +Counter in file 0 8:5 -> 8:17, #1 +Counter in file 0 4:39 -> 4:40, #1 +Counter in file 0 4:48 -> 4:49, (#1 + 0) +Counter in file 0 7:5 -> 7:6, #1 +Emitting segments for file: ../coverage/partial_eq.rs +Combined regions: + 4:17 -> 4:22 (count=2) + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) + 7:5 -> 7:6 (count=1) + 13:9 -> 18:6 (count=2) + 21:11 -> 26:2 (count=1) +Segment at 4:17 (count = 2), RegionEntry +Segment at 4:22 (count = 0), Skipped +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped +Segment at 7:5 (count = 1), RegionEntry +Segment at 7:6 (count = 0), Skipped +Segment at 13:9 (count = 2), RegionEntry +Segment at 18:6 (count = 0), Skipped +Segment at 21:11 (count = 1), RegionEntry +Segment at 26:2 (count = 0), Skipped +Emitting segments for function: _RNvXs0_Cs4fqI2P2rA04_10partial_eqNtB5_7VersionNtNtCs7f2nZg1zwMz_4core3cmp10PartialOrd2ltB5_ +Combined regions: + 4:39 -> 4:40 (count=1) + 4:48 -> 4:49 (count=1) +Segment at 4:39 (count = 1), RegionEntry +Segment at 4:40 (count = 0), Skipped +Segment at 4:48 (count = 1), RegionEntry +Segment at 4:49 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt new file mode 100644 index 0000000000000..255173e5534d1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt @@ -0,0 +1,37 @@ +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 12:9 -> 12:16, (#1 + 0) +Counter in file 0 13:5 -> 18:6, #2 +Counter in file 0 18:6 -> 18:7, (#1 - #2) +Counter in file 0 23:13 -> 25:14, ((#2 + (#1 - #2)) + #3) +Counter in file 0 27:13 -> 27:18, #4 +Counter in file 0 30:9 -> 32:10, #3 +Counter in file 0 34:6 -> 34:7, (#2 + (#1 - #2)) +Counter in file 0 35:1 -> 35:2, (#4 + 0) +Emitting segments for file: ../coverage/simple_loop.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 12:9 -> 12:16 (count=1) + 13:5 -> 18:6 (count=1) + 18:6 -> 18:7 (count=0) + 23:13 -> 25:14 (count=11) + 27:13 -> 27:18 (count=1) + 30:9 -> 32:10 (count=10) + 34:6 -> 34:7 (count=1) + 35:1 -> 35:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 12:9 (count = 1), RegionEntry +Segment at 12:16 (count = 0), Skipped +Segment at 13:5 (count = 1), RegionEntry +Segment at 18:6 (count = 0), RegionEntry +Segment at 18:7 (count = 0), Skipped +Segment at 23:13 (count = 11), RegionEntry +Segment at 25:14 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:18 (count = 0), Skipped +Segment at 30:9 (count = 10), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 34:6 (count = 1), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:1 (count = 1), RegionEntry +Segment at 35:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt new file mode 100644 index 0000000000000..1682a379bc0ff --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt @@ -0,0 +1,57 @@ +Counter in file 0 7:9 -> 9:26, #1 +Counter in file 0 10:8 -> 10:15, (#1 + 0) +Counter in file 0 10:16 -> 12:6, #2 +Counter in file 0 12:6 -> 12:7, (#1 - #2) +Counter in file 0 15:9 -> 15:10, (((#2 + (#1 - #2)) + (#3 + #4)) - #5) +Counter in file 0 17:9 -> 17:13, ((#2 + (#1 - #2)) + (#3 + #4)) +Counter in file 0 22:13 -> 22:22, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 24:13 -> 24:14, #3 +Counter in file 0 26:17 -> 28:18, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) + 0) +Counter in file 0 28:18 -> 28:19, ((((#2 + (#1 - #2)) + (#3 + #4)) - #5) - #3) +Counter in file 0 30:13 -> 37:14, (#3 + 0) +Counter in file 0 40:13 -> 40:15, #4 +Counter in file 0 42:6 -> 42:7, (#2 + (#1 - #2)) +Counter in file 0 42:6 -> 42:7, (#3 + #4) +Counter in file 0 43:1 -> 43:2, #5 +Emitting segments for file: ../coverage/simple_match.rs +Combined regions: + 7:9 -> 9:26 (count=1) + 10:8 -> 10:15 (count=1) + 10:16 -> 12:6 (count=1) + 12:6 -> 12:7 (count=0) + 15:9 -> 15:10 (count=2) + 17:9 -> 17:13 (count=3) + 22:13 -> 22:22 (count=2) + 24:13 -> 24:14 (count=1) + 26:17 -> 28:18 (count=2) + 28:18 -> 28:19 (count=1) + 30:13 -> 37:14 (count=1) + 40:13 -> 40:15 (count=1) + 42:6 -> 42:7 (count=3) + 43:1 -> 43:2 (count=1) +Segment at 7:9 (count = 1), RegionEntry +Segment at 9:26 (count = 0), Skipped +Segment at 10:8 (count = 1), RegionEntry +Segment at 10:15 (count = 0), Skipped +Segment at 10:16 (count = 1), RegionEntry +Segment at 12:6 (count = 0), RegionEntry +Segment at 12:7 (count = 0), Skipped +Segment at 15:9 (count = 2), RegionEntry +Segment at 15:10 (count = 0), Skipped +Segment at 17:9 (count = 3), RegionEntry +Segment at 17:13 (count = 0), Skipped +Segment at 22:13 (count = 2), RegionEntry +Segment at 22:22 (count = 0), Skipped +Segment at 24:13 (count = 1), RegionEntry +Segment at 24:14 (count = 0), Skipped +Segment at 26:17 (count = 2), RegionEntry +Segment at 28:18 (count = 1), RegionEntry +Segment at 28:19 (count = 0), Skipped +Segment at 30:13 (count = 1), RegionEntry +Segment at 37:14 (count = 0), Skipped +Segment at 40:13 (count = 1), RegionEntry +Segment at 40:15 (count = 0), Skipped +Segment at 42:6 (count = 3), RegionEntry +Segment at 42:7 (count = 0), Skipped +Segment at 43:1 (count = 1), RegionEntry +Segment at 43:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_inf_loop.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_inf_loop.txt new file mode 100644 index 0000000000000..5887658fe67a2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_inf_loop.txt @@ -0,0 +1,10 @@ +Counter in file 0 2:8 -> 2:13, #1 +Counter in file 0 5:1 -> 5:2, (#1 - #2) +Emitting segments for file: ../coverage/tight_inf_loop.rs +Combined regions: + 2:8 -> 2:13 (count=1) + 5:1 -> 5:2 (count=1) +Segment at 2:8 (count = 1), RegionEntry +Segment at 2:13 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt new file mode 100644 index 0000000000000..a317cd792910d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt @@ -0,0 +1,72 @@ +Counter in file 0 13:9 -> 14:23, #1 +Counter in file 0 17:9 -> 17:10, ((#1 + (#2 + #3)) - #4) +Counter in file 0 19:9 -> 19:14, (#1 + (#2 + #3)) +Counter in file 0 21:9 -> 25:26, #8 +Counter in file 0 27:13 -> 27:41, #9 +Counter in file 0 27:41 -> 27:42, #5 +Counter in file 0 28:13 -> 28:42, (#9 - #5) +Counter in file 0 28:42 -> 28:43, #6 +Counter in file 0 32:13 -> 32:42, (#8 - #9) +Counter in file 0 32:42 -> 32:43, #7 +Counter in file 0 33:10 -> 33:11, #2 +Counter in file 0 33:10 -> 33:11, #3 +Counter in file 0 34:6 -> 34:7, (#2 + #3) +Counter in file 0 35:5 -> 35:11, #4 +Counter in file 0 36:1 -> 36:2, ((#5 + (#6 + #7)) + #4) +Counter in file 0 5:8 -> 5:20, #1 +Counter in file 0 6:9 -> 6:16, #2 +Counter in file 0 8:9 -> 8:15, (#1 - #2) +Counter in file 0 10:1 -> 10:2, (#2 + (#1 - #2)) +Emitting segments for file: ../coverage/try_error_result.rs +Combined regions: + 5:8 -> 5:20 (count=6) + 6:9 -> 6:16 (count=1) + 8:9 -> 8:15 (count=5) + 10:1 -> 10:2 (count=6) + 13:9 -> 14:23 (count=1) + 17:9 -> 17:10 (count=6) + 19:9 -> 19:14 (count=6) + 21:9 -> 25:26 (count=6) + 27:13 -> 27:41 (count=1) + 27:41 -> 27:42 (count=1) + 28:13 -> 28:42 (count=0) + 28:42 -> 28:43 (count=0) + 32:13 -> 32:42 (count=5) + 32:42 -> 32:43 (count=0) + 33:10 -> 33:11 (count=5) + 34:6 -> 34:7 (count=5) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=1) +Segment at 5:8 (count = 6), RegionEntry +Segment at 5:20 (count = 0), Skipped +Segment at 6:9 (count = 1), RegionEntry +Segment at 6:16 (count = 0), Skipped +Segment at 8:9 (count = 5), RegionEntry +Segment at 8:15 (count = 0), Skipped +Segment at 10:1 (count = 6), RegionEntry +Segment at 10:2 (count = 0), Skipped +Segment at 13:9 (count = 1), RegionEntry +Segment at 14:23 (count = 0), Skipped +Segment at 17:9 (count = 6), RegionEntry +Segment at 17:10 (count = 0), Skipped +Segment at 19:9 (count = 6), RegionEntry +Segment at 19:14 (count = 0), Skipped +Segment at 21:9 (count = 6), RegionEntry +Segment at 25:26 (count = 0), Skipped +Segment at 27:13 (count = 1), RegionEntry +Segment at 27:41 (count = 1), RegionEntry +Segment at 27:42 (count = 0), Skipped +Segment at 28:13 (count = 0), RegionEntry +Segment at 28:42 (count = 0), RegionEntry +Segment at 28:43 (count = 0), Skipped +Segment at 32:13 (count = 5), RegionEntry +Segment at 32:42 (count = 0), RegionEntry +Segment at 32:43 (count = 0), Skipped +Segment at 33:10 (count = 5), RegionEntry +Segment at 33:11 (count = 0), Skipped +Segment at 34:6 (count = 5), RegionEntry +Segment at 34:7 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 1), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt new file mode 100644 index 0000000000000..b0e881da7c8cb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt @@ -0,0 +1,18 @@ +Counter in file 0 2:9 -> 2:16, #1 +Counter in file 0 3:11 -> 3:20, (#1 + #2) +Counter in file 0 3:21 -> 4:6, #2 +Counter in file 0 5:1 -> 5:2, ((#1 + #2) - #2) +Emitting segments for file: ../coverage/while.rs +Combined regions: + 2:9 -> 2:16 (count=1) + 3:11 -> 3:20 (count=1) + 3:21 -> 4:6 (count=0) + 5:1 -> 5:2 (count=1) +Segment at 2:9 (count = 1), RegionEntry +Segment at 2:16 (count = 0), Skipped +Segment at 3:11 (count = 1), RegionEntry +Segment at 3:20 (count = 0), Skipped +Segment at 3:21 (count = 0), RegionEntry +Segment at 4:6 (count = 0), Skipped +Segment at 5:1 (count = 1), RegionEntry +Segment at 5:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_ret.txt b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_ret.txt new file mode 100644 index 0000000000000..f541baec50c0b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_ret.txt @@ -0,0 +1,38 @@ +Counter in file 0 5:9 -> 5:27, #1 +Counter in file 0 7:9 -> 9:10, (#1 + #2) +Counter in file 0 12:13 -> 14:14, ((#1 + #2) - #3) +Counter in file 0 18:21 -> 20:22, #6 +Counter in file 0 22:21 -> 22:27, #4 +Counter in file 0 26:21 -> 26:27, #5 +Counter in file 0 30:9 -> 32:10, #2 +Counter in file 0 35:5 -> 35:11, #3 +Counter in file 0 36:1 -> 36:2, ((#4 + #5) + #3) +Emitting segments for file: ../coverage/while_early_ret.rs +Combined regions: + 5:9 -> 5:27 (count=1) + 7:9 -> 9:10 (count=7) + 12:13 -> 14:14 (count=7) + 18:21 -> 20:22 (count=1) + 22:21 -> 22:27 (count=0) + 26:21 -> 26:27 (count=1) + 30:9 -> 32:10 (count=6) + 35:5 -> 35:11 (count=0) + 36:1 -> 36:2 (count=1) +Segment at 5:9 (count = 1), RegionEntry +Segment at 5:27 (count = 0), Skipped +Segment at 7:9 (count = 7), RegionEntry +Segment at 9:10 (count = 0), Skipped +Segment at 12:13 (count = 7), RegionEntry +Segment at 14:14 (count = 0), Skipped +Segment at 18:21 (count = 1), RegionEntry +Segment at 20:22 (count = 0), Skipped +Segment at 22:21 (count = 0), RegionEntry +Segment at 22:27 (count = 0), Skipped +Segment at 26:21 (count = 1), RegionEntry +Segment at 26:27 (count = 0), Skipped +Segment at 30:9 (count = 6), RegionEntry +Segment at 32:10 (count = 0), Skipped +Segment at 35:5 (count = 0), RegionEntry +Segment at 35:11 (count = 0), Skipped +Segment at 36:1 (count = 1), RegionEntry +Segment at 36:2 (count = 0), Skipped diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/Makefile b/src/test/run-make-fulldeps/coverage-spanview-base/Makefile index fa2f4ec394e2c..fb9f5215fe8ee 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/Makefile +++ b/src/test/run-make-fulldeps/coverage-spanview-base/Makefile @@ -6,8 +6,13 @@ -include ../coverage/coverage_tools.mk +BASEDIR=../coverage-spanview-base SOURCEDIR=../coverage +ifeq ($(UNAME),Darwin) +SED_HAS_ISSUES=1 +endif + all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) # Ensure there are no `expected` results for tests that may have been removed or renamed @@ -25,8 +30,36 @@ endif -Zinstrument-coverage \ -Clink-dead-code=$(LINK_DEAD_CODE) \ -Zdump-mir=InstrumentCoverage \ + -Zdump-mir-spanview \ -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@ +ifdef SED_HAS_ISSUES + # FIXME(richkadel): MacOS's default sed has some significant limitations. Until I've come up + # with a better workaround, I'm disabling this test for MacOS. + # + # For future reference, see if `gsed` is available as an alternative. + which gsed || echo "no gsed" +else + + for path in "$(TMPDIR)"/mir_dump.$@/*; do \ + echo $$path; \ + file="$$(basename "$$path")"; \ + echo $$file; \ + urlescaped="$$("$(PYTHON)" $(BASEDIR)/escape_url.py $$file)" || exit $$?; \ + echo $$urlescaped; \ + sed -i -e '1a\ +' "$$path"; \ + done && true # for/done ends in non-zero status + ifdef RUSTC_BLESS_TEST mkdir -p expected_mir_dump.$@ cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ @@ -37,3 +70,5 @@ else cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ endif + +endif diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py b/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py new file mode 100644 index 0000000000000..b725ed4630302 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys + +# Support python 2 or 3 +try: + from urllib.parse import quote +except ImportError: + from urllib import quote + +# Converts the input string into a valid URL parameter string. +print (quote(' '.join(sys.argv[1:]))) diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html index 43f75c574d0ee..2d179aa0f1147 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + closure.main-{closure#0} - Coverage Spans @@ -65,16 +75,19 @@ 35:13-35:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0
;
if @0⦊is_false⦉@0 @1,3⦊{ +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4">@1,3⦊{
countdown = 10; +36:21-38:10: @3[1]: _3 = const () +38:10-38:10: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊⦉@2 + @4,5⦊"alt string 2".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html index 8f07ec5fcde66..58e81a221d3a8 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + closure.main-{closure#1} - Coverage Spans @@ -65,16 +75,19 @@ 77:13-77:26: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +78:21-80:10: @3[1]: _3 = const () +80:10-80:10: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊⦉@2 + @4,5⦊"alt string 4".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html index ca9031a1094a4..0614da6cee27f 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + closure.main-{closure#2} - Coverage Spans @@ -65,16 +75,19 @@ 20:21-20:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +21:29-23:18: @3[1]: _3 = const () +23:18-23:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊⦉@2 + @4,5⦊"alt string 1".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html index 820f8d9c6cf8f..bbafb44017cf9 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + closure.main-{closure#3} - Coverage Spans @@ -65,16 +75,19 @@ 62:21-62:34: @0[2]: FakeRead(ForLet, _2)">@0⦊mut countdown = 0⦉@0; if @0⦊is_false⦉@0 @1,3⦊{ +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4">@1,3⦊{ countdown = 10; +63:29-65:18: @3[1]: _3 = const () +65:18-65:18: @3.Goto: goto -> bb4"> countdown = 10; }⦉@1,3 - }⦉@1,3@2⦊⦉@2 + @4,5⦊"alt string 3".to_owned() - }⦉@4,5 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html index f70576ca24e94..8d1938c0922be 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + closure.main - Coverage Spans diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html similarity index 56% rename from src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html rename to src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html index 28f1d013c835f..cda1040c29114 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.various_conditions/various_conditions.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html @@ -1,7 +1,17 @@ + -various_conditions.main - Coverage Spans +conditions.main - Coverage Spans + + +
fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..334a64b0bb8f9 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +loops_branches.{impl#0}-fmt - Coverage Spans + + + +
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊⦉@9@5⦊⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊⦉@22
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..7fbda5d0b3d2c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,135 @@ + + + + +nested_loops.main - Coverage Spans + + + +
fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊⦉@23 + }@30⦊⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊⦉@7 +}@33⦊⦉@33
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a7830911376bf --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,295 @@ + + + + +partial_eq.main - Coverage Spans + + + +
fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a3c98e97bbd87 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +partial_eq.{impl#0}-new - Coverage Spans + + + +
pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..1a40b30099da6 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,76 @@ + + + + +partial_eq.{impl#1}-cmp - Coverage Spans + + + +
@14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊⦉@15
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..0fa59ade1398d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..5399895992e11 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..044dd7eb9f577 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-ge - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..30047ab79725b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..00b5d91b1cf91 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..f1f78e4663949 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-gt - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..e8da313d18e08 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..91bd3b4af8c2b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-le-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49ebcfaafb54e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-le - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..55438a463c420 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..812dc622ec038 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..742b45773232b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-lt - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3714c774bae3f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + + +partial_eq.{impl#2}-partial_cmp - Coverage Spans + + + +
@17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊⦉@18
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..b908a35f1b799 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,75 @@ + + + + +partial_eq.{impl#4}-assert_receiver_is_total_eq - Coverage Spans + + + +
@0⦊Eq⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..c7970e368638b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,77 @@ + + + + +partial_eq.{impl#6}-eq - Coverage Spans + + + +
@2⦊@1⦊PartialEq⦉@1⦉@2@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..be1f7f7d2e726 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +partial_eq.{impl#6}-ne - Coverage Spans + + + +
@2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..65b95bae78d6e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,109 @@ + + + + +partial_eq.{impl#7}-fmt - Coverage Spans + + + +
@0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a61d3cb8bdcb3 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,88 @@ + + + + +partial_eq.{impl#8}-clone - Coverage Spans + + + +
@0,1,2,3⦊Clone⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 4b21d3fc263a3..914e829faa0bd 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + simple_loop.main - Coverage Spans @@ -89,39 +99,45 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7">@4,6⦊{
countdown +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> countdown = +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> = 10 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> 10 ; +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊⦉@5 loop { if - @8,9⦊countdown - == - 0⦉@8,9 { - @10,12⦊break⦉@10,12 + @10,12⦊break⦉@10,12 ; } - @11⦊countdown - -= - 1⦉@11 + @13⦊countdown + -= + 1⦉@13 ; - } + }@7⦊⦉@7 }@10,12⦊⦉@10,12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index 5ba770ef6078e..2488ac563e77e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + simple_match.main - Coverage Spans @@ -86,51 +96,39 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 1⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 0; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 0; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊⦉@5 - @9,10,11⦊for - _⦉@9,10,11 + for + @13,15,17⦊_⦉@13,15,17 in - @7,8⦊0..2⦉@7,8 + @9,10,11⦊0..2⦉@9,10,11 { let z ; match - @13,15,17⦊countdown - { - x - if - x - @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < - 1⦉@13,15,17 + 1⦉@13,15,17@19⦊⦉@19 => => @16⦊{}⦉@16 } - } + }@7,8⦊⦉@7,8@20⦊⦉@20 }@12⦊⦉@12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..033707a87bb69 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,80 @@ + + + + +tight_inf_loop.main - Coverage Spans + + + +
fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊⦉@1,3 + } +}@2⦊⦉@2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html index 9f9933423406e..dfe2eb073aa18 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + try_error_result.call - Coverage Spans diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html index 660c3031f3592..5c9baee5d8050 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + try_error_result.main - Coverage Spans @@ -65,37 +75,54 @@ countdown = 10⦉@0,1
; - @2,3,4⦊for - _⦉@2,3,4 + for + @6,8⦊_⦉@6,8 in - @0,1⦊0..10⦉@0,1 + @2,3,4⦊0..10⦉@2,3,4 { - @6,8⦊countdown - -= 1⦉@6,8 - ; - if - @9⦊countdown < 5⦉@9 +25:13-25:26: @9[6]: FakeRead(ForMatchedPlace, _18)">@9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9
{ - @10,12,13,14⦊call(/*return_error=*/ true)?⦉@10,12,13,14; + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + @15,21,22⦊call(/*return_error=*/ false)⦉@15,21,22@24,26,27,28⦊?⦉@24,26,27,28; } else { - @11,21,22⦊call(/*return_error=*/ false)?⦉@11,21,22; - } - } - @5⦊Ok(())⦉@5 -}@31⦊⦉@31 + @11,29,30⦊call(/*return_error=*/ false)⦉@11,29,30@32,34,35,36⦊?⦉@32,34,35,36; + }@23⦊⦉@23@31⦊⦉@31 + }@37⦊⦉@37 + @5⦊Ok(())⦉@5 +}@38⦊⦉@38@39⦊⦉@39 diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3ba5135122b93 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +while.main - Coverage Spans + + + +
fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html similarity index 83% rename from src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html rename to src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html index b96789a92194d..765e3b62c11c2 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html @@ -1,7 +1,17 @@ + -while_early_return.main - Coverage Spans +while_early_ret.main - Coverage Spans + + +
fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..334a64b0bb8f9 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +loops_branches.{impl#0}-fmt - Coverage Spans + + + +
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊⦉@9@5⦊⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊⦉@22
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..7fbda5d0b3d2c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,135 @@ + + + + +nested_loops.main - Coverage Spans + + + +
fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊⦉@23 + }@30⦊⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊⦉@7 +}@33⦊⦉@33
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a7830911376bf --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,295 @@ + + + + +partial_eq.main - Coverage Spans + + + +
fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a3c98e97bbd87 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +partial_eq.{impl#0}-new - Coverage Spans + + + +
pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..1a40b30099da6 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,76 @@ + + + + +partial_eq.{impl#1}-cmp - Coverage Spans + + + +
@14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊⦉@15
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..0fa59ade1398d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..5399895992e11 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..044dd7eb9f577 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-ge - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..30047ab79725b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..00b5d91b1cf91 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..f1f78e4663949 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-gt - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..e8da313d18e08 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..91bd3b4af8c2b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-le-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..49ebcfaafb54e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-le - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..55438a463c420 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..812dc622ec038 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
major: usize, + @0,1,2,3⦊⦉@0,1,2,3minor: usize
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..742b45773232b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-lt - Coverage Spans + + + +
@0,1,2,3,4⦊⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊⦉@0,1,2,3,4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3714c774bae3f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + + +partial_eq.{impl#2}-partial_cmp - Coverage Spans + + + +
@17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊⦉@18
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..b908a35f1b799 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,75 @@ + + + + +partial_eq.{impl#4}-assert_receiver_is_total_eq - Coverage Spans + + + +
@0⦊Eq⦉@0
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..c7970e368638b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,77 @@ + + + + +partial_eq.{impl#6}-eq - Coverage Spans + + + +
@2⦊@1⦊PartialEq⦉@1⦉@2@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..be1f7f7d2e726 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +partial_eq.{impl#6}-ne - Coverage Spans + + + +
@2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..65b95bae78d6e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,109 @@ + + + + +partial_eq.{impl#7}-fmt - Coverage Spans + + + +
@0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..a61d3cb8bdcb3 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,88 @@ + + + + +partial_eq.{impl#8}-clone - Coverage Spans + + + +
@0,1,2,3⦊Clone⦉@0,1,2,3
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html index 4b21d3fc263a3..914e829faa0bd 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + simple_loop.main - Coverage Spans @@ -89,39 +99,45 @@ @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7">@4,6⦊{
countdown +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> countdown = +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> = 10 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> 10 ; +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> ; }⦉@4,6 +13:5-18:6: @6[1]: _6 = const () +18:6-18:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊⦉@5 loop { if - @8,9⦊countdown - == - 0⦉@8,9 { - @10,12⦊break⦉@10,12 + @10,12⦊break⦉@10,12 ; } - @11⦊countdown - -= - 1⦉@11 + @13⦊countdown + -= + 1⦉@13 ; - } + }@7⦊⦉@7 }@10,12⦊⦉@10,12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html index 5ba770ef6078e..2488ac563e77e 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + simple_match.main - Coverage Spans @@ -86,51 +96,39 @@ 9:9-9:22: @3[3]: FakeRead(ForLet, _5)"> let mut countdown = 1⦉@0,1,2,3; if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7">@4,6⦊{ countdown = 0; +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> countdown = 0; }⦉@4,6 +10:16-12:6: @6[1]: _6 = const () +12:6-12:6: @6.Goto: goto -> bb7"> }⦉@4,6@5⦊⦉@5 - @9,10,11⦊for - _⦉@9,10,11 + for + @13,15,17⦊_⦉@13,15,17 in - @7,8⦊0..2⦉@7,8 + @9,10,11⦊0..2⦉@9,10,11 { let z ; match - @13,15,17⦊countdown - { - x - if - x - @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < - 1⦉@13,15,17 + 1⦉@13,15,17@19⦊⦉@19 => => @16⦊{}⦉@16 } - } + }@7,8⦊⦉@7,8@20⦊⦉@20 }@12⦊⦉@12 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..033707a87bb69 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,80 @@ + + + + +tight_inf_loop.main - Coverage Spans + + + +
fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊⦉@1,3 + } +}@2⦊⦉@2
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html index 9f9933423406e..dfe2eb073aa18 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + try_error_result.call - Coverage Spans diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html index 660c3031f3592..5c9baee5d8050 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -1,4 +1,14 @@ + try_error_result.main - Coverage Spans @@ -65,37 +75,54 @@ countdown = 10⦉@0,1
; - @2,3,4⦊for - _⦉@2,3,4 + for + @6,8⦊_⦉@6,8 in - @0,1⦊0..10⦉@0,1 + @2,3,4⦊0..10⦉@2,3,4 { - @6,8⦊countdown - -= 1⦉@6,8 - ; - if - @9⦊countdown < 5⦉@9 +25:13-25:26: @9[6]: FakeRead(ForMatchedPlace, _18)">@9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9
{ - @10,12,13,14⦊call(/*return_error=*/ true)?⦉@10,12,13,14; + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + @15,21,22⦊call(/*return_error=*/ false)⦉@15,21,22@24,26,27,28⦊?⦉@24,26,27,28; } else { - @11,21,22⦊call(/*return_error=*/ false)?⦉@11,21,22; - } - } - @5⦊Ok(())⦉@5 -}@31⦊⦉@31 + @11,29,30⦊call(/*return_error=*/ false)⦉@11,29,30@32,34,35,36⦊?⦉@32,34,35,36; + }@23⦊⦉@23@31⦊⦉@31 + }@37⦊⦉@37 + @5⦊Ok(())⦉@5 +}@38⦊⦉@38@39⦊⦉@39 diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000000..3ba5135122b93 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +while.main - Coverage Spans + + + +
fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊⦉@4
+ + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html similarity index 83% rename from src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html rename to src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html index b96789a92194d..765e3b62c11c2 100644 --- a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_return/while_early_return.main.-------.InstrumentCoverage.0.html +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html @@ -1,7 +1,17 @@ + -while_early_return.main - Coverage Spans +while_early_ret.main - Coverage Spans