@@ -497,6 +497,19 @@ fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
497
497
start. to ( end)
498
498
}
499
499
500
+ /// Reports a resolution failure diagnostic.
501
+ ///
502
+ /// Ideally we can report the diagnostic with the actual span in the source where the link failure
503
+ /// occurred. However, there's a mismatch between the span in the source code and the span in the
504
+ /// markdown, so we have to do a bit of work to figure out the correspondence.
505
+ ///
506
+ /// It's not too hard to find the span for sugared doc comments (`///` and `/**`), because the
507
+ /// source will match the markdown exactly, excluding the comment markers. However, it's much more
508
+ /// difficult to calculate the spans for unsugared docs, because we have to deal with escaping and
509
+ /// other source features. So, we attempt to find the exact source span of the resolution failure
510
+ /// in sugared docs, but use the span of the documentation attributes themselves for unsugared
511
+ /// docs. Because this span might be overly large, we display the markdown line containing the
512
+ /// failure as a note.
500
513
fn resolution_failure (
501
514
cx : & DocContext ,
502
515
attrs : & Attributes ,
@@ -507,34 +520,50 @@ fn resolution_failure(
507
520
let sp = span_of_attrs ( attrs) ;
508
521
let msg = format ! ( "`[{}]` cannot be resolved, ignoring it..." , path_str) ;
509
522
510
- let code_dox = sp. to_src ( cx) ;
511
-
512
- let doc_comment_padding = 3 ;
513
523
let mut diag = if let Some ( link_range) = link_range {
514
- // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
515
- // ^ ~~~~~~
516
- // | link_range
517
- // last_new_line_offset
518
-
519
524
let mut diag;
520
- if dox. lines ( ) . count ( ) == code_dox. lines ( ) . count ( ) {
521
- let line_offset = dox[ ..link_range. start ] . lines ( ) . count ( ) ;
522
- // The span starts in the `///`, so we don't have to account for the leading whitespace
523
- let code_dox_len = if line_offset <= 1 {
524
- doc_comment_padding
525
- } else {
526
- // The first `///`
527
- doc_comment_padding +
528
- // Each subsequent leading whitespace and `///`
529
- code_dox. lines ( ) . skip ( 1 ) . take ( line_offset - 1 ) . fold ( 0 , |sum, line| {
530
- sum + doc_comment_padding + line. len ( ) - line. trim_start ( ) . len ( )
531
- } )
532
- } ;
533
525
534
- // Extract the specific span
526
+ if attrs. doc_strings . iter ( ) . all ( |frag| match frag {
527
+ DocFragment :: SugaredDoc ( ..) => true ,
528
+ _ => false ,
529
+ } ) {
530
+ let source_dox = sp. to_src ( cx) ;
531
+ let mut source_lines = source_dox. lines ( ) . peekable ( ) ;
532
+ let mut md_lines = dox. lines ( ) . peekable ( ) ;
533
+
534
+ // The number of bytes from the start of the source span to the resolution failure that
535
+ // are *not* part of the markdown, like comment markers.
536
+ let mut source_offset = 0 ;
537
+
538
+ // Eat any source lines before the markdown starts (e.g., `/**` on its own line).
539
+ while let Some ( source_line) = source_lines. peek ( ) {
540
+ if source_line. contains ( md_lines. peek ( ) . unwrap ( ) ) {
541
+ break ;
542
+ }
543
+
544
+ // Include the newline.
545
+ source_offset += source_line. len ( ) + 1 ;
546
+ source_lines. next ( ) . unwrap ( ) ;
547
+ }
548
+
549
+ // The number of lines up to and including the resolution failure.
550
+ let num_lines = dox[ ..link_range. start ] . lines ( ) . count ( ) ;
551
+
552
+ // Consume inner comment markers (e.g., `///` or ` *`).
553
+ for ( source_line, md_line) in source_lines. zip ( md_lines) . take ( num_lines) {
554
+ source_offset += if md_line. is_empty ( ) {
555
+ // If there is no markdown on this line, then the whole line is a comment
556
+ // marker. We don't have to count the newline here because it's in the markdown
557
+ // too.
558
+ source_line. len ( )
559
+ } else {
560
+ source_line. find ( md_line) . unwrap ( )
561
+ } ;
562
+ }
563
+
535
564
let sp = sp. from_inner_byte_pos (
536
- link_range. start + code_dox_len ,
537
- link_range. end + code_dox_len ,
565
+ link_range. start + source_offset ,
566
+ link_range. end + source_offset ,
538
567
) ;
539
568
540
569
diag = cx. tcx . struct_span_lint_node ( lint:: builtin:: INTRA_DOC_LINK_RESOLUTION_FAILURE ,
@@ -548,6 +577,10 @@ fn resolution_failure(
548
577
sp,
549
578
& msg) ;
550
579
580
+ // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
581
+ // ^ ~~~~
582
+ // | link_range
583
+ // last_new_line_offset
551
584
let last_new_line_offset = dox[ ..link_range. start ] . rfind ( '\n' ) . map_or ( 0 , |n| n + 1 ) ;
552
585
let line = dox[ last_new_line_offset..] . lines ( ) . next ( ) . unwrap_or ( "" ) ;
553
586
0 commit comments