Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3fc6dfd

Browse files
authoredFeb 19, 2025
Rollup merge of rust-lang#137251 - Zalathar:holes-visitor, r=jieyouxu
coverage: Get hole spans from nested items without fully visiting them This is a small simplification to the code that collects the spans of nested items within a function, so that those spans can be treated as “holes” to be avoided by the current function's coverage mappings. The old code was using `nested_filter::All` to ensure that the visitor would see nested items. But we don't need the actual items themselves; we just need their spans, which we can obtain via a custom implementation of `visit_nested_item`. This avoids the more expansive queries required by `nested_filter::All`.
2 parents dd60b6c + d38f688 commit 3fc6dfd

File tree

4 files changed

+114
-42
lines changed

4 files changed

+114
-42
lines changed
 

‎compiler/rustc_mir_transform/src/coverage/mod.rs

+26-25
Original file line numberDiff line numberDiff line change
@@ -346,35 +346,37 @@ fn extract_hole_spans_from_hir<'tcx>(
346346
body_span: Span, // Usually `hir_body.value.span`, but not always
347347
hir_body: &hir::Body<'tcx>,
348348
) -> Vec<Span> {
349-
struct HolesVisitor<'hir, F> {
350-
tcx: TyCtxt<'hir>,
351-
visit_hole_span: F,
349+
struct HolesVisitor<'tcx> {
350+
tcx: TyCtxt<'tcx>,
351+
body_span: Span,
352+
hole_spans: Vec<Span>,
352353
}
353354

354-
impl<'hir, F: FnMut(Span)> Visitor<'hir> for HolesVisitor<'hir, F> {
355-
/// - We need `NestedFilter::INTRA = true` so that `visit_item` will be called.
356-
/// - Bodies of nested items don't actually get visited, because of the
357-
/// `visit_item` override.
358-
/// - For nested bodies that are not part of an item, we do want to visit any
359-
/// items contained within them.
360-
type NestedFilter = nested_filter::All;
355+
impl<'tcx> Visitor<'tcx> for HolesVisitor<'tcx> {
356+
/// We have special handling for nested items, but we still want to
357+
/// traverse into nested bodies of things that are not considered items,
358+
/// such as "anon consts" (e.g. array lengths).
359+
type NestedFilter = nested_filter::OnlyBodies;
361360

362-
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
361+
fn maybe_tcx(&mut self) -> TyCtxt<'tcx> {
363362
self.tcx
364363
}
365364

366-
fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
367-
(self.visit_hole_span)(item.span);
365+
/// We override `visit_nested_item` instead of `visit_item` because we
366+
/// only need the item's span, not the item itself.
367+
fn visit_nested_item(&mut self, id: hir::ItemId) -> Self::Result {
368+
let span = self.tcx.def_span(id.owner_id.def_id);
369+
self.visit_hole_span(span);
368370
// Having visited this item, we don't care about its children,
369371
// so don't call `walk_item`.
370372
}
371373

372374
// We override `visit_expr` instead of the more specific expression
373375
// visitors, so that we have direct access to the expression span.
374-
fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
376+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
375377
match expr.kind {
376378
hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => {
377-
(self.visit_hole_span)(expr.span);
379+
self.visit_hole_span(expr.span);
378380
// Having visited this expression, we don't care about its
379381
// children, so don't call `walk_expr`.
380382
}
@@ -384,18 +386,17 @@ fn extract_hole_spans_from_hir<'tcx>(
384386
}
385387
}
386388
}
387-
388-
let mut hole_spans = vec![];
389-
let mut visitor = HolesVisitor {
390-
tcx,
391-
visit_hole_span: |hole_span| {
389+
impl HolesVisitor<'_> {
390+
fn visit_hole_span(&mut self, hole_span: Span) {
392391
// Discard any holes that aren't directly visible within the body span.
393-
if body_span.contains(hole_span) && body_span.eq_ctxt(hole_span) {
394-
hole_spans.push(hole_span);
392+
if self.body_span.contains(hole_span) && self.body_span.eq_ctxt(hole_span) {
393+
self.hole_spans.push(hole_span);
395394
}
396-
},
397-
};
395+
}
396+
}
397+
398+
let mut visitor = HolesVisitor { tcx, body_span, hole_spans: vec![] };
398399

