@@ -32,7 +32,7 @@ use rustc_middle::mir::{
3232use rustc_middle:: ty:: TyCtxt ;
3333use rustc_span:: def_id:: DefId ;
3434use 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 } ;
3636
3737/// A simple error message wrapper for `coverage::Error`s.
3838#[ derive( Debug ) ]
@@ -552,24 +552,109 @@ fn get_body_span<'tcx>(
552552 let mut body_span = hir_body. value . span ;
553553 let def_id = mir_body. source . def_id ( ) ;
554554
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 {
556581 // If the MIR function is a closure, and if the closure body span
557582 // starts from a macro, but it's content is not in that macro, try
558583 // to find a non-macro callsite, and instrument the spans there
559584 // instead.
560- loop {
585+ while body_span. from_expansion ( ) {
586+ body_is_from_expansion = true ;
561587 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
564635 }
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
567639 } else {
568- break ;
640+ Ok ( Some ( span ) ) // initialize max_span to first useful span
569641 }
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;
570656 }
571657 }
572-
573658 body_span
574659}
575660
0 commit comments