@@ -497,6 +497,19 @@ fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
497497 start. to ( end)
498498}
499499
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.
500513fn resolution_failure (
501514 cx : & DocContext ,
502515 attrs : & Attributes ,
@@ -507,34 +520,50 @@ fn resolution_failure(
507520 let sp = span_of_attrs ( attrs) ;
508521 let msg = format ! ( "`[{}]` cannot be resolved, ignoring it..." , path_str) ;
509522
510- let code_dox = sp. to_src ( cx) ;
511-
512- let doc_comment_padding = 3 ;
513523 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-
519524 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- } ;
533525
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+
535564 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 ,
538567 ) ;
539568
540569 diag = cx. tcx . struct_span_lint_node ( lint:: builtin:: INTRA_DOC_LINK_RESOLUTION_FAILURE ,
@@ -548,6 +577,10 @@ fn resolution_failure(
548577 sp,
549578 & msg) ;
550579
580+ // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
581+ // ^ ~~~~
582+ // | link_range
583+ // last_new_line_offset
551584 let last_new_line_offset = dox[ ..link_range. start ] . rfind ( '\n' ) . map_or ( 0 , |n| n + 1 ) ;
552585 let line = dox[ last_new_line_offset..] . lines ( ) . next ( ) . unwrap_or ( "" ) ;
553586
0 commit comments