399400
visitor.visit_body(hir_body);
400-
hole_spans
401+
visitor.hole_spans
401402
}

‎tests/coverage/holes.cov-map

+18-13
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,57 @@
11
Function name: <holes::main::MyStruct>::_method (unused)
2-
Raw bytes (9): 0x[01, 01, 00, 01, 00, 25, 09, 00, 1d]
2+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 2b, 09, 00, 1d]
33
Number of files: 1
44
- file 0 => global file 1
55
Number of expressions: 0
66
Number of file 0 mappings: 1
7-
- Code(Zero) at (prev + 37, 9) to (start + 0, 29)
7+
- Code(Zero) at (prev + 43, 9) to (start + 0, 29)
88
Highest counter ID seen: (none)
99

1010
Function name: holes::main
11-
Raw bytes (44): 0x[01, 01, 00, 08, 01, 08, 01, 06, 11, 01, 0f, 05, 00, 12, 01, 04, 05, 00, 12, 01, 07, 05, 00, 12, 01, 06, 05, 00, 12, 01, 06, 05, 03, 0f, 01, 0a, 05, 03, 0f, 01, 0a, 05, 01, 02]
11+
Raw bytes (69): 0x[01, 01, 00, 0d, 01, 08, 01, 01, 12, 01, 05, 05, 00, 12, 01, 07, 09, 00, 11, 01, 09, 05, 00, 12, 01, 04, 05, 00, 12, 01, 07, 05, 00, 12, 01, 06, 05, 00, 12, 01, 04, 05, 00, 12, 01, 04, 05, 00, 12, 01, 06, 05, 03, 0f, 01, 0a, 05, 03, 0f, 01, 0a, 05, 0c, 0d, 01, 0f, 0e, 05, 02]
1212
Number of files: 1
1313
- file 0 => global file 1
1414
Number of expressions: 0
15-
Number of file 0 mappings: 8
16-
- Code(Counter(0)) at (prev + 8, 1) to (start + 6, 17)
17-
- Code(Counter(0)) at (prev + 15, 5) to (start + 0, 18)
15+
Number of file 0 mappings: 13
16+
- Code(Counter(0)) at (prev + 8, 1) to (start + 1, 18)
17+
- Code(Counter(0)) at (prev + 5, 5) to (start + 0, 18)
18+
- Code(Counter(0)) at (prev + 7, 9) to (start + 0, 17)
19+
- Code(Counter(0)) at (prev + 9, 5) to (start + 0, 18)
1820
- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 18)
1921
- Code(Counter(0)) at (prev + 7, 5) to (start + 0, 18)
2022
- Code(Counter(0)) at (prev + 6, 5) to (start + 0, 18)
23+
- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 18)
24+
- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 18)
2125
- Code(Counter(0)) at (prev + 6, 5) to (start + 3, 15)
2226
- Code(Counter(0)) at (prev + 10, 5) to (start + 3, 15)
23-
- Code(Counter(0)) at (prev + 10, 5) to (start + 1, 2)
27+
- Code(Counter(0)) at (prev + 10, 5) to (start + 12, 13)
28+
- Code(Counter(0)) at (prev + 15, 14) to (start + 5, 2)
2429
Highest counter ID seen: c0
2530

2631
Function name: holes::main::_unused_fn (unused)
27-
Raw bytes (9): 0x[01, 01, 00, 01, 00, 19, 05, 00, 17]
32+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 1f, 05, 00, 17]
2833
Number of files: 1
2934
- file 0 => global file 1
3035
Number of expressions: 0
3136
Number of file 0 mappings: 1
32-
- Code(Zero) at (prev + 25, 5) to (start + 0, 23)
37+
- Code(Zero) at (prev + 31, 5) to (start + 0, 23)
3338
Highest counter ID seen: (none)
3439

3540
Function name: holes::main::{closure#0} (unused)
36-
Raw bytes (9): 0x[01, 01, 00, 01, 00, 12, 09, 02, 0a]
41+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 18, 09, 02, 0a]
3742
Number of files: 1
3843
- file 0 => global file 1
3944
Number of expressions: 0
4045
Number of file 0 mappings: 1
41-
- Code(Zero) at (prev + 18, 9) to (start + 2, 10)
46+
- Code(Zero) at (prev + 24, 9) to (start + 2, 10)
4247
Highest counter ID seen: (none)
4348

