@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxIndexMap;
12
12
use rustc_data_structures:: unord:: UnordSet ;
13
13
use rustc_middle:: ty:: TyCtxt ;
14
14
use 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} ;
16
16
use thin_vec:: ThinVec ;
17
17
use tracing:: { debug, trace} ;
18
18
@@ -157,7 +157,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
157
157
} ;
158
158
159
159
for fragment in docs {
160
- if fragment. doc == kw :: Empty {
160
+ if fragment. doc == sym :: empty {
161
161
continue ;
162
162
}
163
163
@@ -177,7 +177,7 @@ pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
177
177
///
178
178
/// Note: remove the trailing newline where appropriate
179
179
pub fn add_doc_fragment ( out : & mut String , frag : & DocFragment ) {
180
- if frag. doc == kw :: Empty {
180
+ if frag. doc == sym :: empty {
181
181
out. push ( '\n' ) ;
182
182
return ;
183
183
}
@@ -514,20 +514,30 @@ pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
514
514
/// This method does not always work, because markdown bytes don't necessarily match source bytes,
515
515
/// like if escapes are used in the string. In this case, it returns `None`.
516
516
///
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:
518
521
///
519
522
/// - 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`.
521
524
/// - 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`.
522
529
pub fn source_span_for_markdown_range (
523
530
tcx : TyCtxt < ' _ > ,
524
531
markdown : & str ,
525
532
md_range : & Range < usize > ,
526
533
fragments : & [ DocFragment ] ,
527
534
) -> Option < Span > {
535
+ use rustc_span:: BytePos ;
536
+
537
+ let map = tcx. sess . source_map ( ) ;
528
538
if let & [ fragment] = & fragments
529
539
&& 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 )
531
541
&& snippet. trim_end ( ) == markdown. trim_end ( )
532
542
&& let Ok ( md_range_lo) = u32:: try_from ( md_range. start )
533
543
&& let Ok ( md_range_hi) = u32:: try_from ( md_range. end )
@@ -544,10 +554,43 @@ pub fn source_span_for_markdown_range(
544
554
let is_all_sugared_doc = fragments. iter ( ) . all ( |frag| frag. kind == DocFragmentKind :: SugaredDoc ) ;
545
555
546
556
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
+ }
547
590
return None ;
548
591
}
549
592
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 ( ) ?;
551
594
552
595
let starting_line = markdown[ ..md_range. start ] . matches ( '\n' ) . count ( ) ;
553
596
let ending_line = starting_line + markdown[ md_range. start ..md_range. end ] . matches ( '\n' ) . count ( ) ;
0 commit comments