extra_fragment: Option<String>,
}
+#[derive(Clone)]
struct DiagnosticInfo<'a> {
item: &'a Item,
dox: &'a str,
}
impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
- fn fold_item(&mut self, mut item: Item) -> Option<Item> {
+ fn fold_item(&mut self, item: Item) -> Option<Item> {
use rustc_middle::ty::DefIdTree;
let parent_node = if item.is_fake() {
for md_link in markdown_links(&doc) {
let link = self.resolve_link(&item, &doc, &self_name, parent_node, krate, md_link);
if let Some(link) = link {
- item.attrs.links.push(link);
+ self.cx.cache.intra_doc_links.entry(item.def_id).or_default().push(link);
}
}
}
return None;
}
+ let diag_info = DiagnosticInfo {
+ item,
+ dox,
+ ori_link: &ori_link.link,
+ link_range: ori_link.range.clone(),
+ };
+
let link = ori_link.link.replace("`", "");
let no_backticks_range = range_between_backticks(&ori_link);
let parts = link.split('#').collect::<Vec<_>>();
let (link, extra_fragment) = if parts.len() > 2 {
// A valid link can't have multiple #'s
- anchor_failure(
- self.cx,
- &item,
- &link,
- dox,
- ori_link.range,
- AnchorFailure::MultipleAnchors,
- );
+ anchor_failure(self.cx, diag_info, AnchorFailure::MultipleAnchors);
return None;
} else if parts.len() == 2 {
if parts[0].trim().is_empty() {
// See issue #83859.
let disambiguator_range = (no_backticks_range.start + relative_range.start)
..(no_backticks_range.start + relative_range.end);
- disambiguator_error(self.cx, &item, dox, disambiguator_range, &err_msg);
+ disambiguator_error(self.cx, diag_info, disambiguator_range, &err_msg);
}
return None;
}
debug!("attempting to resolve item without parent module: {}", path_str);
resolution_failure(
self,
- &item,
+ diag_info,
path_str,
disambiguator,
- dox,
- ori_link.range,
smallvec![ResolutionFailure::NoParentItem],
);
return None;
debug!("link has malformed generics: {}", path_str);
resolution_failure(
self,
- &item,
+ diag_info,
path_str,
disambiguator,
- dox,
- ori_link.range,
smallvec![err_kind],
);
return None;
return None;
}
- let diag_info = DiagnosticInfo {
- item,
- dox,
- ori_link: &ori_link.link,
- link_range: ori_link.range.clone(),
- };
let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
ResolutionInfo {
module_id,
path_str: path_str.to_owned(),
extra_fragment,
},
- diag_info,
+ diag_info.clone(), // this struct should really be Copy, but Range is not :(
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
)?;
if fragment.is_some() {
anchor_failure(
self.cx,
- &item,
- path_str,
- dox,
- ori_link.range,
+ diag_info,
AnchorFailure::RustdocAnchorConflict(prim),
);
return None;
} else {
// `[char]` when a `char` module is in scope
let candidates = vec![res, prim];
- ambiguity_error(self.cx, &item, path_str, dox, ori_link.range, candidates);
+ ambiguity_error(self.cx, diag_info, path_str, candidates);
return None;
}
}
diag.note(¬e);
suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range);
};
- report_diagnostic(
- self.cx.tcx,
- BROKEN_INTRA_DOC_LINKS,
- &msg,
- &item,
- dox,
- &ori_link.range,
- callback,
- );
+ report_diagnostic(self.cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, callback);
};
let verify = |kind: DefKind, id: DefId| {
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)
{
- privacy_error(self.cx, &item, &path_str, dox, &ori_link);
+ privacy_error(self.cx, &diag_info, &path_str);
}
}
}
}
}
- resolution_failure(
- self,
- diag.item,
- path_str,
- disambiguator,
- diag.dox,
- diag.link_range,
- smallvec![kind],
- );
+ resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]);
// This could just be a normal link or a broken link
// we could potentially check if something is
// "intra-doc-link-like" and warn in that case.
None
}
Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(
- self.cx,
- diag.item,
- diag.ori_link,
- diag.dox,
- diag.link_range,
- msg,
- );
+ anchor_failure(self.cx, diag, msg);
None
}
}
Ok(res)
}
Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(
- self.cx,
- diag.item,
- diag.ori_link,
- diag.dox,
- diag.link_range,
- msg,
- );
+ anchor_failure(self.cx, diag, msg);
return None;
}
Err(ErrorKind::Resolve(box kind)) => Err(kind),
value_ns: match self.resolve(path_str, ValueNS, base_node, extra_fragment) {
Ok(res) => Ok(res),
Err(ErrorKind::AnchorFailure(msg)) => {
- anchor_failure(
- self.cx,
- diag.item,
- diag.ori_link,
- diag.dox,
- diag.link_range,
- msg,
- );
+ anchor_failure(self.cx, diag, msg);
return None;
}
Err(ErrorKind::Resolve(box kind)) => Err(kind),
if len == 0 {
resolution_failure(
self,
- diag.item,
+ diag,
path_str,
disambiguator,
- diag.dox,
- diag.link_range,
candidates.into_iter().filter_map(|res| res.err()).collect(),
);
// this could just be a normal link
}
// If we're reporting an ambiguity, don't mention the namespaces that failed
let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
- ambiguity_error(
- self.cx,
- diag.item,
- path_str,
- diag.dox,
- diag.link_range,
- candidates.present_items().collect(),
- );
+ ambiguity_error(self.cx, diag, path_str, candidates.present_items().collect());
None
}
}
break;
}
}
- resolution_failure(
- self,
- diag.item,
- path_str,
- disambiguator,
- diag.dox,
- diag.link_range,
- smallvec![kind],
- );
+ resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]);
None
}
}
tcx: TyCtxt<'_>,
lint: &'static Lint,
msg: &str,
- item: &Item,
- dox: &str,
- link_range: &Range<usize>,
+ DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>,
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
) {
let hir_id = match DocContext::as_local_hir_id(tcx, item.def_id) {
/// `std::io::Error::x`, this will resolve `std::io::Error`.
fn resolution_failure(
collector: &mut LinkCollector<'_, '_>,
- item: &Item,
+ diag_info: DiagnosticInfo<'_>,
path_str: &str,
disambiguator: Option<Disambiguator>,
- dox: &str,
- link_range: Range<usize>,
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
) {
let tcx = collector.cx.tcx;
tcx,
BROKEN_INTRA_DOC_LINKS,
&format!("unresolved link to `{}`", path_str),
- item,
- dox,
- &link_range,
+ &diag_info,
|diag, sp| {
let item = |res: Res| format!("the {} `{}`", res.descr(), res.name(tcx),);
let assoc_item_not_allowed = |res: Res| {
disambiguator,
diag,
path_str,
- dox,
+ diag_info.dox,
sp,
- &link_range,
+ &diag_info.link_range,
)
}
}
/// Report an anchor failure.
-fn anchor_failure(
- cx: &DocContext<'_>,
- item: &Item,
- path_str: &str,
- dox: &str,
- link_range: Range<usize>,
- failure: AnchorFailure,
-) {
+fn anchor_failure(cx: &DocContext<'_>, diag_info: DiagnosticInfo<'_>, failure: AnchorFailure) {
let msg = match failure {
- AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str),
+ AnchorFailure::MultipleAnchors => {
+ format!("`{}` contains multiple anchors", diag_info.ori_link)
+ }
AnchorFailure::RustdocAnchorConflict(res) => format!(
"`{}` contains an anchor, but links to {kind}s are already anchored",
- path_str,
+ diag_info.ori_link,
kind = res.descr(),
),
};
- report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
+ report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| {
if let Some(sp) = sp {
diag.span_label(sp, "contains invalid anchor");
}
+ if let AnchorFailure::RustdocAnchorConflict(Res::Primitive(_)) = failure {
+ diag.note("this restriction may be lifted in a future release");
+ diag.note("see https://github.com/rust-lang/rust/issues/83083 for more information");
+ }
});
}
/// Report an error in the link disambiguator.
fn disambiguator_error(
cx: &DocContext<'_>,
- item: &Item,
- dox: &str,
- link_range: Range<usize>,
+ mut diag_info: DiagnosticInfo<'_>,
+ disambiguator_range: Range<usize>,
msg: &str,
) {
- report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, item, dox, &link_range, |_diag, _sp| {});
+ diag_info.link_range = disambiguator_range;
+ report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |diag, _sp| {
+ let msg = format!(
+ "see https://doc.rust-lang.org/{}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators \
+ for more info about disambiguators",
+ crate::doc_rust_lang_org_channel(),
+ );
+ diag.note(&msg);
+ });
}
/// Report an ambiguity error, where there were multiple possible resolutions.
fn ambiguity_error(
cx: &DocContext<'_>,
- item: &Item,
+ diag_info: DiagnosticInfo<'_>,
path_str: &str,
- dox: &str,
- link_range: Range<usize>,
candidates: Vec<Res>,
) {
let mut msg = format!("`{}` is ", path_str);
}
}
- report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
+ report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, |diag, sp| {
if let Some(sp) = sp {
diag.span_label(sp, "ambiguous link");
} else {
for res in candidates {
let disambiguator = Disambiguator::from_res(res);
- suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range);
+ suggest_disambiguator(
+ disambiguator,
+ diag,
+ path_str,
+ diag_info.dox,
+ sp,
+ &diag_info.link_range,
+ );
}
});
}
}
/// Report a link from a public item to a private one.
-fn privacy_error(cx: &DocContext<'_>, item: &Item, path_str: &str, dox: &str, link: &MarkdownLink) {
+fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: &str) {
let sym;
- let item_name = match item.name {
+ let item_name = match diag_info.item.name {
Some(name) => {
sym = name.as_str();
&*sym
let msg =
format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
- report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link.range, |diag, sp| {
+ report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, &msg, diag_info, |diag, sp| {
if let Some(sp) = sp {
diag.span_label(sp, "this item is private");
}