@@ -460,6 +460,19 @@ pub fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
460
460
start. to ( end)
461
461
}
462
462
463
+ /// Reports a resolution failure diagnostic.
464
+ ///
465
+ /// Ideally we can report the diagnostic with the actual span in the source where the link failure
466
+ /// occurred. However, there's a mismatch between the span in the source code and the span in the
467
+ /// markdown, so we have to do a bit of work to figure out the correspondence.
468
+ ///
469
+ /// It's not too hard to find the span for sugared doc comments (`///` and `/**`), because the
470
+ /// source will match the markdown exactly, excluding the comment markers. However, it's much more
471
+ /// difficult to calculate the spans for unsugared docs, because we have to deal with escaping and
472
+ /// other source features. So, we attempt to find the exact source span of the resolution failure
473
+ /// in sugared docs, but use the span of the documentation attributes themselves for unsugared
474
+ /// docs. Because this span might be overly large, we display the markdown line containing the
475
+ /// failure as a note.
463
476
fn resolution_failure (
464
477
cx : & DocContext ,
465
478
attrs : & Attributes ,
@@ -470,34 +483,50 @@ fn resolution_failure(
470
483
let sp = span_of_attrs ( attrs) ;
471
484
let msg = format ! ( "`[{}]` cannot be resolved, ignoring it..." , path_str) ;
472
485
473
- let code_dox = sp. to_src ( cx) ;
474
-
475
- let doc_comment_padding = 3 ;
476
486
let mut diag = if let Some ( link_range) = link_range {
477
- // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
478
- // ^ ~~~~~~
479
- // | link_range
480
- // last_new_line_offset
481
-
482
487
let mut diag;
483
- if dox. lines ( ) . count ( ) == code_dox. lines ( ) . count ( ) {
484
- let line_offset = dox[ ..link_range. start ] . lines ( ) . count ( ) ;
485
- // The span starts in the `///`, so we don't have to account for the leading whitespace
486
- let code_dox_len = if line_offset <= 1 {
487
- doc_comment_padding
488
- } else {
489
- // The first `///`
490
- doc_comment_padding +
491
- // Each subsequent leading whitespace and `///`
492
- code_dox. lines ( ) . skip ( 1 ) . take ( line_offset - 1 ) . fold ( 0 , |sum, line| {
493
- sum + doc_comment_padding + line. len ( ) - line. trim_start ( ) . len ( )
494
- } )
495
- } ;
496
488
497
- // Extract the specific span
489
+ if attrs. doc_strings . iter ( ) . all ( |frag| match frag {
490
+ DocFragment :: SugaredDoc ( ..) => true ,
491
+ _ => false ,
492
+ } ) {
493
+ let source_dox = sp. to_src ( cx) ;
494
+ let mut source_lines = source_dox. lines ( ) . peekable ( ) ;
495
+ let mut md_lines = dox. lines ( ) . peekable ( ) ;
496
+
497
+ // The number of bytes from the start of the source span to the resolution failure that
498
+ // are *not* part of the markdown, like comment markers.
499
+ let mut source_offset = 0 ;
500
+
501
+ // Eat any source lines before the markdown starts (e.g., `/**` on its own line).
502
+ while let Some ( source_line) = source_lines. peek ( ) {
503
+ if source_line. contains ( md_lines. peek ( ) . unwrap ( ) ) {
504
+ break ;
505
+ }
506
+
507
+ // Include the newline.
508
+ source_offset += source_line. len ( ) + 1 ;
509
+ source_lines. next ( ) . unwrap ( ) ;
510
+ }
511
+
512
+ // The number of lines up to and including the resolution failure.
513
+ let num_lines = dox[ ..link_range. start ] . lines ( ) . count ( ) ;
514
+
515
+ // Consume inner comment markers (e.g., `///` or ` *`).
516
+ for ( source_line, md_line) in source_lines. zip ( md_lines) . take ( num_lines) {
517
+ source_offset += if md_line. is_empty ( ) {
518
+ // If there is no markdown on this line, then the whole line is a comment
519
+ // marker. We don't have to count the newline here because it's in the markdown
520
+ // too.
521
+ source_line. len ( )
522
+ } else {
523
+ source_line. find ( md_line) . unwrap ( )
524
+ } ;
525
+ }
526
+
498
527
let sp = sp. from_inner_byte_pos (
499
- link_range. start + code_dox_len ,
500
- link_range. end + code_dox_len ,
528
+ link_range. start + source_offset ,
529
+ link_range. end + source_offset ,
501
530
) ;
502
531
503
532
diag = cx. tcx . struct_span_lint_node ( lint:: builtin:: INTRA_DOC_LINK_RESOLUTION_FAILURE ,
@@ -511,6 +540,10 @@ fn resolution_failure(
511
540
sp,
512
541
& msg) ;
513
542
543
+ // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
544
+ // ^ ~~~~
545
+ // | link_range
546
+ // last_new_line_offset
514
547
let last_new_line_offset = dox[ ..link_range. start ] . rfind ( '\n' ) . map_or ( 0 , |n| n + 1 ) ;
515
548
let line = dox[ last_new_line_offset..] . lines ( ) . next ( ) . unwrap_or ( "" ) ;
516
549
0 commit comments