@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxIndexMap;
1212use rustc_data_structures:: unord:: UnordSet ;
1313use rustc_middle:: ty:: TyCtxt ;
1414use rustc_span:: def_id:: DefId ;
15- use rustc_span:: { DUMMY_SP , InnerSpan , Span , Symbol , kw , sym} ;
15+ use rustc_span:: { DUMMY_SP , InnerSpan , Span , Symbol , sym} ;
1616use thin_vec:: ThinVec ;
1717use tracing:: { debug, trace} ;
1818
@@ -157,7 +157,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
157157 } ;
158158
159159 for fragment in docs {
160- if fragment. doc == kw :: Empty {
160+ if fragment. doc == sym :: empty {
161161 continue ;
162162 }
163163
@@ -177,7 +177,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
177177///
178178/// Note: remove the trailing newline where appropriate
179179pub fn add_doc_fragment ( out : & mut String , frag : & DocFragment ) {
180- if frag. doc == kw :: Empty {
180+ if frag. doc == sym :: empty {
181181 out. push ( '\n' ) ;
182182 return ;
183183 }
@@ -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,43 @@ 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+ // we need to calculate the span start,
584+ // then use that in our calulations for the span end
585+ let lo = sp. lo ( ) + BytePos ( match_start as u32 ) ;
586+ return Some (
587+ sp. with_lo ( lo) . with_hi ( lo + BytePos ( ( md_range. end - md_range. start ) as u32 ) ) ,
588+ ) ;
589+ }
547590 return None ;
548591 }
549592
550- let snippet = tcx . sess . source_map ( ) . span_to_snippet ( span_of_fragments ( fragments) ?) . ok ( ) ?;
593+ let snippet = map . span_to_snippet ( span_of_fragments ( fragments) ?) . ok ( ) ?;
551594
552595 let starting_line = markdown[ ..md_range. start ] . matches ( '\n' ) . count ( ) ;
553596 let ending_line = starting_line + markdown[ md_range. start ..md_range. end ] . matches ( '\n' ) . count ( ) ;
0 commit comments