Only report reference-style link errors once
Co-authored-by: Joshua Nelson <joshua@yottadb.com>
This commit is contained in:
parent
e821a6ef78
commit
4b612dd9cc
6 changed files with 175 additions and 67 deletions
|
@ -37,7 +37,9 @@ use crate::doctest;
|
||||||
use crate::html::highlight;
|
use crate::html::highlight;
|
||||||
use crate::html::toc::TocBuilder;
|
use crate::html::toc::TocBuilder;
|
||||||
|
|
||||||
use pulldown_cmark::{html, BrokenLink, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
|
use pulldown_cmark::{
|
||||||
|
html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -327,8 +329,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
type Item = Event<'a>;
|
type Item = Event<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
use pulldown_cmark::LinkType;
|
|
||||||
|
|
||||||
let mut event = self.inner.next();
|
let mut event = self.inner.next();
|
||||||
|
|
||||||
// Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
|
// Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
|
||||||
|
@ -1123,7 +1123,13 @@ crate fn plain_text_summary(md: &str) -> String {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
|
crate struct MarkdownLink {
|
||||||
|
pub kind: LinkType,
|
||||||
|
pub link: String,
|
||||||
|
pub range: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
crate fn markdown_links(md: &str) -> Vec<MarkdownLink> {
|
||||||
if md.is_empty() {
|
if md.is_empty() {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
@ -1163,7 +1169,11 @@ crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
|
||||||
|
|
||||||
let mut push = |link: BrokenLink<'_>| {
|
let mut push = |link: BrokenLink<'_>| {
|
||||||
let span = span_for_link(&CowStr::Borrowed(link.reference), link.span);
|
let span = span_for_link(&CowStr::Borrowed(link.reference), link.span);
|
||||||
links.borrow_mut().push((link.reference.to_owned(), span));
|
links.borrow_mut().push(MarkdownLink {
|
||||||
|
kind: LinkType::ShortcutUnknown,
|
||||||
|
link: link.reference.to_owned(),
|
||||||
|
range: span,
|
||||||
|
});
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push)).into_offset_iter();
|
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push)).into_offset_iter();
|
||||||
|
@ -1174,10 +1184,10 @@ crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
|
||||||
let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
|
let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
|
||||||
|
|
||||||
for ev in iter {
|
for ev in iter {
|
||||||
if let Event::Start(Tag::Link(_, dest, _)) = ev.0 {
|
if let Event::Start(Tag::Link(kind, dest, _)) = ev.0 {
|
||||||
debug!("found link: {}", dest);
|
debug!("found link: {}", dest);
|
||||||
let span = span_for_link(&dest, ev.1);
|
let span = span_for_link(&dest, ev.1);
|
||||||
links.borrow_mut().push((dest.into_string(), span));
|
links.borrow_mut().push(MarkdownLink { kind, link: dest.into_string(), range: span });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
|
use pulldown_cmark::LinkType;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
@ -34,7 +36,7 @@ use std::ops::Range;
|
||||||
use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
|
use crate::clean::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType};
|
||||||
use crate::core::DocContext;
|
use crate::core::DocContext;
|
||||||
use crate::fold::DocFolder;
|
use crate::fold::DocFolder;
|
||||||
use crate::html::markdown::markdown_links;
|
use crate::html::markdown::{markdown_links, MarkdownLink};
|
||||||
use crate::passes::Pass;
|
use crate::passes::Pass;
|
||||||
|
|
||||||
use super::span_of_attrs;
|
use super::span_of_attrs;
|
||||||
|
@ -265,8 +267,9 @@ struct LinkCollector<'a, 'tcx> {
|
||||||
/// because `clean` and the disambiguator code expect them to be different.
|
/// because `clean` and the disambiguator code expect them to be different.
|
||||||
/// See the code for associated items on inherent impls for details.
|
/// See the code for associated items on inherent impls for details.
|
||||||
kind_side_channel: Cell<Option<(DefKind, DefId)>>,
|
kind_side_channel: Cell<Option<(DefKind, DefId)>>,
|
||||||
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link
|
/// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
|
||||||
visited_links: FxHashMap<ResolutionInfo, CachedLink>,
|
/// The link will be `None` if it could not be resolved (i.e. the error was cached).
|
||||||
|
visited_links: FxHashMap<ResolutionInfo, Option<CachedLink>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
||||||
|
@ -901,16 +904,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
|
// NOTE: if there are links that start in one crate and end in another, this will not resolve them.
|
||||||
// This is a degenerate case and it's not supported by rustdoc.
|
// This is a degenerate case and it's not supported by rustdoc.
|
||||||
for (ori_link, link_range) in markdown_links(&doc) {
|
for md_link in markdown_links(&doc) {
|
||||||
let link = self.resolve_link(
|
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
|
||||||
&item,
|
|
||||||
&doc,
|
|
||||||
&self_name,
|
|
||||||
parent_node,
|
|
||||||
krate,
|
|
||||||
ori_link,
|
|
||||||
link_range,
|
|
||||||
);
|
|
||||||
if let Some(link) = link {
|
if let Some(link) = link {
|
||||||
item.attrs.links.push(link);
|
item.attrs.links.push(link);
|
||||||
}
|
}
|
||||||
|
@ -942,27 +937,26 @@ impl LinkCollector<'_, '_> {
|
||||||
self_name: &Option<String>,
|
self_name: &Option<String>,
|
||||||
parent_node: Option<DefId>,
|
parent_node: Option<DefId>,
|
||||||
krate: CrateNum,
|
krate: CrateNum,
|
||||||
ori_link: String,
|
ori_link: MarkdownLink,
|
||||||
link_range: Range<usize>,
|
|
||||||
) -> Option<ItemLink> {
|
) -> Option<ItemLink> {
|
||||||
trace!("considering link '{}'", ori_link);
|
trace!("considering link '{}'", ori_link.link);
|
||||||
|
|
||||||
// Bail early for real links.
|
// Bail early for real links.
|
||||||
if ori_link.contains('/') {
|
if ori_link.link.contains('/') {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// [] is mostly likely not supposed to be a link
|
// [] is mostly likely not supposed to be a link
|
||||||
if ori_link.is_empty() {
|
if ori_link.link.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cx = self.cx;
|
let cx = self.cx;
|
||||||
let link = ori_link.replace("`", "");
|
let link = ori_link.link.replace("`", "");
|
||||||
let parts = link.split('#').collect::<Vec<_>>();
|
let parts = link.split('#').collect::<Vec<_>>();
|
||||||
let (link, extra_fragment) = if parts.len() > 2 {
|
let (link, extra_fragment) = if parts.len() > 2 {
|
||||||
// A valid link can't have multiple #'s
|
// A valid link can't have multiple #'s
|
||||||
anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
|
anchor_failure(cx, &item, &link, dox, ori_link.range, AnchorFailure::MultipleAnchors);
|
||||||
return None;
|
return None;
|
||||||
} else if parts.len() == 2 {
|
} else if parts.len() == 2 {
|
||||||
if parts[0].trim().is_empty() {
|
if parts[0].trim().is_empty() {
|
||||||
|
@ -1018,7 +1012,7 @@ impl LinkCollector<'_, '_> {
|
||||||
path_str,
|
path_str,
|
||||||
disambiguator,
|
disambiguator,
|
||||||
dox,
|
dox,
|
||||||
link_range,
|
ori_link.range,
|
||||||
smallvec![ResolutionFailure::NoParentItem],
|
smallvec![ResolutionFailure::NoParentItem],
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
|
@ -1058,7 +1052,7 @@ impl LinkCollector<'_, '_> {
|
||||||
path_str,
|
path_str,
|
||||||
disambiguator,
|
disambiguator,
|
||||||
dox,
|
dox,
|
||||||
link_range,
|
ori_link.range,
|
||||||
smallvec![err_kind],
|
smallvec![err_kind],
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
|
@ -1074,15 +1068,22 @@ impl LinkCollector<'_, '_> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = ResolutionInfo {
|
let diag_info = DiagnosticInfo {
|
||||||
module_id,
|
item,
|
||||||
dis: disambiguator,
|
dox,
|
||||||
path_str: path_str.to_owned(),
|
ori_link: &ori_link.link,
|
||||||
extra_fragment,
|
link_range: ori_link.range.clone(),
|
||||||
};
|
};
|
||||||
let diag =
|
let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
|
||||||
DiagnosticInfo { item, dox, ori_link: &ori_link, link_range: link_range.clone() };
|
ResolutionInfo {
|
||||||
let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(key, diag)?;
|
module_id,
|
||||||
|
dis: disambiguator,
|
||||||
|
path_str: path_str.to_owned(),
|
||||||
|
extra_fragment,
|
||||||
|
},
|
||||||
|
diag_info,
|
||||||
|
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Check for a primitive which might conflict with a module
|
// Check for a primitive which might conflict with a module
|
||||||
// Report the ambiguity and require that the user specify which one they meant.
|
// Report the ambiguity and require that the user specify which one they meant.
|
||||||
|
@ -1101,7 +1102,7 @@ impl LinkCollector<'_, '_> {
|
||||||
&item,
|
&item,
|
||||||
path_str,
|
path_str,
|
||||||
dox,
|
dox,
|
||||||
link_range,
|
ori_link.range,
|
||||||
AnchorFailure::RustdocAnchorConflict(prim),
|
AnchorFailure::RustdocAnchorConflict(prim),
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
|
@ -1111,7 +1112,7 @@ impl LinkCollector<'_, '_> {
|
||||||
} else {
|
} else {
|
||||||
// `[char]` when a `char` module is in scope
|
// `[char]` when a `char` module is in scope
|
||||||
let candidates = vec![res, prim];
|
let candidates = vec![res, prim];
|
||||||
ambiguity_error(cx, &item, path_str, dox, link_range, candidates);
|
ambiguity_error(cx, &item, path_str, dox, ori_link.range, candidates);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1129,14 +1130,22 @@ impl LinkCollector<'_, '_> {
|
||||||
specified.descr()
|
specified.descr()
|
||||||
);
|
);
|
||||||
diag.note(¬e);
|
diag.note(¬e);
|
||||||
suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
|
suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range);
|
||||||
};
|
};
|
||||||
report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, &item, dox, &link_range, callback);
|
report_diagnostic(
|
||||||
|
cx,
|
||||||
|
BROKEN_INTRA_DOC_LINKS,
|
||||||
|
&msg,
|
||||||
|
&item,
|
||||||
|
dox,
|
||||||
|
&ori_link.range,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
match res {
|
match res {
|
||||||
Res::Primitive(_) => match disambiguator {
|
Res::Primitive(_) => match disambiguator {
|
||||||
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
|
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
|
||||||
Some(ItemLink { link: ori_link, link_text, did: None, fragment })
|
Some(ItemLink { link: ori_link.link, link_text, did: None, fragment })
|
||||||
}
|
}
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
report_mismatch(other, Disambiguator::Primitive);
|
report_mismatch(other, Disambiguator::Primitive);
|
||||||
|
@ -1179,11 +1188,11 @@ impl LinkCollector<'_, '_> {
|
||||||
if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
|
if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
|
||||||
&& !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
|
&& !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
|
||||||
{
|
{
|
||||||
privacy_error(cx, &item, &path_str, dox, link_range);
|
privacy_error(cx, &item, &path_str, dox, &ori_link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let id = clean::register_res(cx, rustc_hir::def::Res::Def(kind, id));
|
let id = clean::register_res(cx, rustc_hir::def::Res::Def(kind, id));
|
||||||
Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment })
|
Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1192,28 +1201,47 @@ impl LinkCollector<'_, '_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
key: ResolutionInfo,
|
key: ResolutionInfo,
|
||||||
diag: DiagnosticInfo<'_>,
|
diag: DiagnosticInfo<'_>,
|
||||||
|
cache_resolution_failure: bool,
|
||||||
) -> Option<(Res, Option<String>)> {
|
) -> Option<(Res, Option<String>)> {
|
||||||
// Try to look up both the result and the corresponding side channel value
|
// Try to look up both the result and the corresponding side channel value
|
||||||
if let Some(ref cached) = self.visited_links.get(&key) {
|
if let Some(ref cached) = self.visited_links.get(&key) {
|
||||||
self.kind_side_channel.set(cached.side_channel);
|
match cached {
|
||||||
return Some(cached.res.clone());
|
Some(cached) => {
|
||||||
|
self.kind_side_channel.set(cached.side_channel.clone());
|
||||||
|
return Some(cached.res.clone());
|
||||||
|
}
|
||||||
|
None if cache_resolution_failure => return None,
|
||||||
|
None => {
|
||||||
|
// Although we hit the cache and found a resolution error, this link isn't
|
||||||
|
// supposed to cache those. Run link resolution again to emit the expected
|
||||||
|
// resolution error.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = self.resolve_with_disambiguator(&key, diag);
|
let res = self.resolve_with_disambiguator(&key, diag);
|
||||||
|
|
||||||
// Cache only if resolved successfully - don't silence duplicate errors
|
// Cache only if resolved successfully - don't silence duplicate errors
|
||||||
if let Some(res) = &res {
|
if let Some(res) = res {
|
||||||
// Store result for the actual namespace
|
// Store result for the actual namespace
|
||||||
self.visited_links.insert(
|
self.visited_links.insert(
|
||||||
key,
|
key,
|
||||||
CachedLink {
|
Some(CachedLink {
|
||||||
res: res.clone(),
|
res: res.clone(),
|
||||||
side_channel: self.kind_side_channel.clone().into_inner(),
|
side_channel: self.kind_side_channel.clone().into_inner(),
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
res
|
Some(res)
|
||||||
|
} else {
|
||||||
|
if cache_resolution_failure {
|
||||||
|
// For reference-style links we only want to report one resolution error
|
||||||
|
// so let's cache them as well.
|
||||||
|
self.visited_links.insert(key, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// After parsing the disambiguator, resolve the main part of the link.
|
/// After parsing the disambiguator, resolve the main part of the link.
|
||||||
|
@ -1964,13 +1992,7 @@ fn suggest_disambiguator(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report a link from a public item to a private one.
|
/// Report a link from a public item to a private one.
|
||||||
fn privacy_error(
|
fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link: &MarkdownLink) {
|
||||||
cx: &DocContext<'_>,
|
|
||||||
item: &Item,
|
|
||||||
path_str: &str,
|
|
||||||
dox: &str,
|
|
||||||
link_range: Range<usize>,
|
|
||||||
) {
|
|
||||||
let sym;
|
let sym;
|
||||||
let item_name = match item.name {
|
let item_name = match item.name {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
|
@ -1982,7 +2004,7 @@ fn privacy_error(
|
||||||
let msg =
|
let msg =
|
||||||
format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
|
format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
|
||||||
|
|
||||||
report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
|
report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link.range, |diag, sp| {
|
||||||
if let Some(sp) = sp {
|
if let Some(sp) = sp {
|
||||||
diag.span_label(sp, "this item is private");
|
diag.span_label(sp, "this item is private");
|
||||||
}
|
}
|
||||||
|
|
20
src/test/rustdoc-ui/reference-link-reports-error-once.rs
Normal file
20
src/test/rustdoc-ui/reference-link-reports-error-once.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![deny(broken_intra_doc_links)]
|
||||||
|
|
||||||
|
/// Links to [a] [link][a]
|
||||||
|
/// And also a [third link][a]
|
||||||
|
/// And also a [reference link][b]
|
||||||
|
///
|
||||||
|
/// Other links to the same target should still emit error: [ref] //~ERROR unresolved link to `ref`
|
||||||
|
/// Duplicate [ref] //~ERROR unresolved link to `ref`
|
||||||
|
///
|
||||||
|
/// Other links to other targets should still emit error: [ref2] //~ERROR unresolved link to `ref2`
|
||||||
|
/// Duplicate [ref2] //~ERROR unresolved link to `ref2`
|
||||||
|
///
|
||||||
|
/// [a]: ref
|
||||||
|
//~^ ERROR unresolved link to `ref`
|
||||||
|
/// [b]: ref2
|
||||||
|
//~^ ERROR unresolved link to
|
||||||
|
|
||||||
|
/// [ref][]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
pub fn f() {}
|
63
src/test/rustdoc-ui/reference-link-reports-error-once.stderr
Normal file
63
src/test/rustdoc-ui/reference-link-reports-error-once.stderr
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
error: unresolved link to `ref`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:13:10
|
||||||
|
|
|
||||||
|
LL | /// [a]: ref
|
||||||
|
| ^^^ no item named `ref` in scope
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:1:9
|
||||||
|
|
|
||||||
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref2`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:15:10
|
||||||
|
|
|
||||||
|
LL | /// [b]: ref2
|
||||||
|
| ^^^^ no item named `ref2` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:7:62
|
||||||
|
|
|
||||||
|
LL | /// Other links to the same target should still emit error: [ref]
|
||||||
|
| ^^^ no item named `ref` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:8:16
|
||||||
|
|
|
||||||
|
LL | /// Duplicate [ref]
|
||||||
|
| ^^^ no item named `ref` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref2`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:10:60
|
||||||
|
|
|
||||||
|
LL | /// Other links to other targets should still emit error: [ref2]
|
||||||
|
| ^^^^ no item named `ref2` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref2`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:11:16
|
||||||
|
|
|
||||||
|
LL | /// Duplicate [ref2]
|
||||||
|
| ^^^^ no item named `ref2` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `ref`
|
||||||
|
--> $DIR/reference-link-reports-error-once.rs:18:6
|
||||||
|
|
|
||||||
|
LL | /// [ref][]
|
||||||
|
| ^^^ no item named `ref` in scope
|
||||||
|
|
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
|
@ -4,4 +4,3 @@
|
||||||
//!
|
//!
|
||||||
//! [a]: std::process::Comman
|
//! [a]: std::process::Comman
|
||||||
//~^ ERROR unresolved
|
//~^ ERROR unresolved
|
||||||
//~| ERROR unresolved
|
|
||||||
|
|
|
@ -10,11 +10,5 @@ note: the lint level is defined here
|
||||||
LL | #![deny(broken_intra_doc_links)]
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unresolved link to `std::process::Comman`
|
error: aborting due to previous error
|
||||||
--> $DIR/reference-links.rs:5:10
|
|
||||||
|
|
|
||||||
LL | //! [a]: std::process::Comman
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ no item named `Comman` in module `process`
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue