about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs57
-rw-r--r--src/doc/rustdoc/src/lints.md26
-rw-r--r--src/librustdoc/html/markdown.rs1
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs72
-rw-r--r--tests/rustdoc-ui/lints/redundant_explicit_links.rs17
-rw-r--r--tests/rustdoc-ui/lints/redundant_explicit_links.stderr97
6 files changed, 217 insertions, 53 deletions
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index d433391f272..083d16d3b04 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -392,16 +392,57 @@ pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<s
     let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
     let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap();
 
-    Parser::new_with_broken_link_callback(
+    parse_links(&doc)
+}
+
+/// Similiar version of `markdown_links` from rustdoc.
+/// This will collect destination links and display text if exists.
+fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
+    let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
+    let mut event_iter = Parser::new_with_broken_link_callback(
         &doc,
         main_body_opts(),
-        Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))),
+        Some(&mut broken_link_callback),
     )
-    .filter_map(|event| match event {
-        Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
-            Some(preprocess_link(&dest))
+    .into_iter();
+    let mut links = Vec::new();
+
+    while let Some(event) = event_iter.next() {
+        match event {
+            Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
+                if let Some(display_text) = collect_link_data(&mut event_iter) {
+                    links.push(display_text);
+                }
+
+                links.push(preprocess_link(&dest));
+            }
+            _ => {}
+        }
+    }
+
+    links
+}
+
+/// Collects additional data of link.
+fn collect_link_data<'input, 'callback>(
+    event_iter: &mut Parser<'input, 'callback>,
+) -> Option<Box<str>> {
+    let mut display_text = None;
+
+    while let Some(event) = event_iter.next() {
+        match event {
+            Event::Text(code) => {
+                display_text = Some(code.to_string().into_boxed_str());
+            }
+            Event::Code(code) => {
+                display_text = Some(code.to_string().into_boxed_str());
+            }
+            Event::End(_) => {
+                break;
+            }
+            _ => {}
         }
-        _ => None,
-    })
-    .collect()
+    }
+
+    display_text
 }
diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md
index fe06b303e72..f15e6e451e7 100644
--- a/src/doc/rustdoc/src/lints.md
+++ b/src/doc/rustdoc/src/lints.md
@@ -420,17 +420,29 @@ as computed automatic links.
 This usually means the explicit links is removeable. For example:
 
 ```rust
-#![warn(rustdoc::redundant_explicit_links)]
+#![warn(rustdoc::redundant_explicit_links)] // note: unnecessary - warns by default.
 
-pub fn dummy_target() {} // note: unnecessary - warns by default.
-
-/// [`dummy_target`](dummy_target)
-/// [dummy_target](dummy_target)
-pub fn c() {}
+/// add takes 2 [`usize`](usize) and performs addition
+/// on them, then returns result.
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
 ```
 
 Which will give:
 
 ```text
-
+error: redundant explicit rustdoc link
+  --> src/lib.rs:3:27
+   |
+3  | /// add takes 2 [`usize`](usize) and performs addition
+   |                           ^^^^^
+   |
+   = note: Explicit link does not affect the original link
+note: the lint level is defined here
+  --> src/lib.rs:1:9
+   |
+1  | #![deny(rustdoc::redundant_explicit_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: Remove explicit link instead
 ```
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index b5ab8a7b12a..2fe052283a9 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1421,6 +1421,7 @@ pub(crate) fn markdown_links<'md, R>(
     links
 }
 
