@@ -32,7 +32,7 @@ use rustc_middle::mir::{
32
32
use rustc_middle:: ty:: TyCtxt ;
33
33
use rustc_span:: def_id:: DefId ;
34
34
use rustc_span:: source_map:: SourceMap ;
35
- use rustc_span:: { CharPos , ExpnKind , Pos , SourceFile , Span , Symbol } ;
35
+ use rustc_span:: { CharPos , ExpnData , ExpnKind , Pos , SourceFile , Span , Symbol } ;
36
36
37
37
/// A simple error message wrapper for `coverage::Error`s.
38
38
#[ derive( Debug ) ]
@@ -552,24 +552,109 @@ fn get_body_span<'tcx>(
552
552
let mut body_span = hir_body. value . span ;
553
553
let def_id = mir_body. source . def_id ( ) ;
554
554
555
- if tcx. is_closure ( def_id) {
555
+ // If debug is enabled, log expansion span info, if any.
556
+ debug ! ( "{}" , {
557
+ let mut span = body_span;
558
+ while span. from_expansion( ) {
559
+ let ExpnData { kind, call_site, .. } = span. ctxt( ) . outer_expn_data( ) ;
560
+ debug!(
561
+ "Body expansion span: {:?}...
562
+ expanded from {:?}
563
+ at call_site={:?}" ,
564
+ span, kind, call_site
565
+ ) ;
566
+ span = call_site;
567
+ }
568
+ "-- end of debug message showing body_span expansion --"
569
+ } ) ;
570
+
571
+ if body_span. in_derive_expansion ( ) {
572
+ // Derive macro expansions can be expanded, but they expand to structs
573
+ // and struct fields, and I think it's less helpful in coverage reports.
574
+ return body_span;
575
+ }
576
+
577
+ let mut body_is_from_expansion = false ;
578
+ if !tcx. is_closure ( def_id) {
579
+ body_is_from_expansion = body_span. from_expansion ( ) ;
580
+ } else {
556
581
// If the MIR function is a closure, and if the closure body span
557
582
// starts from a macro, but it's content is not in that macro, try
558
583
// to find a non-macro callsite, and instrument the spans there
559
584
// instead.
560
- loop {
585
+ while body_span. from_expansion ( ) {
586
+ body_is_from_expansion = true ;
561
587
let expn_data = body_span. ctxt ( ) . outer_expn_data ( ) ;
562
- if expn_data. is_root ( ) {
563
- break ;
588
+ body_span = expn_data. call_site ;
589
+ match expn_data. kind {
590
+ ExpnKind :: Macro { .. } => { }
591
+ _ => break ,
592
+ }
593
+ }
594
+ } ;
595
+
596
+ if !body_is_from_expansion {
597
+ // If the body_span (or recomputed body_span, for closures with macros)
598
+ // is not from an expansion, then there is no need to try to compute
599
+ // a span from the MIR statements and terminators.
600
+ return body_span;
601
+ }
602
+
603
+ // If the body is from an expansion, then there's no certainty that the
604
+ // internal statements were also expanded into the same ctxt or elsewhere.
605
+ // So, search the MIR `Statement` and `Terminator` spans, and if their
606
+ // spans exist ouside the `body_span` compute a different `body_span`
607
+ // from the spans of the MIR's `Statement`s and `Terminator`s, so
608
+ // coverage can be applied to those spans.
609
+
610
+ if let Ok ( Some ( max_span) ) = spans:: filtered_from_mir ( mir_body) . try_fold (
611
+ None ,
612
+ |some_max_span : Option < Span > , mut span| {
613
+ if span. is_empty ( ) || span == body_span {
614
+ // A span that matches the body_span doesn't add anything, and both spans that match
615
+ // the body_span, and empty spans can exist in the given body_span, even if the
616
+ // statements we want to cover are from a separate and distinct span, so we need
617
+ // to ignore them. If no other statements exist outside the body_span, then we will
618
+ // keep the given body_span.
619
+ return Ok ( some_max_span) ; // continue iterating
620
+ }
621
+
622
+ while span. ctxt ( ) != body_span. ctxt ( ) {
623
+ if !span. from_expansion ( ) {
624
+ return Ok ( some_max_span) ; // continue iterating
625
+ }
626
+ span = span. ctxt ( ) . outer_expn_data ( ) . call_site ;
627
+ }
628
+
629
+ if body_span. contains ( span) {
630
+ debug ! (
631
+ "using body_span={:?}; it contains a statement or terminator with span={:?}" ,
632
+ body_span, span
633
+ ) ;
634
+ return Err ( ( ) ) ; // break from iterating
564
635
}
565
- if let ExpnKind :: Macro { .. } = expn_data. kind {
566
- body_span = expn_data. call_site ;
636
+
637
+ if let Some ( max_span) = some_max_span {
638
+ Ok ( Some ( max_span. to ( span) ) ) // extend max_span
567
639
} else {
568
- break ;
640
+ Ok ( Some ( span ) ) // initialize max_span to first useful span
569
641
}
642
+ } ,
643
+ ) {
644
+ debug ! (
645
+ "max_span = {:?}; overlaps body_span? {}, body_span hi() only touches? {}" ,
646
+ max_span,
647
+ body_span. overlaps( max_span) ,
648
+ body_span. hi( ) == max_span. lo( )
649
+ ) ;
650
+ if body_span. overlaps ( max_span) {
651
+ // extend the body_span to include the computed max_span
652
+ body_span = body_span. to ( max_span) ;
653
+ } else {
654
+ // use the computed max_span instead of the original body_span
655
+ body_span = max_span;
570
656
}
571
657
}
572
-
573
658
body_span
574
659
}
575
660
0 commit comments