mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
[WIP] give better errors for broken intra doc links
This commit is contained in:
parent
7d289aeade
commit
3797f29aad
@ -6,6 +6,7 @@ use rustc_ast::NodeId;
|
|||||||
use rustc_macros::HashStable_Generic;
|
use rustc_macros::HashStable_Generic;
|
||||||
use rustc_span::hygiene::MacroKind;
|
use rustc_span::hygiene::MacroKind;
|
||||||
|
|
||||||
|
use std::array::IntoIter;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
|
/// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
|
||||||
@ -291,6 +292,14 @@ impl<T> PerNS<T> {
|
|||||||
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
|
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
|
||||||
PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
|
PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(self) -> IntoIter<T, 3> {
|
||||||
|
IntoIter::new([self.value_ns, self.type_ns, self.macro_ns])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> IntoIter<&T, 3> {
|
||||||
|
IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
|
impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
|
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html
|
||||||
|
|
||||||
|
#![feature(array_value_iter)]
|
||||||
#![feature(crate_visibility_modifier)]
|
#![feature(crate_visibility_modifier)]
|
||||||
#![feature(const_fn)] // For the unsizing cast on `&[]`
|
#![feature(const_fn)] // For the unsizing cast on `&[]`
|
||||||
#![feature(const_panic)]
|
#![feature(const_panic)]
|
||||||
|
@ -17,7 +17,7 @@ use rustc_span::hygiene::MacroKind;
|
|||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
@ -47,10 +47,53 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ErrorKind {
|
enum ErrorKind {
|
||||||
ResolutionFailure,
|
Resolve(ResolutionFailure),
|
||||||
AnchorFailure(AnchorFailure),
|
AnchorFailure(AnchorFailure),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum ResolutionFailure {
|
||||||
|
/// This resolved, but with the wrong namespace.
|
||||||
|
/// `Namespace` is the expected namespace (as opposed to the actual).
|
||||||
|
WrongNamespace(Res, Namespace),
|
||||||
|
/// `String` is the base name of the path (not necessarily the whole link)
|
||||||
|
NotInScope(String),
|
||||||
|
/// this is a primitive type without an impls (no associated methods)
|
||||||
|
/// when will this actually happen?
|
||||||
|
/// the `Res` is the primitive it resolved to
|
||||||
|
NoPrimitiveImpl(Res, String),
|
||||||
|
/// `[u8::not_found]`
|
||||||
|
/// the `Res` is the primitive it resolved to
|
||||||
|
NoPrimitiveAssocItem { res: Res, prim_name: String, assoc_item: String },
|
||||||
|
/// `[S::not_found]`
|
||||||
|
/// the `String` is the associated item that wasn't found
|
||||||
|
NoAssocItem(Res, String),
|
||||||
|
/// should not ever happen
|
||||||
|
NoParentItem,
|
||||||
|
/// the root of this path resolved, but it was not an enum.
|
||||||
|
NotAnEnum(Res),
|
||||||
|
/// this could be an enum variant, but the last path fragment wasn't resolved.
|
||||||
|
/// the `String` is the variant that didn't exist
|
||||||
|
NotAVariant(Res, String),
|
||||||
|
/// used to communicate that this should be ignored, but shouldn't be reported to the user
|
||||||
|
Dummy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResolutionFailure {
|
||||||
|
fn res(&self) -> Option<Res> {
|
||||||
|
use ResolutionFailure::*;
|
||||||
|
match self {
|
||||||
|
NoPrimitiveAssocItem { res, .. }
|
||||||
|
| NoAssocItem(res, _)
|
||||||
|
| NoPrimitiveImpl(res, _)
|
||||||
|
| NotAnEnum(res)
|
||||||
|
| NotAVariant(res, _)
|
||||||
|
| WrongNamespace(res, _) => Some(*res),
|
||||||
|
NotInScope(_) | NoParentItem | Dummy => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum AnchorFailure {
|
enum AnchorFailure {
|
||||||
MultipleAnchors,
|
MultipleAnchors,
|
||||||
Primitive,
|
Primitive,
|
||||||
@ -85,10 +128,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
let cx = self.cx;
|
let cx = self.cx;
|
||||||
|
|
||||||
let mut split = path_str.rsplitn(3, "::");
|
let mut split = path_str.rsplitn(3, "::");
|
||||||
let variant_field_name =
|
let variant_field_name = split
|
||||||
split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
|
.next()
|
||||||
|
.map(|f| Symbol::intern(f))
|
||||||
|
.expect("fold_item should ensure link is non-empty");
|
||||||
let variant_name =
|
let variant_name =
|
||||||
split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
|
// we're not sure this is a variant at all, so use the full string
|
||||||
|
split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.to_string())))?;
|
||||||
|
// TODO: this looks very wrong, why are we requiring 3 fields?
|
||||||
let path = split
|
let path = split
|
||||||
.next()
|
.next()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
@ -99,14 +146,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
f.to_owned()
|
f.to_owned()
|
||||||
})
|
})
|
||||||
.ok_or(ErrorKind::ResolutionFailure)?;
|
// TODO: is this right?
|
||||||
|
.ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(variant_name.to_string())))?;
|
||||||
let (_, ty_res) = cx
|
let (_, ty_res) = cx
|
||||||
.enter_resolver(|resolver| {
|
.enter_resolver(|resolver| {
|
||||||
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
|
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
|
||||||
})
|
})
|
||||||
.map_err(|_| ErrorKind::ResolutionFailure)?;
|
.map_err(|_| ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string())))?;
|
||||||
if let Res::Err = ty_res {
|
if let Res::Err = ty_res {
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string())));
|
||||||
}
|
}
|
||||||
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
|
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
|
||||||
match ty_res {
|
match ty_res {
|
||||||
@ -118,7 +166,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
.flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
|
.flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
|
||||||
.any(|item| item.ident.name == variant_name)
|
.any(|item| item.ident.name == variant_name)
|
||||||
{
|
{
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
// This is just to let `fold_item` know that this shouldn't be considered;
|
||||||
|
// it's a bug for the error to make it to the user
|
||||||
|
return Err(ErrorKind::Resolve(ResolutionFailure::Dummy));
|
||||||
}
|
}
|
||||||
match cx.tcx.type_of(did).kind() {
|
match cx.tcx.type_of(did).kind() {
|
||||||
ty::Adt(def, _) if def.is_enum() => {
|
ty::Adt(def, _) if def.is_enum() => {
|
||||||
@ -131,18 +181,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::ResolutionFailure)
|
Err(ErrorKind::Resolve(ResolutionFailure::NotAVariant(
|
||||||
|
ty_res,
|
||||||
|
variant_field_name.to_string(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(ErrorKind::ResolutionFailure),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(ErrorKind::ResolutionFailure),
|
_ => Err(ErrorKind::Resolve(ResolutionFailure::NotAnEnum(ty_res))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves a string as a macro.
|
/// Resolves a string as a macro.
|
||||||
fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res> {
|
fn macro_resolve(
|
||||||
|
&self,
|
||||||
|
path_str: &str,
|
||||||
|
parent_id: Option<DefId>,
|
||||||
|
) -> Result<Res, ResolutionFailure> {
|
||||||
let cx = self.cx;
|
let cx = self.cx;
|
||||||
let path = ast::Path::from_ident(Ident::from_str(path_str));
|
let path = ast::Path::from_ident(Ident::from_str(path_str));
|
||||||
cx.enter_resolver(|resolver| {
|
cx.enter_resolver(|resolver| {
|
||||||
@ -154,11 +211,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
|
if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
|
||||||
return Some(res.map_id(|_| panic!("unexpected id")));
|
return Ok(res.map_id(|_| panic!("unexpected id")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
|
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
|
||||||
return Some(res.map_id(|_| panic!("unexpected id")));
|
return Ok(res.map_id(|_| panic!("unexpected id")));
|
||||||
}
|
}
|
||||||
if let Some(module_id) = parent_id {
|
if let Some(module_id) = parent_id {
|
||||||
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
|
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
|
||||||
@ -168,13 +225,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
// don't resolve builtins like `#[derive]`
|
// don't resolve builtins like `#[derive]`
|
||||||
if let Res::Def(..) = res {
|
if let Res::Def(..) = res {
|
||||||
let res = res.map_id(|_| panic!("unexpected node_id"));
|
let res = res.map_id(|_| panic!("unexpected node_id"));
|
||||||
return Some(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("attempting to resolve item without parent module: {}", path_str);
|
debug!("attempting to resolve item without parent module: {}", path_str);
|
||||||
|
return Err(ResolutionFailure::NoParentItem);
|
||||||
}
|
}
|
||||||
None
|
return Err(ResolutionFailure::NotInScope(path_str.to_string()));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// Resolves a string as a path within a particular namespace. Also returns an optional
|
/// Resolves a string as a path within a particular namespace. Also returns an optional
|
||||||
@ -196,8 +254,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
});
|
});
|
||||||
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
|
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
|
||||||
let result = match result {
|
let result = match result {
|
||||||
Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
|
Ok((_, Res::Err)) => Err(()),
|
||||||
_ => result.map_err(|_| ErrorKind::ResolutionFailure),
|
_ => result.map_err(|_| ()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok((_, res)) = result {
|
if let Ok((_, res)) = result {
|
||||||
@ -226,7 +284,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if value != (ns == ValueNS) {
|
if value != (ns == ValueNS) {
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
return Err(ErrorKind::Resolve(ResolutionFailure::WrongNamespace(res, ns)));
|
||||||
}
|
}
|
||||||
} else if let Some((path, prim)) = is_primitive(path_str, ns) {
|
} else if let Some((path, prim)) = is_primitive(path_str, ns) {
|
||||||
if extra_fragment.is_some() {
|
if extra_fragment.is_some() {
|
||||||
@ -237,9 +295,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
|
|
||||||
// Try looking for methods and associated items.
|
// Try looking for methods and associated items.
|
||||||
let mut split = path_str.rsplitn(2, "::");
|
let mut split = path_str.rsplitn(2, "::");
|
||||||
let item_name =
|
// this can be an `unwrap()` because we ensure the link is never empty
|
||||||
split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
|
let item_name = Symbol::intern(split.next().unwrap());
|
||||||
let path = split
|
let path_root = split
|
||||||
.next()
|
.next()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
if f == "self" || f == "Self" {
|
if f == "self" || f == "Self" {
|
||||||
@ -249,10 +307,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
f.to_owned()
|
f.to_owned()
|
||||||
})
|
})
|
||||||
.ok_or(ErrorKind::ResolutionFailure)?;
|
// If there's no `::`, it's not an associated item.
|
||||||
|
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
|
||||||
|
.ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string())))?;
|
||||||
|
|
||||||
if let Some((path, prim)) = is_primitive(&path, TypeNS) {
|
if let Some((path, prim)) = is_primitive(&path_root, ns) {
|
||||||
for &impl_ in primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)? {
|
let impls = primitive_impl(cx, &path).ok_or_else(|| {
|
||||||
|
ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root))
|
||||||
|
})?;
|
||||||
|
for &impl_ in impls {
|
||||||
let link = cx
|
let link = cx
|
||||||
.tcx
|
.tcx
|
||||||
.associated_items(impl_)
|
.associated_items(impl_)
|
||||||
@ -272,19 +335,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
return Ok(link);
|
return Ok(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
return Err(ErrorKind::Resolve(ResolutionFailure::NoPrimitiveAssocItem {
|
||||||
|
res: prim,
|
||||||
|
prim_name: path.to_string(),
|
||||||
|
assoc_item: item_name.to_string(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, ty_res) = cx
|
let (_, ty_res) = cx
|
||||||
.enter_resolver(|resolver| {
|
.enter_resolver(|resolver| {
|
||||||
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
|
resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
|
||||||
})
|
})
|
||||||
.map_err(|_| ErrorKind::ResolutionFailure)?;
|
.map_err(|_| {
|
||||||
|
ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone()))
|
||||||
|
})?;
|
||||||
if let Res::Err = ty_res {
|
if let Res::Err = ty_res {
|
||||||
return if ns == Namespace::ValueNS {
|
return if ns == Namespace::ValueNS {
|
||||||
self.variant_field(path_str, current_item, module_id)
|
self.variant_field(path_str, current_item, module_id)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::ResolutionFailure)
|
Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root)))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
|
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
|
||||||
@ -380,7 +449,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We already know this isn't in ValueNS, so no need to check variant_field
|
// We already know this isn't in ValueNS, so no need to check variant_field
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem(
|
||||||
|
ty_res,
|
||||||
|
item_name.to_string(),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Res::Def(DefKind::Trait, did) => cx
|
Res::Def(DefKind::Trait, did) => cx
|
||||||
@ -419,12 +491,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
|||||||
if ns == Namespace::ValueNS {
|
if ns == Namespace::ValueNS {
|
||||||
self.variant_field(path_str, current_item, module_id)
|
self.variant_field(path_str, current_item, module_id)
|
||||||
} else {
|
} else {
|
||||||
Err(ErrorKind::ResolutionFailure)
|
Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem(
|
||||||
|
ty_res,
|
||||||
|
item_name.to_string(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
debug!("attempting to resolve item without parent module: {}", path_str);
|
debug!("attempting to resolve item without parent module: {}", path_str);
|
||||||
Err(ErrorKind::ResolutionFailure)
|
// TODO: maybe this should just be an ICE?
|
||||||
|
Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -562,10 +638,10 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx
|
|||||||
/// Check for resolve collisions between a trait and its derive
|
/// Check for resolve collisions between a trait and its derive
|
||||||
///
|
///
|
||||||
/// These are common and we should just resolve to the trait in that case
|
/// These are common and we should just resolve to the trait in that case
|
||||||
fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
|
fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure>>) -> bool {
|
||||||
if let PerNS {
|
if let PerNS {
|
||||||
type_ns: Some((Res::Def(DefKind::Trait, _), _)),
|
type_ns: Ok((Res::Def(DefKind::Trait, _), _)),
|
||||||
macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
|
macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
|
||||||
..
|
..
|
||||||
} = *ns
|
} = *ns
|
||||||
{
|
{
|
||||||
@ -764,8 +840,15 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||||||
match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment)
|
match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment)
|
||||||
{
|
{
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(ErrorKind::ResolutionFailure) => {
|
Err(ErrorKind::Resolve(kind)) => {
|
||||||
resolution_failure(cx, &item, path_str, &dox, link_range);
|
resolution_failure(
|
||||||
|
cx,
|
||||||
|
&item,
|
||||||
|
path_str,
|
||||||
|
&dox,
|
||||||
|
link_range,
|
||||||
|
smallvec![kind],
|
||||||
|
);
|
||||||
// This could just be a normal link or a broken link
|
// This could just be a normal link or a broken link
|
||||||
// we could potentially check if something is
|
// we could potentially check if something is
|
||||||
// "intra-doc-link-like" and warn in that case.
|
// "intra-doc-link-like" and warn in that case.
|
||||||
@ -792,13 +875,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||||||
) {
|
) {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
debug!("got res in TypeNS: {:?}", res);
|
debug!("got res in TypeNS: {:?}", res);
|
||||||
Some(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
Err(ErrorKind::AnchorFailure(msg)) => {
|
Err(ErrorKind::AnchorFailure(msg)) => {
|
||||||
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
|
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Err(ErrorKind::ResolutionFailure) => None,
|
Err(ErrorKind::Resolve(kind)) => Err(kind),
|
||||||
},
|
},
|
||||||
value_ns: match self.resolve(
|
value_ns: match self.resolve(
|
||||||
path_str,
|
path_str,
|
||||||
@ -807,48 +890,62 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||||||
base_node,
|
base_node,
|
||||||
&extra_fragment,
|
&extra_fragment,
|
||||||
) {
|
) {
|
||||||
Ok(res) => Some(res),
|
Ok(res) => Ok(res),
|
||||||
Err(ErrorKind::AnchorFailure(msg)) => {
|
Err(ErrorKind::AnchorFailure(msg)) => {
|
||||||
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
|
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Err(ErrorKind::ResolutionFailure) => None,
|
Err(ErrorKind::Resolve(kind)) => Err(kind),
|
||||||
}
|
}
|
||||||
.and_then(|(res, fragment)| {
|
.and_then(|(res, fragment)| {
|
||||||
// Constructors are picked up in the type namespace.
|
// Constructors are picked up in the type namespace.
|
||||||
match res {
|
match res {
|
||||||
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
|
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
|
||||||
|
Err(ResolutionFailure::WrongNamespace(res, TypeNS))
|
||||||
|
}
|
||||||
_ => match (fragment, extra_fragment) {
|
_ => match (fragment, extra_fragment) {
|
||||||
(Some(fragment), Some(_)) => {
|
(Some(fragment), Some(_)) => {
|
||||||
// Shouldn't happen but who knows?
|
// Shouldn't happen but who knows?
|
||||||
Some((res, Some(fragment)))
|
Ok((res, Some(fragment)))
|
||||||
}
|
|
||||||
(fragment, None) | (None, fragment) => {
|
|
||||||
Some((res, fragment))
|
|
||||||
}
|
}
|
||||||
|
(fragment, None) | (None, fragment) => Ok((res, fragment)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if candidates.is_empty() {
|
let mut candidates_iter =
|
||||||
resolution_failure(cx, &item, path_str, &dox, link_range);
|
candidates.iter().filter_map(|res| res.as_ref().ok());
|
||||||
|
let len = candidates_iter.clone().count();
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
drop(candidates_iter);
|
||||||
|
resolution_failure(
|
||||||
|
cx,
|
||||||
|
&item,
|
||||||
|
path_str,
|
||||||
|
&dox,
|
||||||
|
link_range,
|
||||||
|
candidates.into_iter().filter_map(|res| res.err()).collect(),
|
||||||
|
);
|
||||||
// this could just be a normal link
|
// this could just be a normal link
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = candidates.clone().present_items().count();
|
|
||||||
|
|
||||||
if len == 1 {
|
if len == 1 {
|
||||||
candidates.present_items().next().unwrap()
|
candidates_iter.next().unwrap().clone()
|
||||||
} else if len == 2 && is_derive_trait_collision(&candidates) {
|
} else if len == 2 && is_derive_trait_collision(&candidates) {
|
||||||
|
drop(candidates_iter);
|
||||||
candidates.type_ns.unwrap()
|
candidates.type_ns.unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
drop(candidates_iter);
|
||||||
if is_derive_trait_collision(&candidates) {
|
if is_derive_trait_collision(&candidates) {
|
||||||
candidates.macro_ns = None;
|
candidates.macro_ns =
|
||||||
|
Err(ResolutionFailure::NotInScope(path_str.to_string()));
|
||||||
}
|
}
|
||||||
|
// If we're reporting an ambiguity, don't mention the namespaces that failed
|
||||||
let candidates =
|
let candidates =
|
||||||
candidates.map(|candidate| candidate.map(|(res, _)| res));
|
candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
|
||||||
ambiguity_error(
|
ambiguity_error(
|
||||||
cx,
|
cx,
|
||||||
&item,
|
&item,
|
||||||
@ -861,11 +958,44 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(MacroNS) => {
|
Some(MacroNS) => {
|
||||||
if let Some(res) = self.macro_resolve(path_str, base_node) {
|
match self.macro_resolve(path_str, base_node) {
|
||||||
(res, extra_fragment)
|
Ok(res) => (res, extra_fragment),
|
||||||
} else {
|
Err(mut kind) => {
|
||||||
resolution_failure(cx, &item, path_str, &dox, link_range);
|
// `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
|
||||||
continue;
|
for &ns in &[TypeNS, ValueNS] {
|
||||||
|
match self.resolve(
|
||||||
|
path_str,
|
||||||
|
ns,
|
||||||
|
¤t_item,
|
||||||
|
base_node,
|
||||||
|
&extra_fragment,
|
||||||
|
) {
|
||||||
|
Ok(res) => {
|
||||||
|
kind = ResolutionFailure::WrongNamespace(res.0, MacroNS)
|
||||||
|
}
|
||||||
|
// This will show up in the other namespace, no need to handle it here
|
||||||
|
Err(ErrorKind::Resolve(
|
||||||
|
ResolutionFailure::WrongNamespace(..),
|
||||||
|
)) => {}
|
||||||
|
Err(ErrorKind::AnchorFailure(_)) => {}
|
||||||
|
Err(ErrorKind::Resolve(inner_kind)) => {
|
||||||
|
if let Some(res) = inner_kind.res() {
|
||||||
|
kind =
|
||||||
|
ResolutionFailure::WrongNamespace(res, MacroNS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolution_failure(
|
||||||
|
cx,
|
||||||
|
&item,
|
||||||
|
path_str,
|
||||||
|
&dox,
|
||||||
|
link_range,
|
||||||
|
smallvec![kind],
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -907,7 +1037,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
|
|||||||
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
|
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
|
||||||
// The resolved item did not match the disambiguator; give a better error than 'not found'
|
// The resolved item did not match the disambiguator; give a better error than 'not found'
|
||||||
let msg = format!("incompatible link kind for `{}`", path_str);
|
let msg = format!("incompatible link kind for `{}`", path_str);
|
||||||
report_diagnostic(cx, &msg, &item, &dox, link_range.clone(), |diag, sp| {
|
report_diagnostic(cx, &msg, &item, &dox, &link_range, |diag, sp| {
|
||||||
let note = format!(
|
let note = format!(
|
||||||
"this link resolved to {} {}, which is not {} {}",
|
"this link resolved to {} {}, which is not {} {}",
|
||||||
resolved.article(),
|
resolved.article(),
|
||||||
@ -1161,7 +1291,7 @@ fn report_diagnostic(
|
|||||||
msg: &str,
|
msg: &str,
|
||||||
item: &Item,
|
item: &Item,
|
||||||
dox: &str,
|
dox: &str,
|
||||||
link_range: Option<Range<usize>>,
|
link_range: &Option<Range<usize>>,
|
||||||
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
|
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
|
||||||
) {
|
) {
|
||||||
let hir_id = match cx.as_local_hir_id(item.def_id) {
|
let hir_id = match cx.as_local_hir_id(item.def_id) {
|
||||||
@ -1218,19 +1348,107 @@ fn resolution_failure(
|
|||||||
path_str: &str,
|
path_str: &str,
|
||||||
dox: &str,
|
dox: &str,
|
||||||
link_range: Option<Range<usize>>,
|
link_range: Option<Range<usize>>,
|
||||||
|
kinds: SmallVec<[ResolutionFailure; 3]>,
|
||||||
) {
|
) {
|
||||||
report_diagnostic(
|
report_diagnostic(
|
||||||
cx,
|
cx,
|
||||||
&format!("unresolved link to `{}`", path_str),
|
&format!("unresolved link to `{}`", path_str),
|
||||||
item,
|
item,
|
||||||
dox,
|
dox,
|
||||||
link_range,
|
&link_range,
|
||||||
|diag, sp| {
|
|diag, sp| {
|
||||||
if let Some(sp) = sp {
|
let in_scope = kinds.iter().any(|kind| kind.res().is_some());
|
||||||
diag.span_label(sp, "unresolved link");
|
let mut reported_not_in_scope = false;
|
||||||
}
|
let item = |res: Res| {
|
||||||
|
if let Some(id) = res.opt_def_id() {
|
||||||
|
(format!("the {} `{}`", res.descr(), cx.tcx.item_name(id).to_string()), ",")
|
||||||
|
} else {
|
||||||
|
(format!("{} {}", res.article(), res.descr()), "")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for failure in kinds {
|
||||||
|
match failure {
|
||||||
|
// already handled above
|
||||||
|
ResolutionFailure::NotInScope(base) => {
|
||||||
|
if in_scope || reported_not_in_scope {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reported_not_in_scope = true;
|
||||||
|
diag.note(&format!("no item named `{}` is in scope", base));
|
||||||
|
diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
|
||||||
|
}
|
||||||
|
ResolutionFailure::Dummy => continue,
|
||||||
|
ResolutionFailure::WrongNamespace(res, expected_ns) => {
|
||||||
|
let (item, comma) = item(res);
|
||||||
|
let note = format!(
|
||||||
|
"this link resolves to {}{} which is not in the {} namespace",
|
||||||
|
item,
|
||||||
|
comma,
|
||||||
|
expected_ns.descr()
|
||||||
|
);
|
||||||
|
diag.note(¬e);
|
||||||
|
|
||||||
diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
|
if let Res::Def(kind, _) = res {
|
||||||
|
let disambiguator = Disambiguator::Kind(kind);
|
||||||
|
suggest_disambiguator(
|
||||||
|
disambiguator,
|
||||||
|
diag,
|
||||||
|
path_str,
|
||||||
|
dox,
|
||||||
|
sp,
|
||||||
|
&link_range,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResolutionFailure::NoParentItem => {
|
||||||
|
panic!("all intra doc links should have a parent item")
|
||||||
|
}
|
||||||
|
ResolutionFailure::NoPrimitiveImpl(res, _) => {
|
||||||
|
let (item, comma) = item(res);
|
||||||
|
let note = format!(
|
||||||
|
"this link partially resolves to {}{} which does not have any associated items",
|
||||||
|
item, comma,
|
||||||
|
);
|
||||||
|
diag.note(¬e);
|
||||||
|
}
|
||||||
|
ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => {
|
||||||
|
let note = format!(
|
||||||
|
"the builtin type `{}` does not have an associated item named `{}`",
|
||||||
|
prim_name, assoc_item
|
||||||
|
);
|
||||||
|
diag.note(¬e);
|
||||||
|
}
|
||||||
|
ResolutionFailure::NoAssocItem(res, assoc_item) => {
|
||||||
|
let (item, _) = item(res);
|
||||||
|
diag.note(&format!("this link partially resolves to {}", item));
|
||||||
|
// FIXME: when are items neither a primitive nor a Def?
|
||||||
|
if let Res::Def(_, def_id) = res {
|
||||||
|
let name = cx.tcx.item_name(def_id);
|
||||||
|
let note = format!(
|
||||||
|
"`{}` has no field, variant, or associated item named `{}`",
|
||||||
|
name, assoc_item
|
||||||
|
);
|
||||||
|
diag.note(¬e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: is there ever a case where this happens?
|
||||||
|
ResolutionFailure::NotAnEnum(res) => {
|
||||||
|
let (item, comma) = item(res);
|
||||||
|
let note =
|
||||||
|
format!("this link resolves to {}{} which is not an enum", item, comma);
|
||||||
|
diag.note(¬e);
|
||||||
|
diag.note("if this were an enum, it might have a variant which resolved");
|
||||||
|
}
|
||||||
|
ResolutionFailure::NotAVariant(res, variant) => {
|
||||||
|
let note = format!(
|
||||||
|
"this link partially resolves to {}, but there is no variant named {}",
|
||||||
|
item(res).0,
|
||||||
|
variant
|
||||||
|
);
|
||||||
|
diag.note(¬e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1269,7 +1487,7 @@ fn anchor_failure(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| {
|
report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| {
|
||||||
if let Some(sp) = sp {
|
if let Some(sp) = sp {
|
||||||
diag.span_label(sp, "contains invalid anchor");
|
diag.span_label(sp, "contains invalid anchor");
|
||||||
}
|
}
|
||||||
@ -1308,7 +1526,7 @@ fn ambiguity_error(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| {
|
report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| {
|
||||||
if let Some(sp) = sp {
|
if let Some(sp) = sp {
|
||||||
diag.span_label(sp, "ambiguous link");
|
diag.span_label(sp, "ambiguous link");
|
||||||
} else {
|
} else {
|
||||||
@ -1356,7 +1574,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, &msg, item, dox, link_range, |diag, sp| {
|
report_diagnostic(cx, &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");
|
||||||
}
|
}
|
||||||
@ -1384,7 +1602,8 @@ fn handle_variant(
|
|||||||
let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
|
let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
|
||||||
parent
|
parent
|
||||||
} else {
|
} else {
|
||||||
return Err(ErrorKind::ResolutionFailure);
|
// TODO: this should just be an unwrap, there should never be `Variant`s without a parent
|
||||||
|
return Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem));
|
||||||
};
|
};
|
||||||
let parent_def = Res::Def(DefKind::Enum, parent);
|
let parent_def = Res::Def(DefKind::Enum, parent);
|
||||||
let variant = cx.tcx.expect_variant_res(res);
|
let variant = cx.tcx.expect_variant_res(res);
|
||||||
|
@ -2,13 +2,14 @@ error: unresolved link to `v2`
|
|||||||
--> $DIR/deny-intra-link-resolution-failure.rs:3:6
|
--> $DIR/deny-intra-link-resolution-failure.rs:3:6
|
||||||
|
|
|
|
||||||
LL | /// [v2]
|
LL | /// [v2]
|
||||||
| ^^ unresolved link
|
| ^^
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/deny-intra-link-resolution-failure.rs:1:9
|
--> $DIR/deny-intra-link-resolution-failure.rs:1:9
|
||||||
|
|
|
|
||||||
LL | #![deny(broken_intra_doc_links)]
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= note: no item named `v2` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
@ -2,14 +2,15 @@ error: unresolved link to `TypeAlias::hoge`
|
|||||||
--> $DIR/intra-doc-alias-ice.rs:5:30
|
--> $DIR/intra-doc-alias-ice.rs:5:30
|
||||||
|
|
|
|
||||||
LL | /// [broken cross-reference](TypeAlias::hoge)
|
LL | /// [broken cross-reference](TypeAlias::hoge)
|
||||||
| ^^^^^^^^^^^^^^^ unresolved link
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/intra-doc-alias-ice.rs:1:9
|
--> $DIR/intra-doc-alias-ice.rs:1:9
|
||||||
|
|
|
|
||||||
LL | #![deny(broken_intra_doc_links)]
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= note: this link partially resolves to the type alias `TypeAlias`,
|
||||||
|
= note: `TypeAlias` has no field, variant, or associated item named `hoge`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
59
src/test/rustdoc-ui/intra-link-errors.rs
Normal file
59
src/test/rustdoc-ui/intra-link-errors.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#![deny(broken_intra_doc_links)]
|
||||||
|
//~^ NOTE lint level is defined
|
||||||
|
|
||||||
|
//! [std::io::oops]
|
||||||
|
//! [std::io::oops::not::here]
|
||||||
|
|
||||||
|
// FIXME: this should say that it was skipped (maybe an allowed by default lint?)
|
||||||
|
/// [<invalid syntax>]
|
||||||
|
|
||||||
|
// FIXME: this could say which path was the first to not be found (in this case, `path`)
|
||||||
|
/// [path::to::nonexistent::module]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE no item named `path::to::nonexistent` is in scope
|
||||||
|
//~| HELP to escape
|
||||||
|
|
||||||
|
// TODO: why does this say `f` and not `f::A`??
|
||||||
|
/// [f::A]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE no item named `f` is in scope
|
||||||
|
//~| HELP to escape
|
||||||
|
|
||||||
|
/// [S::A]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE this link partially resolves
|
||||||
|
//~| NOTE `S` has no field
|
||||||
|
|
||||||
|
/// [S::fmt]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE this link partially resolves
|
||||||
|
//~| NOTE `S` has no field
|
||||||
|
|
||||||
|
/// [E::D]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE this link partially resolves
|
||||||
|
//~| NOTE `E` has no field
|
||||||
|
|
||||||
|
/// [u8::not_found]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| NOTE the builtin type `u8` does not have an associated item named `not_found`
|
||||||
|
|
||||||
|
/// [S!]
|
||||||
|
//~^ ERROR unresolved link
|
||||||
|
//~| HELP to link to the unit struct, use its disambiguator
|
||||||
|
//~| NOTE this link resolves to the unit struct `S`
|
||||||
|
pub fn f() {}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct S;
|
||||||
|
|
||||||
|
pub enum E { A, B, C }
|
||||||
|
|
||||||
|
/// [type@S::h]
|
||||||
|
impl S {
|
||||||
|
pub fn h() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [type@T::g]
|
||||||
|
pub trait T {
|
||||||
|
fn g() {}
|
||||||
|
}
|
68
src/test/rustdoc-ui/intra-link-errors.stderr
Normal file
68
src/test/rustdoc-ui/intra-link-errors.stderr
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
error: unresolved link to `path::to::nonexistent::module`
|
||||||
|
--> $DIR/intra-link-errors.rs:8:6
|
||||||
|
|
|
||||||
|
LL | /// [path::to::nonexistent::module]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/intra-link-errors.rs:1:9
|
||||||
|
|
|
||||||
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= note: no item named `path::to::nonexistent` is in scope
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `f::A`
|
||||||
|
--> $DIR/intra-link-errors.rs:14:6
|
||||||
|
|
|
||||||
|
LL | /// [f::A]
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: no item named `f` is in scope
|
||||||
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
|
error: unresolved link to `S::A`
|
||||||
|
--> $DIR/intra-link-errors.rs:19:6
|
||||||
|
|
|
||||||
|
LL | /// [S::A]
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: this link partially resolves to the struct `S`,
|
||||||
|
= note: `S` has no field, variant, or associated item named `A`
|
||||||
|
|
||||||
|
error: unresolved link to `S::fmt`
|
||||||
|
--> $DIR/intra-link-errors.rs:24:6
|
||||||
|
|
|
||||||
|
LL | /// [S::fmt]
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
= note: this link partially resolves to the struct `S`,
|
||||||
|
= note: `S` has no field, variant, or associated item named `fmt`
|
||||||
|
|
||||||
|
error: unresolved link to `E::D`
|
||||||
|
--> $DIR/intra-link-errors.rs:29:6
|
||||||
|
|
|
||||||
|
LL | /// [E::D]
|
||||||
|
| ^^^^
|
||||||
|
|
|
||||||
|
= note: this link partially resolves to the enum `E`,
|
||||||
|
= note: `E` has no field, variant, or associated item named `D`
|
||||||
|
|
||||||
|
error: unresolved link to `u8::not_found`
|
||||||
|
--> $DIR/intra-link-errors.rs:34:6
|
||||||
|
|
|
||||||
|
LL | /// [u8::not_found]
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the builtin type `u8` does not have an associated item named `not_found`
|
||||||
|
|
||||||
|
error: unresolved link to `S`
|
||||||
|
--> $DIR/intra-link-errors.rs:38:6
|
||||||
|
|
|
||||||
|
LL | /// [S!]
|
||||||
|
| ^^ help: to link to the unit struct, use its disambiguator: `value@S`
|
||||||
|
|
|
||||||
|
= note: this link resolves to the unit struct `S`, which is not in the value namespace
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
@ -2,13 +2,14 @@ error: unresolved link to `i`
|
|||||||
--> $DIR/intra-link-span-ice-55723.rs:9:10
|
--> $DIR/intra-link-span-ice-55723.rs:9:10
|
||||||
|
|
|
|
||||||
LL | /// (arr[i])
|
LL | /// (arr[i])
|
||||||
| ^ unresolved link
|
| ^
|
||||||
|
|
|
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/intra-link-span-ice-55723.rs:1:9
|
--> $DIR/intra-link-span-ice-55723.rs:1:9
|
||||||
|
|
|
|
||||||
LL | #![deny(broken_intra_doc_links)]
|
LL | #![deny(broken_intra_doc_links)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= note: no item named `i` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
@ -2,33 +2,37 @@ warning: unresolved link to `error`
|
|||||||
--> $DIR/intra-links-warning-crlf.rs:7:6
|
--> $DIR/intra-links-warning-crlf.rs:7:6
|
||||||
|
|
|
|
||||||
LL | /// [error]
|
LL | /// [error]
|
||||||
| ^^^^^ unresolved link
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(broken_intra_doc_links)]` on by default
|
= note: `#[warn(broken_intra_doc_links)]` on by default
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error1`
|
warning: unresolved link to `error1`
|
||||||
--> $DIR/intra-links-warning-crlf.rs:12:11
|
--> $DIR/intra-links-warning-crlf.rs:12:11
|
||||||
|
|
|
|
||||||
LL | /// docs [error1]
|
LL | /// docs [error1]
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error1` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error2`
|
warning: unresolved link to `error2`
|
||||||
--> $DIR/intra-links-warning-crlf.rs:15:11
|
--> $DIR/intra-links-warning-crlf.rs:15:11
|
||||||
|
|
|
|
||||||
LL | /// docs [error2]
|
LL | /// docs [error2]
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error2` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
--> $DIR/intra-links-warning-crlf.rs:23:20
|
--> $DIR/intra-links-warning-crlf.rs:23:20
|
||||||
|
|
|
|
||||||
LL | * It also has an [error].
|
LL | * It also has an [error].
|
||||||
| ^^^^^ unresolved link
|
| ^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: 4 warnings emitted
|
warning: 4 warnings emitted
|
||||||
|
@ -2,73 +2,82 @@ warning: unresolved link to `Foo::baz`
|
|||||||
--> $DIR/intra-links-warning.rs:3:23
|
--> $DIR/intra-links-warning.rs:3:23
|
||||||
|
|
|
|
||||||
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
||||||
| ^^^^^^^^ unresolved link
|
| ^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(broken_intra_doc_links)]` on by default
|
= note: `#[warn(broken_intra_doc_links)]` on by default
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= note: this link partially resolves to the struct `Foo`,
|
||||||
|
= note: `Foo` has no field, variant, or associated item named `baz`
|
||||||
|
|
||||||
warning: unresolved link to `Bar::foo`
|
warning: unresolved link to `Bar::foo`
|
||||||
--> $DIR/intra-links-warning.rs:3:35
|
--> $DIR/intra-links-warning.rs:3:35
|
||||||
|
|
|
|
||||||
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
LL | //! Test with [Foo::baz], [Bar::foo], ...
|
||||||
| ^^^^^^^^ unresolved link
|
| ^^^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Bar` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `Uniooon::X`
|
warning: unresolved link to `Uniooon::X`
|
||||||
--> $DIR/intra-links-warning.rs:6:13
|
--> $DIR/intra-links-warning.rs:6:13
|
||||||
|
|
|
|
||||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||||
| ^^^^^^^^^^ unresolved link
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Uniooon` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `Qux::Z`
|
warning: unresolved link to `Qux::Z`
|
||||||
--> $DIR/intra-links-warning.rs:6:30
|
--> $DIR/intra-links-warning.rs:6:30
|
||||||
|
|
|
|
||||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Qux` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `Uniooon::X`
|
warning: unresolved link to `Uniooon::X`
|
||||||
--> $DIR/intra-links-warning.rs:10:14
|
--> $DIR/intra-links-warning.rs:10:14
|
||||||
|
|
|
|
||||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||||
| ^^^^^^^^^^ unresolved link
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Uniooon` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `Qux::Z`
|
warning: unresolved link to `Qux::Z`
|
||||||
--> $DIR/intra-links-warning.rs:10:31
|
--> $DIR/intra-links-warning.rs:10:31
|
||||||
|
|
|
|
||||||
LL | //! , [Uniooon::X] and [Qux::Z].
|
LL | //! , [Uniooon::X] and [Qux::Z].
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Qux` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `Qux:Y`
|
warning: unresolved link to `Qux:Y`
|
||||||
--> $DIR/intra-links-warning.rs:14:13
|
--> $DIR/intra-links-warning.rs:14:13
|
||||||
|
|
|
|
||||||
LL | /// [Qux:Y]
|
LL | /// [Qux:Y]
|
||||||
| ^^^^^ unresolved link
|
| ^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `Qux:Y` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
--> $DIR/intra-links-warning.rs:58:30
|
--> $DIR/intra-links-warning.rs:58:30
|
||||||
|
|
|
|
||||||
LL | * time to introduce a link [error]*/
|
LL | * time to introduce a link [error]*/
|
||||||
| ^^^^^ unresolved link
|
| ^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
--> $DIR/intra-links-warning.rs:64:30
|
--> $DIR/intra-links-warning.rs:64:30
|
||||||
|
|
|
|
||||||
LL | * time to introduce a link [error]
|
LL | * time to introduce a link [error]
|
||||||
| ^^^^^ unresolved link
|
| ^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
@ -81,6 +90,7 @@ LL | #[doc = "single line [error]"]
|
|||||||
|
|
||||||
single line [error]
|
single line [error]
|
||||||
^^^^^
|
^^^^^
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
@ -93,6 +103,7 @@ LL | #[doc = "single line with \"escaping\" [error]"]
|
|||||||
|
|
||||||
single line with "escaping" [error]
|
single line with "escaping" [error]
|
||||||
^^^^^
|
^^^^^
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error`
|
warning: unresolved link to `error`
|
||||||
@ -107,46 +118,52 @@ LL | | /// [error]
|
|||||||
|
|
||||||
[error]
|
[error]
|
||||||
^^^^^
|
^^^^^
|
||||||
|
= note: no item named `error` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error1`
|
warning: unresolved link to `error1`
|
||||||
--> $DIR/intra-links-warning.rs:80:11
|
--> $DIR/intra-links-warning.rs:80:11
|
||||||
|
|
|
|
||||||
LL | /// docs [error1]
|
LL | /// docs [error1]
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error1` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `error2`
|
warning: unresolved link to `error2`
|
||||||
--> $DIR/intra-links-warning.rs:82:11
|
--> $DIR/intra-links-warning.rs:82:11
|
||||||
|
|
|
|
||||||
LL | /// docs [error2]
|
LL | /// docs [error2]
|
||||||
| ^^^^^^ unresolved link
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `error2` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `BarA`
|
warning: unresolved link to `BarA`
|
||||||
--> $DIR/intra-links-warning.rs:21:10
|
--> $DIR/intra-links-warning.rs:21:10
|
||||||
|
|
|
|
||||||
LL | /// bar [BarA] bar
|
LL | /// bar [BarA] bar
|
||||||
| ^^^^ unresolved link
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `BarA` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `BarB`
|
warning: unresolved link to `BarB`
|
||||||
--> $DIR/intra-links-warning.rs:27:9
|
--> $DIR/intra-links-warning.rs:27:9
|
||||||
|
|
|
|
||||||
LL | * bar [BarB] bar
|
LL | * bar [BarB] bar
|
||||||
| ^^^^ unresolved link
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `BarB` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `BarC`
|
warning: unresolved link to `BarC`
|
||||||
--> $DIR/intra-links-warning.rs:34:6
|
--> $DIR/intra-links-warning.rs:34:6
|
||||||
|
|
|
|
||||||
LL | bar [BarC] bar
|
LL | bar [BarC] bar
|
||||||
| ^^^^ unresolved link
|
| ^^^^
|
||||||
|
|
|
|
||||||
|
= note: no item named `BarC` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `BarD`
|
warning: unresolved link to `BarD`
|
||||||
@ -159,6 +176,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
|
|||||||
|
|
||||||
bar [BarD] bar
|
bar [BarD] bar
|
||||||
^^^^
|
^^^^
|
||||||
|
= note: no item named `BarD` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
|
|
||||||
warning: unresolved link to `BarF`
|
warning: unresolved link to `BarF`
|
||||||
@ -174,6 +192,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz");
|
|||||||
|
|
||||||
bar [BarF] bar
|
bar [BarF] bar
|
||||||
^^^^
|
^^^^
|
||||||
|
= note: no item named `BarF` is in scope
|
||||||
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
|
||||||
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user