@@ -514,20 +514,30 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
514514/// This method does not always work, because markdown bytes don't necessarily match source bytes, 
515515/// like if escapes are used in the string. In this case, it returns `None`. 
516516/// 
517- /// This method will return `Some` only if: 
517+ /// `markdown` is typically the entire documentation for an item, 
518+ /// after combining fragments. 
519+ /// 
520+ /// This method will return `Some` only if one of the following is true: 
518521/// 
519522/// - The doc is made entirely from sugared doc comments, which cannot contain escapes 
520- /// - The doc is entirely from a single doc fragment,  with a string literal,  exactly equal 
523+ /// - The doc is entirely from a single doc fragment with a string literal exactly equal to `markdown`.  
521524/// - The doc comes from `include_str!` 
525+ /// - The doc includes exactly one substring matching `markdown[md_range]` which is contained in a single doc fragment. 
526+ /// 
527+ /// This function is defined in the compiler so it can be used by 
528+ /// both `rustdoc` and `clippy`. 
522529pub  fn  source_span_for_markdown_range ( 
523530    tcx :  TyCtxt < ' _ > , 
524531    markdown :  & str , 
525532    md_range :  & Range < usize > , 
526533    fragments :  & [ DocFragment ] , 
527534)  -> Option < Span >  { 
535+     use  rustc_span:: BytePos ; 
536+ 
537+     let  map = tcx. sess . source_map ( ) ; 
528538    if  let  & [ fragment]  = & fragments
529539        && fragment. kind  == DocFragmentKind :: RawDoc 
530-         && let  Ok ( snippet)  = tcx . sess . source_map ( ) . span_to_snippet ( fragment. span ) 
540+         && let  Ok ( snippet)  = map . span_to_snippet ( fragment. span ) 
531541        && snippet. trim_end ( )  == markdown. trim_end ( ) 
532542        && let  Ok ( md_range_lo)  = u32:: try_from ( md_range. start ) 
533543        && let  Ok ( md_range_hi)  = u32:: try_from ( md_range. end ) 
@@ -544,10 +554,42 @@ pub fn source_span_for_markdown_range(
544554    let  is_all_sugared_doc = fragments. iter ( ) . all ( |frag| frag. kind  == DocFragmentKind :: SugaredDoc ) ; 
545555
546556    if  !is_all_sugared_doc { 
557+         // This case ignores the markdown outside of the range so that it can 
558+         // work in cases where the markdown is made from several different 
559+         // doc fragments, but the target range does not span across multiple 
560+         // fragments. 
561+         let  mut  match_data = None ; 
562+         let  pat = & markdown[ md_range. clone ( ) ] ; 
563+         // This heirustic doesn't make sense with a zero-sized range. 
564+         if  pat. is_empty ( )  { 
565+             return  None ; 
566+         } 
567+         for  ( i,  fragment)  in  fragments. iter ( ) . enumerate ( )  { 
568+             if  let  Ok ( snippet)  = map. span_to_snippet ( fragment. span ) 
569+                 && let  Some ( match_start)  = snippet. find ( pat) 
570+             { 
571+                 // If there is either a match in a previous fragment, or 
572+                 // multiple matches in this fragment, there is ambiguity. 
573+                 if  match_data. is_none ( )  && !snippet[ match_start + 1 ..] . contains ( pat)  { 
574+                     match_data = Some ( ( i,  match_start) ) ; 
575+                 }  else  { 
576+                     // Heirustic produced ambiguity, return nothing. 
577+                     return  None ; 
578+                 } 
579+             } 
580+         } 
581+         if  let  Some ( ( i,  match_start) )  = match_data { 
582+             let  sp = fragments[ i] . span ; 
583+             return  Some ( 
584+                 sp
585+                     . with_lo ( sp. lo ( )  + BytePos ( match_start as  u32 ) ) 
586+                     . with_hi ( sp. lo ( )  + BytePos ( ( md_range. end  - md_range. start )  as  u32 ) ) , 
587+             ) ; 
588+         } 
547589        return  None ; 
548590    } 
549591
550-     let  snippet = tcx . sess . source_map ( ) . span_to_snippet ( span_of_fragments ( fragments) ?) . ok ( ) ?; 
592+     let  snippet = map . span_to_snippet ( span_of_fragments ( fragments) ?) . ok ( ) ?; 
551593
552594    let  starting_line = markdown[ ..md_range. start ] . matches ( '\n' ) . count ( ) ; 
553595    let  ending_line = starting_line + markdown[ md_range. start ..md_range. end ] . matches ( '\n' ) . count ( ) ; 
0 commit comments