4449
Function name: holes::main::{closure#1} (unused)
45-
Raw bytes (9): 0x[01, 01, 00, 01, 00, 3d, 09, 02, 0a]
50+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 4b, 09, 02, 0a]
4651
Number of files: 1
4752
- file 0 => global file 1
4853
Number of expressions: 0
4954
Number of file 0 mappings: 1
50-
- Code(Zero) at (prev + 61, 9) to (start + 2, 10)
55+
- Code(Zero) at (prev + 75, 9) to (start + 2, 10)
5156
Highest counter ID seen: (none)
5257

‎tests/coverage/holes.coverage

+37-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77
LL| |
88
LL| 1|fn main() {
99
LL| 1| black_box(());
10-
LL| 1|
11-
LL| 1| // Splitting this across multiple lines makes it easier to see where the
12-
LL| 1| // coverage mapping regions begin and end.
13-
LL| 1| #[rustfmt::skip]
10+
LL| |
11+
LL| | static MY_STATIC: () = ();
12+
LL| |
13+
LL| 1| black_box(());
14+
LL| |
15+
LL| | const MY_CONST: () = ();
16+
LL| |
17+
LL| | // Splitting this across multiple lines makes it easier to see where the
18+
LL| | // coverage mapping regions begin and end.
19+
LL| | #[rustfmt::skip]
1420
LL| 1| let _closure =
1521
LL| | |
1622
LL| | _arg: (),
@@ -39,6 +45,14 @@
3945
LL| |
4046
LL| 1| black_box(());
4147
LL| |
48+
LL| | trait MyTrait {}
49+
LL| |
50+
LL| 1| black_box(());
51+
LL| |
52+
LL| | impl MyTrait for MyStruct {}
53+
LL| |
54+
LL| 1| black_box(());
55+
LL| |
4256
LL| | macro_rules! _my_macro {
4357
LL| | () => {};
4458
LL| | }
@@ -64,5 +78,24 @@
6478
LL| | ;
6579
LL| |
6680
LL| 1| black_box(());
81+
LL| 1|
82+
LL| 1| // This tests the edge case of a const block nested inside an "anon const",
83+
LL| 1| // such as the length of an array literal. Handling this case requires
84+
LL| 1| // `nested_filter::OnlyBodies` or equivalent.
85+
LL| 1| #[rustfmt::skip]
86+
LL| 1| let _const_block_inside_anon_const =
87+
LL| 1| [
88+
LL| 1| 0
89+
LL| 1| ;
90+
LL| 1| 7
91+
LL| 1| +
92+
LL| 1| const
93+
LL| | {
94+
LL| | 3
95+
LL| 1| }
96+
LL| 1| ]
97+
LL| 1| ;
98+
LL| 1|
99+
LL| 1| black_box(());
67100
LL| 1|}
68101

‎tests/coverage/holes.rs

+33
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ use core::hint::black_box;
88
fn main() {
99
black_box(());
1010

11+
static MY_STATIC: () = ();
12+
13+
black_box(());
14+
15+
const MY_CONST: () = ();
16+
1117
// Splitting this across multiple lines makes it easier to see where the
1218
// coverage mapping regions begin and end.
1319
#[rustfmt::skip]
@@ -39,6 +45,14 @@ fn main() {
3945

4046
black_box(());
4147

48+
trait MyTrait {}
49+
50+
black_box(());
51+
52+
impl MyTrait for MyStruct {}
53+
54+
black_box(());
55+
4256
macro_rules! _my_macro {
4357
() => {};
4458
}
@@ -64,4 +78,23 @@ fn main() {
6478
;
6579

6680
black_box(());
81+
82+
// This tests the edge case of a const block nested inside an "anon const",
83+
// such as the length of an array literal. Handling this case requires
84+
// `nested_filter::OnlyBodies` or equivalent.
85+
#[rustfmt::skip]
86+
let _const_block_inside_anon_const =
87+
[
88+
0
89+
;
90+
7
91+
+
92+
const
93+
{
94+
3
95+
}
96+
]
97+
;
98+
99+
black_box(());
67100
}

0 commit comments

Comments
 (0)
Failed to load comments.