@@ -11,6 +11,7 @@ use rustc_abi::Align;
1111use rustc_codegen_ssa:: traits:: {
1212 BaseTypeCodegenMethods as _, ConstCodegenMethods , StaticCodegenMethods ,
1313} ;
14+ use rustc_index:: IndexVec ;
1415use rustc_middle:: mir:: coverage:: {
1516 BasicCoverageBlock , CovTerm , CoverageIdsInfo , Expression , FunctionCoverageInfo , Mapping ,
1617 MappingKind , Op ,
@@ -104,6 +105,16 @@ fn fill_region_tables<'tcx>(
104105 ids_info : & ' tcx CoverageIdsInfo ,
105106 covfun : & mut CovfunRecord < ' tcx > ,
106107) {
108+ // If this function is unused, replace all counters with zero.
109+ let counter_for_bcb = |bcb : BasicCoverageBlock | -> ffi:: Counter {
110+ let term = if covfun. is_used {
111+ ids_info. term_for_bcb [ bcb] . expect ( "every BCB in a mapping was given a term" )
112+ } else {
113+ CovTerm :: Zero
114+ } ;
115+ ffi:: Counter :: from_term ( term)
116+ } ;
117+
107118 // Currently a function's mappings must all be in the same file, so use the
108119 // first mapping's span to determine the file.
109120 let source_map = tcx. sess . source_map ( ) ;
@@ -115,6 +126,12 @@ fn fill_region_tables<'tcx>(
115126
116127 let local_file_id = covfun. virtual_file_mapping . push_file ( & source_file) ;
117128
129+ // If this testing flag is set, add an extra unused entry to the local
130+ // file table, to help test the code for detecting unused file IDs.
131+ if tcx. sess . coverage_inject_unused_local_file ( ) {
132+ covfun. virtual_file_mapping . push_file ( & source_file) ;
133+ }
134+
118135 // In rare cases, _all_ of a function's spans are discarded, and coverage
119136 // codegen needs to handle that gracefully to avoid #133606.
120137 // It's hard for tests to trigger this organically, so instead we set
@@ -135,16 +152,6 @@ fn fill_region_tables<'tcx>(
135152 // For each counter/region pair in this function+file, convert it to a
136153 // form suitable for FFI.
137154 for & Mapping { ref kind, span } in & fn_cov_info. mappings {
138- // If this function is unused, replace all counters with zero.
139- let counter_for_bcb = |bcb : BasicCoverageBlock | -> ffi:: Counter {
140- let term = if covfun. is_used {
141- ids_info. term_for_bcb [ bcb] . expect ( "every BCB in a mapping was given a term" )
142- } else {
143- CovTerm :: Zero
144- } ;
145- ffi:: Counter :: from_term ( term)
146- } ;
147-
148155 let Some ( coords) = make_coords ( span) else { continue } ;
149156 let cov_span = coords. make_coverage_span ( local_file_id) ;
150157
@@ -177,6 +184,19 @@ fn fill_region_tables<'tcx>(
177184 }
178185}
179186
187+ /// LLVM requires all local file IDs to have at least one mapping region.
188+ /// If that's not the case, skip this function, to avoid an assertion failure
189+ /// (or worse) in LLVM.
190+ fn check_local_file_table ( covfun : & CovfunRecord < ' _ > ) -> bool {
191+ let mut local_file_id_seen =
192+ IndexVec :: < u32 , _ > :: from_elem_n ( false , covfun. virtual_file_mapping . local_file_table . len ( ) ) ;
193+ for cov_span in covfun. regions . all_cov_spans ( ) {
194+ local_file_id_seen[ cov_span. file_id ] = true ;
195+ }
196+
197+ local_file_id_seen. into_iter ( ) . all ( |seen| seen)
198+ }
199+
180200/// Generates the contents of the covfun record for this function, which
181201/// contains the function's coverage mapping data. The record is then stored
182202/// as a global variable in the `__llvm_covfun` section.
@@ -185,6 +205,10 @@ pub(crate) fn generate_covfun_record<'tcx>(
185205 global_file_table : & GlobalFileTable ,
186206 covfun : & CovfunRecord < ' tcx > ,
187207) {
208+ if !check_local_file_table ( covfun) {
209+ return ;
210+ }
211+
188212 let & CovfunRecord {
189213 mangled_function_name,
190214 source_hash,
0 commit comments