rustc_codegen_llvm/coverageinfo/mapgen/
spans.rs
1use rustc_middle::mir::coverage::FunctionCoverageInfo;
2use rustc_span::source_map::SourceMap;
3use rustc_span::{BytePos, Pos, SourceFile, Span};
4use tracing::debug;
5
6use crate::coverageinfo::ffi;
7use crate::coverageinfo::mapgen::LocalFileId;
8
9pub(crate) fn make_coverage_span(
20 file_id: LocalFileId,
21 source_map: &SourceMap,
22 fn_cov_info: &FunctionCoverageInfo,
23 file: &SourceFile,
24 span: Span,
25) -> Option<ffi::CoverageSpan> {
26 let span = ensure_non_empty_span(source_map, fn_cov_info, span)?;
27
28 let lo = span.lo();
29 let hi = span.hi();
30
31 let line_and_byte_column = |pos: BytePos| -> Option<(usize, usize)> {
34 let rpos = file.relative_position(pos);
35 let line_index = file.lookup_line(rpos)?;
36 let line_start = file.lines()[line_index];
37 Some((line_index + 1, (rpos - line_start).to_usize() + 1))
39 };
40
41 let (mut start_line, start_col) = line_and_byte_column(lo)?;
42 let (mut end_line, end_col) = line_and_byte_column(hi)?;
43
44 start_line = source_map.doctest_offset_line(&file.name, start_line);
47 end_line = source_map.doctest_offset_line(&file.name, end_line);
48
49 check_coverage_span(ffi::CoverageSpan {
50 file_id: file_id.as_u32(),
51 start_line: start_line as u32,
52 start_col: start_col as u32,
53 end_line: end_line as u32,
54 end_col: end_col as u32,
55 })
56}
57
58fn ensure_non_empty_span(
59 source_map: &SourceMap,
60 fn_cov_info: &FunctionCoverageInfo,
61 span: Span,
62) -> Option<Span> {
63 if !span.is_empty() {
64 return Some(span);
65 }
66
67 let lo = span.lo();
68 let hi = span.hi();
69
70 let try_next = hi < fn_cov_info.body_span.hi();
73 let try_prev = fn_cov_info.body_span.lo() < lo;
74 if !(try_next || try_prev) {
75 return None;
76 }
77
78 source_map
79 .span_to_source(span, |src, start, end| try {
80 if try_next && src.as_bytes()[end] == b'{' {
85 Some(span.with_hi(hi + BytePos(1)))
86 } else if try_prev && src.as_bytes()[start - 1] == b'}' {
87 Some(span.with_lo(lo - BytePos(1)))
88 } else {
89 None
90 }
91 })
92 .ok()?
93}
94
95fn check_coverage_span(cov_span: ffi::CoverageSpan) -> Option<ffi::CoverageSpan> {
100 let ffi::CoverageSpan { file_id: _, start_line, start_col, end_line, end_col } = cov_span;
101
102 let all_nonzero = [start_line, start_col, end_line, end_col].into_iter().all(|x| x != 0);
105 let end_col_has_high_bit_unset = (end_col & (1 << 31)) == 0;
108 let is_ordered = (start_line, start_col) <= (end_line, end_col);
111
112 if all_nonzero && end_col_has_high_bit_unset && is_ordered {
113 Some(cov_span)
114 } else {
115 debug!(
116 ?cov_span,
117 ?all_nonzero,
118 ?end_col_has_high_bit_unset,
119 ?is_ordered,
120 "Skipping source region that would be misinterpreted or rejected by LLVM"
121 );
122 debug_assert!(false, "Improper source region: {cov_span:?}");
124 None
125 }
126}