@@ -498,20 +498,25 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
498
498
/// This method does not always work, because markdown bytes don't necessarily match source bytes,
499
499
/// like if escapes are used in the string. In this case, it returns `None`.
500
500
///
501
+ /// `markdown` is typically the entire documentation for an item,
502
+ /// after combining fragments.
503
+ ///
501
504
/// This method will return `Some` only if:
502
505
///
503
506
/// - The doc is made entirely from sugared doc comments, which cannot contain escapes
504
507
/// - The doc is entirely from a single doc fragment, with a string literal, exactly equal
505
508
/// - The doc comes from `include_str!`
509
+ /// - The doc includes exactly one substring matching `markdown[md_range]` which is contained in a single doc fragment.
506
510
pub fn source_span_for_markdown_range (
507
511
tcx : TyCtxt < ' _ > ,
508
512
markdown : & str ,
509
513
md_range : & Range < usize > ,
510
514
fragments : & [ DocFragment ] ,
511
515
) -> Option < Span > {
516
+ let span_to_snippet = |span| tcx. sess . source_map ( ) . span_to_snippet ( span) ;
512
517
if let & [ fragment] = & fragments
513
518
&& fragment. kind == DocFragmentKind :: RawDoc
514
- && let Ok ( snippet) = tcx . sess . source_map ( ) . span_to_snippet ( fragment. span )
519
+ && let Ok ( snippet) = span_to_snippet ( fragment. span )
515
520
&& snippet. trim_end ( ) == markdown. trim_end ( )
516
521
&& let Ok ( md_range_lo) = u32:: try_from ( md_range. start )
517
522
&& let Ok ( md_range_hi) = u32:: try_from ( md_range. end )
@@ -528,6 +533,30 @@ pub fn source_span_for_markdown_range(
528
533
let is_all_sugared_doc = fragments. iter ( ) . all ( |frag| frag. kind == DocFragmentKind :: SugaredDoc ) ;
529
534
530
535
if !is_all_sugared_doc {
536
+ // this case ignores the markdown outside of the range so that it can
537
+ // work in cases where the markdown is made from several different
538
+ // doc fragments, but the target range does not span across multiple
539
+ // fragments.
540
+ let mut match_data = None ;
541
+ for ( i, fragment) in fragments. iter ( ) . enumerate ( ) {
542
+ if let Ok ( snippet) = span_to_snippet ( fragment. span )
543
+ && let Some ( match_start) = snippet. find ( & markdown[ md_range. clone ( ) ] )
544
+ {
545
+ if match_data. is_none ( ) {
546
+ match_data = Some ( ( i, match_start) ) ;
547
+ } else {
548
+ // heirustic produced ambiguity, return nothing.
549
+ return None ;
550
+ }
551
+ }
552
+ }
553
+ if let Some ( ( i, match_start) ) = match_data {
554
+ use rustc_span:: BytePos ;
555
+ let mut sp = fragments[ i] . span ;
556
+ sp = sp. with_lo ( sp. lo ( ) + BytePos ( match_start as u32 ) ) ;
557
+ sp = sp. with_hi ( sp. lo ( ) + BytePos ( ( md_range. end - md_range. start ) as u32 ) ) ;
558
+ return Some ( sp) ;
559
+ }
531
560
return None ;
532
561
}
533
562
0 commit comments