+/// Collects additional data of link.
 fn collect_link_data<'input, 'callback>(
     event_iter: &mut OffsetIter<'input, 'callback>,
 ) -> Option<CowStr<'input>> {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 78b86cdfa82..2ed0077c3d5 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -994,15 +994,7 @@ impl LinkCollector<'_, '_> {
                 _ => find_nearest_parent_module(self.cx.tcx, item_id).unwrap(),
             };
             for md_link in preprocessed_markdown_links(&doc) {
-                let PreprocessedMarkdownLink(_pp_link, ori_link) = &md_link;
-                let diag_info = DiagnosticInfo {
-                    item,
-                    dox: &doc,
-                    ori_link: &ori_link.link,
-                    link_range: ori_link.range.clone(),
-                };
-
-                let link = self.resolve_link(item, item_id, module_id, &md_link, &diag_info);
+                let link = self.resolve_link(&doc, item, item_id, module_id, &md_link);
                 if let Some(link) = link {
                     self.cx.cache.intra_doc_links.entry(item.item_id).or_default().insert(link);
                 }
@@ -1015,14 +1007,20 @@ impl LinkCollector<'_, '_> {
     /// FIXME(jynelson): this is way too many arguments
     fn resolve_link(
         &mut self,
+        dox: &String,
         item: &Item,
         item_id: DefId,
         module_id: DefId,
         PreprocessedMarkdownLink(pp_link, ori_link): &PreprocessedMarkdownLink,
-        diag_info: &DiagnosticInfo<'_>,
     ) -> Option<ItemLink> {
         trace!("considering link '{}'", ori_link.link);
 
+        let diag_info = DiagnosticInfo {
+            item,
+            dox,
+            ori_link: &ori_link.link,
+            link_range: ori_link.range.clone(),
+        };
         let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
             pp_link.as_ref().map_err(|err| err.report(self.cx, diag_info.clone())).ok()?;
         let disambiguator = *disambiguator;
@@ -1045,11 +1043,14 @@ impl LinkCollector<'_, '_> {
         self.check_redundant_explicit_link(
             &res,
             path_str,
-            item_id,
-            module_id,
-            disambiguator,
+            ResolutionInfo {
+                item_id,
+                module_id,
+                dis: disambiguator,
+                path_str: ori_link.display_text.clone().into_boxed_str(),
+                extra_fragment: extra_fragment.clone(),
+            },
             &ori_link,
-            extra_fragment,
             &diag_info,
         );
 
@@ -1384,15 +1385,13 @@ impl LinkCollector<'_, '_> {
         }
     }
 
+    /// Check if resolution of inline link's display text and explicit link are same.
     fn check_redundant_explicit_link(
         &mut self,
-        ex_res: &Res,
-        ex: &Box<str>,
-        item_id: DefId,
-        module_id: DefId,
-        dis: Option<Disambiguator>,
+        explicit_res: &Res,
+        explicit_link: &Box<str>,
+        display_res_info: ResolutionInfo,
         ori_link: &MarkdownLink,
-        extra_fragment: &Option<String>,
         diag_info: &DiagnosticInfo<'_>,
     ) {
         // Check if explicit resolution's path is same as resolution of original link's display text path, e.g.
@@ -1409,21 +1408,15 @@ impl LinkCollector<'_, '_> {
             return;
         }
 
-        let di_text = &ori_link.display_text;
-        let di_len = di_text.len();
-        let ex_len = ex.len();
-
-        let intra_doc_links = std::mem::take(&mut self.cx.cache.intra_doc_links);
+        let display_text = &ori_link.display_text;
+        let display_len = display_text.len();
+        let explicit_len = explicit_link.len();
 
-        if ex_len >= di_len && &ex[(ex_len - di_len)..] == di_text {
-            let Some((di_res, _)) = self.resolve_with_disambiguator_cached(
-                ResolutionInfo {
-                    item_id,
-                    module_id,
-                    dis,
-                    path_str: di_text.clone().into_boxed_str(),
-                    extra_fragment: extra_fragment.clone(),
-                },
+        if explicit_len >= display_len
+            && &explicit_link[(explicit_len - display_len)..] == display_text
+        {
+            let Some((display_res, _)) = self.resolve_with_disambiguator_cached(
+                display_res_info,
                 diag_info.clone(), // this struct should really be Copy, but Range is not :(
                 // For reference-style links we want to report only one error so unsuccessful
                 // resolutions are cached, for other links we want to report an error every
@@ -1433,7 +1426,7 @@ impl LinkCollector<'_, '_> {
                 return;
             };
 
-            if &di_res == ex_res {
+            if &display_res == explicit_res {
                 use crate::lint::REDUNDANT_EXPLICIT_LINKS;
 
                 report_diagnostic(
@@ -1444,9 +1437,14 @@ impl LinkCollector<'_, '_> {
                     |diag, sp, _link_range| {
                         if let Some(sp) = sp {
                             diag.note("Explicit link does not affect the original link")
-                                .span_suggestion(sp, "Remove explicit link instead", format!("[{}]", ori_link.link), Applicability::MachineApplicable);
+                                .span_suggestion_hidden(
+                                    sp,
+                                    "Remove explicit link instead",
+                                    format!(""),
+                                    Applicability::MachineApplicable,
+                                );
                         }
-                    }
+                    },
                 );
             }
         }
diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.rs b/tests/rustdoc-ui/lints/redundant_explicit_links.rs
index e794388476b..d33396f6810 100644
--- a/tests/rustdoc-ui/lints/redundant_explicit_links.rs
+++ b/tests/rustdoc-ui/lints/redundant_explicit_links.rs
@@ -2,5 +2,20 @@
 
 pub fn dummy_target() {}
 
+/// [dummy_target](dummy_target)
+/// [`dummy_target`](dummy_target)
+/// [Vec](Vec)
+/// [`Vec`](Vec)
+/// [Vec](std::vec::Vec)
 /// [`Vec`](std::vec::Vec)
-pub fn c() {}
+/// [std::vec::Vec](std::vec::Vec)
+/// [`std::vec::Vec`](std::vec::Vec)
+/// [usize](usize)
+/// [`usize`](usize)
+/// [std::primitive::usize](usize)
+/// [`std::primitive::usize`](usize)
+pub fn should_warn() {}
+
+/// [`Vec<T>`](Vec)
+/// [`Vec<T>`](std::vec::Vec)
+pub fn should_not_warn() {}
diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr
new file mode 100644
index 00000000000..4ca427d62ce
--- /dev/null
+++ b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr
@@ -0,0 +1,97 @@
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:5:20
+   |
+LL | /// [dummy_target](dummy_target)
+   |                    ^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+note: the lint level is defined here
+  --> $DIR/redundant_explicit_links.rs:1:9
+   |
+LL | #![deny(rustdoc::redundant_explicit_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:6:22
+   |
+LL | /// [`dummy_target`](dummy_target)
+   |                      ^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:7:11
+   |
+LL | /// [Vec](Vec)
+   |           ^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:8:13
+   |
+LL | /// [`Vec`](Vec)
+   |             ^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:9:11
+   |
+LL | /// [Vec](std::vec::Vec)
+   |           ^^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:10:13
+   |
+LL | /// [`Vec`](std::vec::Vec)
+   |             ^^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:11:21
+   |
+LL | /// [std::vec::Vec](std::vec::Vec)
+   |                     ^^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:12:23
+   |
+LL | /// [`std::vec::Vec`](std::vec::Vec)
+   |                       ^^^^^^^^^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:13:13
+   |
+LL | /// [usize](usize)
+   |             ^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: redundant explicit rustdoc link
+  --> $DIR/redundant_explicit_links.rs:14:15
+   |
+LL | /// [`usize`](usize)
+   |               ^^^^^
+   |
+   = note: Explicit link does not affect the original link
+   = help: Remove explicit link instead
+
+error: aborting due to 10 previous errors
+