From 1a6d41cdb5219763eeaf3604e5805a854e5ba931 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 2 Feb 2022 16:37:17 -0700 Subject: [PATCH] rustdoc: resolve intra-doc links when checking HTML Similar to #86451 CC #67799 --- src/librustdoc/passes/html_tags.rs | 28 +++++++- .../intra-doc/html-as-generics-intra-doc.rs | 25 +++++++ .../html-as-generics-intra-doc.stderr | 69 +++++++++++++++++++ 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.rs create mode 100644 src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.stderr diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs index 9caadef3dec7c..f047f6bb1699b 100644 --- a/src/librustdoc/passes/html_tags.rs +++ b/src/librustdoc/passes/html_tags.rs @@ -5,7 +5,7 @@ use crate::core::DocContext; use crate::html::markdown::main_body_opts; use crate::visit::DocVisitor; -use pulldown_cmark::{Event, Parser, Tag}; +use pulldown_cmark::{BrokenLink, Event, LinkType, Parser, Tag}; use std::iter::Peekable; use std::ops::Range; @@ -249,7 +249,31 @@ impl<'a, 'tcx> DocVisitor for InvalidHtmlTagsLinter<'a, 'tcx> { let mut is_in_comment = None; let mut in_code_block = false; - let p = Parser::new_ext(&dox, main_body_opts()).into_offset_iter(); + let link_names = item.link_names(&self.cx.cache); + + let mut replacer = |broken_link: BrokenLink<'_>| { + if let Some(link) = + link_names.iter().find(|link| *link.original_text == *broken_link.reference) + { + Some((link.href.as_str().into(), link.new_text.as_str().into())) + } else if matches!( + &broken_link.link_type, + LinkType::Reference | LinkType::ReferenceUnknown + ) { + // If the link is shaped [like][this], suppress any broken HTML in the [this] part. + // The `broken_intra_doc_links` will report typos in there anyway. + Some(( + broken_link.reference.to_string().into(), + broken_link.reference.to_string().into(), + )) + } else { + None + } + }; + + let p = + Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer)) + .into_offset_iter(); for (event, range) in p { match event { diff --git a/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.rs b/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.rs new file mode 100644 index 0000000000000..b5470c859fdf4 --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.rs @@ -0,0 +1,25 @@ +#![deny(rustdoc::invalid_html_tags)] +#![deny(rustdoc::broken_intra_doc_links)] + +pub struct ExistentStruct(T); + +/// This [test][ExistentStruct] thing! +pub struct NoError; + +/// This [ExistentStruct] thing! +//~^ ERROR unclosed HTML tag `i32` +pub struct PartialErrorOnlyHtml; + +/// This [test][NonExistentStruct] thing! +//~^ ERROR unresolved link +pub struct PartialErrorOnlyResolve; + +/// This [NonExistentStruct2] thing! +//~^ ERROR unclosed HTML tag `i32` +//~| ERROR unresolved link +pub struct YesError; + +/// This [NonExistentStruct3][] thing! +//~^ ERROR unclosed HTML tag `i32` +//~| ERROR unresolved link +pub struct YesErrorCollapsed; diff --git a/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.stderr b/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.stderr new file mode 100644 index 0000000000000..00fe229da40ce --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc/html-as-generics-intra-doc.stderr @@ -0,0 +1,69 @@ +error: unresolved link to `NonExistentStruct` + --> $DIR/html-as-generics-intra-doc.rs:13:17 + | +LL | /// This [test][NonExistentStruct] thing! + | ^^^^^^^^^^^^^^^^^^^^^^ no item named `NonExistentStruct` in scope + | +note: the lint level is defined here + --> $DIR/html-as-generics-intra-doc.rs:2:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `NonExistentStruct2` + --> $DIR/html-as-generics-intra-doc.rs:17:11 + | +LL | /// This [NonExistentStruct2] thing! + | ^^^^^^^^^^^^^^^^^^^^^^^ no item named `NonExistentStruct2` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `NonExistentStruct3` + --> $DIR/html-as-generics-intra-doc.rs:22:11 + | +LL | /// This [NonExistentStruct3][] thing! + | ^^^^^^^^^^^^^^^^^^^^^^^ no item named `NonExistentStruct3` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unclosed HTML tag `i32` + --> $DIR/html-as-generics-intra-doc.rs:9:25 + | +LL | /// This [ExistentStruct] thing! + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/html-as-generics-intra-doc.rs:1:9 + | +LL | #![deny(rustdoc::invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try marking as source code + | +LL | /// This [`ExistentStruct`] thing! + | + + + +error: unclosed HTML tag `i32` + --> $DIR/html-as-generics-intra-doc.rs:17:29 + | +LL | /// This [NonExistentStruct2] thing! + | ^^^^^ + | +help: try marking as source code + | +LL | /// This [`NonExistentStruct2`] thing! + | + + + +error: unclosed HTML tag `i32` + --> $DIR/html-as-generics-intra-doc.rs:22:29 + | +LL | /// This [NonExistentStruct3][] thing! + | ^^^^^ + | +help: try marking as source code + | +LL | /// This [`NonExistentStruct3`][] thing! + | + + + +error: aborting due to 6 previous errors +