Auto merge of #76955 - jyn514:refactor-diagnostics, r=euclio

Refactor and fix intra-doc link diagnostics, and fix links to primitives

Closes https://github.com/rust-lang/rust/issues/76925, closes https://github.com/rust-lang/rust/issues/76693, closes https://github.com/rust-lang/rust/issues/76692.

Originally I only meant to fix #76925. But the hack with `has_primitive` was so bad it was easier to fix the primitive issues than to try and work around it.

Note that this still has one bug: `std::primitive::i32::MAX` does not resolve. However, this fixes the ICE so I'm fine with fixing the link in a later PR.

This is part of a series of refactors to make #76467 possible.

This is best reviewed commit-by-commit; it has detailed commit messages.

r? `@euclio`
This commit is contained in:
bors 2020-09-27 08:12:29 +00:00
commit 71bdb84817
11 changed files with 329 additions and 279 deletions

View File

@ -2004,6 +2004,30 @@ pub enum PrimTy {
Char,
}
impl PrimTy {
pub fn name_str(self) -> &'static str {
match self {
PrimTy::Int(i) => i.name_str(),
PrimTy::Uint(u) => u.name_str(),
PrimTy::Float(f) => f.name_str(),
PrimTy::Str => "str",
PrimTy::Bool => "bool",
PrimTy::Char => "char",
}
}
pub fn name(self) -> Symbol {
match self {
PrimTy::Int(i) => i.name(),
PrimTy::Uint(u) => u.name(),
PrimTy::Float(f) => f.name(),
PrimTy::Str => sym::str,
PrimTy::Bool => sym::bool,
PrimTy::Char => sym::char,
}
}
}
#[derive(Debug, HashStable_Generic)]
pub struct BareFnTy<'hir> {
pub unsafety: Unsafety,

View File

@ -57,45 +57,16 @@ enum ResolutionFailure<'a> {
/// This resolved, but with the wrong namespace.
/// `Namespace` is the expected namespace (as opposed to the actual).
WrongNamespace(Res, Namespace),
/// This has a partial resolution, but is not in the TypeNS and so cannot
/// have associated items or fields.
CannotHaveAssociatedItems(Res, Namespace),
/// `name` is the base name of the path (not necessarily the whole link)
NotInScope { module_id: DefId, name: Cow<'a, str> },
/// 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: &'a str, assoc_item: Symbol },
/// `[S::not_found]`
/// the `String` is the associated item that wasn't found
NoAssocItem(Res, Symbol),
/// The link failed to resolve. `resolution_failure` should look to see if there's
/// a more helpful error that can be given.
NotResolved { module_id: DefId, partial_res: Option<Res>, unresolved: Cow<'a, str> },
/// should not ever happen
NoParentItem,
/// 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, Symbol),
/// used to communicate that this should be ignored, but shouldn't be reported to the user
Dummy,
}
impl ResolutionFailure<'a> {
// A partial or full resolution
fn res(&self) -> Option<Res> {
use ResolutionFailure::*;
match self {
NoPrimitiveAssocItem { res, .. }
| NoAssocItem(res, _)
| NoPrimitiveImpl(res, _)
| NotAVariant(res, _)
| WrongNamespace(res, _)
| CannotHaveAssociatedItems(res, _) => Some(*res),
NotInScope { .. } | NoParentItem | Dummy => None,
}
}
// This resolved fully (not just partially) but is erroneous for some other reason
fn full_res(&self) -> Option<Res> {
match self {
@ -130,22 +101,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
path_str: &'path str,
current_item: &Option<String>,
module_id: DefId,
extra_fragment: &Option<String>,
) -> Result<(Res, Option<String>), ErrorKind<'path>> {
let cx = self.cx;
let no_res = || ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: path_str.into(),
};
debug!("looking for enum variant {}", path_str);
let mut split = path_str.rsplitn(3, "::");
let variant_field_name = split
let (variant_field_str, variant_field_name) = split
.next()
.map(|f| Symbol::intern(f))
.map(|f| (f, Symbol::intern(f)))
.expect("fold_item should ensure link is non-empty");
let variant_name =
let (variant_str, variant_name) =
// we're not sure this is a variant at all, so use the full string
split.next().map(|f| Symbol::intern(f)).ok_or_else(|| ResolutionFailure::NotInScope {
module_id,
name: path_str.into(),
})?;
// If there's no second component, the link looks like `[path]`.
// So there's no partial res and we should say the whole link failed to resolve.
split.next().map(|f| (f, Symbol::intern(f))).ok_or_else(no_res)?;
let path = split
.next()
.map(|f| {
@ -156,10 +130,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}
f.to_owned()
})
.ok_or_else(|| ResolutionFailure::NotInScope {
module_id,
name: variant_name.to_string().into(),
})?;
// If there's no third component, we saw `[a::b]` before and it failed to resolve.
// So there's no partial res.
.ok_or_else(no_res)?;
let ty_res = cx
.enter_resolver(|resolver| {
resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
@ -167,7 +140,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
.map(|(_, res)| res)
.unwrap_or(Res::Err);
if let Res::Err = ty_res {
return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into());
return Err(no_res().into());
}
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
match ty_res {
@ -190,38 +163,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
ty_res,
Some(format!(
"variant.{}.field.{}",
variant_name, variant_field_name
variant_str, variant_field_name
)),
))
} else {
Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into())
Err(ResolutionFailure::NotResolved {
module_id,
partial_res: Some(Res::Def(DefKind::Enum, def.did)),
unresolved: variant_field_str.into(),
}
.into())
}
}
_ => unreachable!(),
}
}
// `variant_field` looks at 3 different path segments in a row.
// But `NoAssocItem` assumes there are only 2. Check to see if there's
// an intermediate segment that resolves.
_ => {
let intermediate_path = format!("{}::{}", path, variant_name);
// NOTE: we have to be careful here, because we're already in `resolve`.
// We know this doesn't recurse forever because we use a shorter path each time.
// NOTE: this uses `TypeNS` because nothing else has a valid path segment after
let kind = if let Some(intermediate) = self.check_full_res(
TypeNS,
&intermediate_path,
module_id,
current_item,
extra_fragment,
) {
ResolutionFailure::NoAssocItem(intermediate, variant_field_name)
} else {
// Even with the shorter path, it didn't resolve, so say that.
ResolutionFailure::NoAssocItem(ty_res, variant_name)
};
Err(kind.into())
_ => Err(ResolutionFailure::NotResolved {
module_id,
partial_res: Some(ty_res),
unresolved: variant_str.into(),
}
.into()),
}
}
@ -242,11 +204,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
false,
) {
if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
return Some(Ok(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)) {
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
return Ok(res.map_id(|_| panic!("unexpected id")));
}
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
if let Ok((_, res)) =
@ -255,28 +217,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// don't resolve builtins like `#[derive]`
if let Res::Def(..) = res {
let res = res.map_id(|_| panic!("unexpected node_id"));
return Some(Ok(res));
return Ok(res);
}
}
None
})
// This weird control flow is so we don't borrow the resolver more than once at a time
.unwrap_or_else(|| {
let mut split = path_str.rsplitn(2, "::");
if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
if let Some(res) = self.check_full_res(TypeNS, parent, module_id, &None, &None) {
return Err(if matches!(res, Res::PrimTy(_)) {
ResolutionFailure::NoPrimitiveAssocItem {
res,
prim_name: parent,
assoc_item: Symbol::intern(base),
}
} else {
ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
});
}
}
Err(ResolutionFailure::NotInScope { module_id, name: path_str.into() })
Err(ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: path_str.into(),
})
})
}
@ -312,13 +260,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
return handle_variant(cx, res, extra_fragment);
}
// Not a trait item; just return what we found.
Res::PrimTy(..) => {
Res::PrimTy(ty) => {
if extra_fragment.is_some() {
return Err(ErrorKind::AnchorFailure(
AnchorFailure::RustdocAnchorConflict(res),
));
}
return Ok((res, Some(path_str.to_owned())));
return Ok((res, Some(ty.name_str().to_owned())));
}
Res::Def(DefKind::Mod, _) => {
return Ok((res, extra_fragment.clone()));
@ -331,6 +279,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
if value != (ns == ValueNS) {
return Err(ResolutionFailure::WrongNamespace(res, ns).into());
}
// FIXME: why is this necessary?
} else if let Some((path, prim)) = is_primitive(path_str, ns) {
if extra_fragment.is_some() {
return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim)));
@ -341,7 +290,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// Try looking for methods and associated items.
let mut split = path_str.rsplitn(2, "::");
// this can be an `unwrap()` because we ensure the link is never empty
let item_name = Symbol::intern(split.next().unwrap());
let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
let path_root = split
.next()
.map(|f| {
@ -356,12 +305,20 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
.ok_or_else(|| {
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() }
ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: item_str.into(),
}
})?;
if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
let impls = primitive_impl(cx, &path)
.ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?;
let impls =
primitive_impl(cx, &path).ok_or_else(|| ResolutionFailure::NotResolved {
module_id,
partial_res: Some(prim),
unresolved: item_str.into(),
})?;
for &impl_ in impls {
let link = cx
.tcx
@ -377,7 +334,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
ty::AssocKind::Const => "associatedconstant",
ty::AssocKind::Type => "associatedtype",
})
.map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))));
.map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_str))));
if let Some(link) = link {
return Ok(link);
}
@ -388,10 +345,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
item_name,
ns.descr()
);
return Err(ResolutionFailure::NoPrimitiveAssocItem {
res: prim,
prim_name: path,
assoc_item: item_name,
return Err(ResolutionFailure::NotResolved {
module_id,
partial_res: Some(prim),
unresolved: item_str.into(),
}
.into());
}
@ -405,25 +362,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
let ty_res = match ty_res {
Err(()) | Ok(Res::Err) => {
return if ns == Namespace::ValueNS {
self.variant_field(path_str, current_item, module_id, extra_fragment)
self.variant_field(path_str, current_item, module_id)
} else {
// See if it only broke because of the namespace.
let kind = cx.enter_resolver(|resolver| {
// NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it)
for &ns in &[MacroNS, ValueNS] {
match resolver
.resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id)
{
Ok((_, Res::Err)) | Err(()) => {}
Ok((_, res)) => {
let res = res.map_id(|_| panic!("unexpected node_id"));
return ResolutionFailure::CannotHaveAssociatedItems(res, ns);
}
}
}
ResolutionFailure::NotInScope { module_id, name: path_root.into() }
});
Err(kind.into())
Err(ResolutionFailure::NotResolved {
module_id,
partial_res: None,
unresolved: path_root.into(),
}
.into())
};
}
Ok(res) => res,
@ -473,7 +419,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
// but the disambiguator logic expects the associated item.
// Store the kind in a side channel so that only the disambiguator logic looks at it.
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
Ok((ty_res, Some(format!("{}.{}", out, item_str))))
})
} else if ns == Namespace::ValueNS {
debug!("looking for variants or fields named {} for {:?}", item_name, did);
@ -516,7 +462,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
}
} else {
// We already know this isn't in ValueNS, so no need to check variant_field
return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into());
return Err(ResolutionFailure::NotResolved {
module_id,
partial_res: Some(ty_res),
unresolved: item_str.into(),
}
.into());
}
}
Res::Def(DefKind::Trait, did) => cx
@ -540,16 +491,21 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
} else {
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
Ok((res, Some(format!("{}.{}", kind, item_name))))
Ok((res, Some(format!("{}.{}", kind, item_str))))
}
}),
_ => None,
};
res.unwrap_or_else(|| {
if ns == Namespace::ValueNS {
self.variant_field(path_str, current_item, module_id, extra_fragment)
self.variant_field(path_str, current_item, module_id)
} else {
Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into())
Err(ResolutionFailure::NotResolved {
module_id,
partial_res: Some(ty_res),
unresolved: item_str.into(),
}
.into())
}
})
}
@ -1044,12 +1000,12 @@ impl LinkCollector<'_, '_> {
suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
});
};
if let Res::PrimTy(_) = res {
if let Res::PrimTy(..) = res {
match disambiguator {
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
item.attrs.links.push(ItemLink {
link: ori_link,
link_text: path_str.to_owned(),
link_text,
did: None,
fragment,
});
@ -1127,6 +1083,8 @@ impl LinkCollector<'_, '_> {
// We only looked in one namespace. Try to give a better error if possible.
if kind.full_res().is_none() {
let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
// FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
// See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
for &new_ns in &[other_ns, MacroNS] {
if let Some(res) = self.check_full_res(
new_ns,
@ -1529,7 +1487,6 @@ fn resolution_failure(
dox,
&link_range,
|diag, sp| {
let in_scope = kinds.iter().any(|kind| kind.res().is_some());
let item = |res: Res| {
format!(
"the {} `{}`",
@ -1550,53 +1507,142 @@ fn resolution_failure(
// ignore duplicates
let mut variants_seen = SmallVec::<[_; 3]>::new();
for mut failure in kinds {
// Check if _any_ parent of the path gets resolved.
// If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
if let ResolutionFailure::NotInScope { module_id, name } = &mut failure {
let mut current = name.as_ref();
loop {
current = match current.rsplitn(2, "::").nth(1) {
Some(p) => p,
None => {
*name = current.to_owned().into();
break;
}
};
if let Some(res) =
collector.check_full_res(TypeNS, &current, *module_id, &None, &None)
{
failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current));
break;
}
}
}
let variant = std::mem::discriminant(&failure);
if variants_seen.contains(&variant) {
continue;
}
variants_seen.push(variant);
let note = match failure {
ResolutionFailure::NotInScope { module_id, name, .. } => {
if in_scope {
continue;
if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
&mut failure
{
use DefKind::*;
let module_id = *module_id;
// FIXME(jynelson): this might conflict with my `Self` fix in #76467
// FIXME: maybe use itertools `collect_tuple` instead?
fn split(path: &str) -> Option<(&str, &str)> {
let mut splitter = path.rsplitn(2, "::");
splitter.next().and_then(|right| splitter.next().map(|left| (left, right)))
}
// Check if _any_ parent of the path gets resolved.
// If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
let mut name = path_str;
'outer: loop {
let (start, end) = if let Some(x) = split(name) {
x
} else {
// avoid bug that marked [Quux::Z] as missing Z, not Quux
if partial_res.is_none() {
*unresolved = name.into();
}
break;
};
name = start;
for &ns in &[TypeNS, ValueNS, MacroNS] {
if let Some(res) =
collector.check_full_res(ns, &start, module_id, &None, &None)
{
debug!("found partial_res={:?}", res);
*partial_res = Some(res);
*unresolved = end.into();
break 'outer;
}
}
// NOTE: uses an explicit `continue` so the `note:` will come before the `help:`
let module_name = collector.cx.tcx.item_name(module_id);
let note = format!("no item named `{}` in `{}`", name, module_name);
*unresolved = end.into();
}
let last_found_module = match *partial_res {
Some(Res::Def(DefKind::Mod, id)) => Some(id),
None => Some(module_id),
_ => None,
};
// See if this was a module: `[path]` or `[std::io::nope]`
if let Some(module) = last_found_module {
let module_name = collector.cx.tcx.item_name(module);
let note = format!(
"the module `{}` contains no item named `{}`",
module_name, unresolved
);
if let Some(span) = sp {
diag.span_label(span, &note);
} else {
diag.note(&note);
}
// If the link has `::` in the path, assume it's meant to be an intra-doc link
// If the link has `::` in it, assume it was meant to be an intra-doc link.
// Otherwise, the `[]` might be unrelated.
// FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
if !path_str.contains("::") {
// Otherwise, the `[]` might be unrelated.
// FIXME(https://github.com/raphlinus/pulldown-cmark/issues/373):
// don't show this for autolinks (`<>`), `()` style links, or reference links
diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
}
continue;
}
// Otherwise, it must be an associated item or variant
let res = partial_res.expect("None case was handled by `last_found_module`");
let diagnostic_name;
let (kind, name) = match res {
Res::Def(kind, def_id) => {
diagnostic_name = collector.cx.tcx.item_name(def_id).as_str();
(Some(kind), &*diagnostic_name)
}
Res::PrimTy(ty) => (None, ty.name_str()),
_ => unreachable!("only ADTs and primitives are in scope at module level"),
};
let path_description = if let Some(kind) = kind {
match kind {
Mod | ForeignMod => "inner item",
Struct => "field or associated item",
Enum | Union => "variant or associated item",
Variant
| Field
| Closure
| Generator
| AssocTy
| AssocConst
| AssocFn
| Fn
| Macro(_)
| Const
| ConstParam
| ExternCrate
| Use
| LifetimeParam
| Ctor(_, _)
| AnonConst => {
let note = assoc_item_not_allowed(res);
if let Some(span) = sp {
diag.span_label(span, &note);
} else {
diag.note(&note);
}
return;
}
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
| Static => "associated item",
Impl | GlobalAsm => unreachable!("not a path"),
}
} else {
"associated item"
};
let note = format!(
"the {} `{}` has no {} named `{}`",
res.descr(),
name,
disambiguator.map_or(path_description, |d| d.descr()),
unresolved,
);
if let Some(span) = sp {
diag.span_label(span, &note);
} else {
diag.note(&note);
}
continue;
}
let note = match failure {
ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
ResolutionFailure::Dummy => continue,
ResolutionFailure::WrongNamespace(res, expected_ns) => {
if let Res::Def(kind, _) = res {
@ -1621,79 +1667,6 @@ fn resolution_failure(
diag.level = rustc_errors::Level::Bug;
"all intra doc links should have a parent item".to_owned()
}
ResolutionFailure::NoPrimitiveImpl(res, _) => format!(
"this link partially resolves to {}, which does not have any associated items",
item(res),
),
ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => {
format!(
"the builtin type `{}` does not have an associated item named `{}`",
prim_name, assoc_item
)
}
ResolutionFailure::NoAssocItem(res, assoc_item) => {
use DefKind::*;
let (kind, def_id) = match res {
Res::Def(kind, def_id) => (kind, def_id),
x => unreachable!(
"primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
x,
),
};
let name = collector.cx.tcx.item_name(def_id);
let path_description = if let Some(disambiguator) = disambiguator {
disambiguator.descr()
} else {
match kind {
Mod | ForeignMod => "inner item",
Struct => "field or associated item",
Enum | Union => "variant or associated item",
Variant
| Field
| Closure
| Generator
| AssocTy
| AssocConst
| AssocFn
| Fn
| Macro(_)
| Const
| ConstParam
| ExternCrate
| Use
| LifetimeParam
| Ctor(_, _)
| AnonConst => {
let note = assoc_item_not_allowed(res);
if let Some(span) = sp {
diag.span_label(span, &note);
} else {
diag.note(&note);
}
return;
}
Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
| Static => "associated item",
Impl | GlobalAsm => unreachable!("not a path"),
}
};
format!(
"the {} `{}` has no {} named `{}`",
res.descr(),
name,
path_description,
assoc_item
)
}
ResolutionFailure::CannotHaveAssociatedItems(res, _) => {
assoc_item_not_allowed(res)
}
ResolutionFailure::NotAVariant(res, variant) => format!(
"this link partially resolves to {}, but there is no variant named {}",
item(res),
variant
),
};
if let Some(span) = sp {
diag.span_label(span, &note);

View File

@ -2,7 +2,7 @@ error: unresolved link to `v2`
--> $DIR/deny-intra-link-resolution-failure.rs:3:6
|
LL | /// [v2]
| ^^ no item named `v2` in `deny_intra_link_resolution_failure`
| ^^ the module `deny_intra_link_resolution_failure` contains no item named `v2`
|
note: the lint level is defined here
--> $DIR/deny-intra-link-resolution-failure.rs:1:9

View File

@ -6,19 +6,23 @@
/// [path::to::nonexistent::module]
//~^ ERROR unresolved link
//~| NOTE no item named `path` in `intra_link_errors`
//~| NOTE `intra_link_errors` contains no item named `path`
/// [path::to::nonexistent::macro!]
//~^ ERROR unresolved link
//~| NOTE no item named `path` in `intra_link_errors`
//~| NOTE `intra_link_errors` contains no item named `path`
/// [type@path::to::nonexistent::type]
//~^ ERROR unresolved link
//~| NOTE no item named `path` in `intra_link_errors`
//~| NOTE `intra_link_errors` contains no item named `path`
/// [std::io::not::here]
//~^ ERROR unresolved link
//~| NOTE the module `io` has no inner item
//~| NOTE `io` contains no item named `not`
/// [type@std::io::not::here]
//~^ ERROR unresolved link
//~| NOTE `io` contains no item named `not`
/// [std::io::Error::x]
//~^ ERROR unresolved link
@ -32,6 +36,10 @@
//~^ ERROR unresolved link
//~| NOTE `f` is a function, not a module
/// [f::A!]
//~^ ERROR unresolved link
//~| NOTE `f` is a function, not a module
/// [S::A]
//~^ ERROR unresolved link
//~| NOTE struct `S` has no field or associated item
@ -46,7 +54,16 @@
/// [u8::not_found]
//~^ ERROR unresolved link
//~| NOTE the builtin type `u8` does not have an associated item named `not_found`
//~| NOTE the builtin type `u8` has no associated item named `not_found`
/// [std::primitive::u8::not_found]
//~^ ERROR unresolved link
//~| NOTE the builtin type `u8` has no associated item named `not_found`
/// [type@Vec::into_iter]
//~^ ERROR unresolved link
//~| HELP to link to the associated function, add parentheses
//~| NOTE this link resolves to the associated function `into_iter`
/// [S!]
//~^ ERROR unresolved link

View File

@ -2,7 +2,7 @@ error: unresolved link to `path::to::nonexistent::module`
--> $DIR/intra-link-errors.rs:7:6
|
LL | /// [path::to::nonexistent::module]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
|
note: the lint level is defined here
--> $DIR/intra-link-errors.rs:1:9
@ -14,64 +14,91 @@ error: unresolved link to `path::to::nonexistent::macro`
--> $DIR/intra-link-errors.rs:11:6
|
LL | /// [path::to::nonexistent::macro!]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
error: unresolved link to `path::to::nonexistent::type`
--> $DIR/intra-link-errors.rs:15:6
|
LL | /// [type@path::to::nonexistent::type]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path`
error: unresolved link to `std::io::not::here`
--> $DIR/intra-link-errors.rs:19:6
|
LL | /// [std::io::not::here]
| ^^^^^^^^^^^^^^^^^^ the module `io` has no inner item named `not`
| ^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not`
error: unresolved link to `std::io::not::here`
--> $DIR/intra-link-errors.rs:23:6
|
LL | /// [type@std::io::not::here]
| ^^^^^^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not`
error: unresolved link to `std::io::Error::x`
--> $DIR/intra-link-errors.rs:23:6
--> $DIR/intra-link-errors.rs:27:6
|
LL | /// [std::io::Error::x]
| ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x`
error: unresolved link to `std::io::ErrorKind::x`
--> $DIR/intra-link-errors.rs:27:6
--> $DIR/intra-link-errors.rs:31:6
|
LL | /// [std::io::ErrorKind::x]
| ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x`
error: unresolved link to `f::A`
--> $DIR/intra-link-errors.rs:31:6
--> $DIR/intra-link-errors.rs:35:6
|
LL | /// [f::A]
| ^^^^ `f` is a function, not a module or type, and cannot have associated items
error: unresolved link to `f::A`
--> $DIR/intra-link-errors.rs:39:6
|
LL | /// [f::A!]
| ^^^^^ `f` is a function, not a module or type, and cannot have associated items
error: unresolved link to `S::A`
--> $DIR/intra-link-errors.rs:35:6
--> $DIR/intra-link-errors.rs:43:6
|
LL | /// [S::A]
| ^^^^ the struct `S` has no field or associated item named `A`
error: unresolved link to `S::fmt`
--> $DIR/intra-link-errors.rs:39:6
--> $DIR/intra-link-errors.rs:47:6
|
LL | /// [S::fmt]
| ^^^^^^ the struct `S` has no field or associated item named `fmt`
error: unresolved link to `E::D`
--> $DIR/intra-link-errors.rs:43:6
--> $DIR/intra-link-errors.rs:51:6
|
LL | /// [E::D]
| ^^^^ the enum `E` has no variant or associated item named `D`
error: unresolved link to `u8::not_found`
--> $DIR/intra-link-errors.rs:47:6
--> $DIR/intra-link-errors.rs:55:6
|
LL | /// [u8::not_found]
| ^^^^^^^^^^^^^ the builtin type `u8` does not have an associated item named `not_found`
| ^^^^^^^^^^^^^ the builtin type `u8` has no associated item named `not_found`
error: unresolved link to `std::primitive::u8::not_found`
--> $DIR/intra-link-errors.rs:59:6
|
LL | /// [std::primitive::u8::not_found]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the builtin type `u8` has no associated item named `not_found`
error: unresolved link to `Vec::into_iter`
--> $DIR/intra-link-errors.rs:63:6
|
LL | /// [type@Vec::into_iter]
| ^^^^^^^^^^^^^^^^^^^
| |
| this link resolves to the associated function `into_iter`, which is not in the type namespace
| help: to link to the associated function, add parentheses: `Vec::into_iter()`
error: unresolved link to `S`
--> $DIR/intra-link-errors.rs:51:6
--> $DIR/intra-link-errors.rs:68:6
|
LL | /// [S!]
| ^^
@ -80,7 +107,7 @@ LL | /// [S!]
| help: to link to the struct, prefix with `struct@`: `struct@S`
error: unresolved link to `T::g`
--> $DIR/intra-link-errors.rs:69:6
--> $DIR/intra-link-errors.rs:86:6
|
LL | /// [type@T::g]
| ^^^^^^^^^
@ -89,13 +116,13 @@ LL | /// [type@T::g]
| help: to link to the associated function, add parentheses: `T::g()`
error: unresolved link to `T::h`
--> $DIR/intra-link-errors.rs:74:6
--> $DIR/intra-link-errors.rs:91:6
|
LL | /// [T::h!]
| ^^^^^ the trait `T` has no macro named `h`
error: unresolved link to `S::h`
--> $DIR/intra-link-errors.rs:61:6
--> $DIR/intra-link-errors.rs:78:6
|
LL | /// [type@S::h]
| ^^^^^^^^^
@ -104,7 +131,7 @@ LL | /// [type@S::h]
| help: to link to the associated function, add parentheses: `S::h()`
error: unresolved link to `m`
--> $DIR/intra-link-errors.rs:81:6
--> $DIR/intra-link-errors.rs:98:6
|
LL | /// [m()]
| ^^^
@ -112,5 +139,5 @@ LL | /// [m()]
| this link resolves to the macro `m`, which is not in the value namespace
| help: to link to the macro, add an exclamation mark: `m!`
error: aborting due to 16 previous errors
error: aborting due to 20 previous errors

View File

@ -2,7 +2,7 @@ error: unresolved link to `i`
--> $DIR/intra-link-span-ice-55723.rs:9:10
|
LL | /// arr[i]
| ^ no item named `i` in `intra_link_span_ice_55723`
| ^ the module `intra_link_span_ice_55723` contains no item named `i`
|
note: the lint level is defined here
--> $DIR/intra-link-span-ice-55723.rs:1:9

View File

@ -2,7 +2,7 @@ warning: unresolved link to `error`
--> $DIR/intra-links-warning-crlf.rs:7:6
|
LL | /// [error]
| ^^^^^ no item named `error` in `intra_links_warning_crlf`
| ^^^^^ the module `intra_links_warning_crlf` contains no item named `error`
|
= note: `#[warn(broken_intra_doc_links)]` on by default
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -11,7 +11,7 @@ warning: unresolved link to `error1`
--> $DIR/intra-links-warning-crlf.rs:12:11
|
LL | /// docs [error1]
| ^^^^^^ no item named `error1` in `intra_links_warning_crlf`
| ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error1`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -19,7 +19,7 @@ warning: unresolved link to `error2`
--> $DIR/intra-links-warning-crlf.rs:15:11
|
LL | /// docs [error2]
| ^^^^^^ no item named `error2` in `intra_links_warning_crlf`
| ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error2`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -27,7 +27,7 @@ warning: unresolved link to `error`
--> $DIR/intra-links-warning-crlf.rs:23:20
|
LL | * It also has an [error].
| ^^^^^ no item named `error` in `intra_links_warning_crlf`
| ^^^^^ the module `intra_links_warning_crlf` contains no item named `error`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`

View File

@ -10,37 +10,37 @@ warning: unresolved link to `Bar::foo`
--> $DIR/intra-links-warning.rs:3:35
|
LL | //! Test with [Foo::baz], [Bar::foo], ...
| ^^^^^^^^ no item named `Bar` in `intra_links_warning`
| ^^^^^^^^ the module `intra_links_warning` contains no item named `Bar`
warning: unresolved link to `Uniooon::X`
--> $DIR/intra-links-warning.rs:6:13
|
LL | //! , [Uniooon::X] and [Qux::Z].
| ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning`
| ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon`
warning: unresolved link to `Qux::Z`
--> $DIR/intra-links-warning.rs:6:30
|
LL | //! , [Uniooon::X] and [Qux::Z].
| ^^^^^^ no item named `Qux` in `intra_links_warning`
| ^^^^^^ the module `intra_links_warning` contains no item named `Qux`
warning: unresolved link to `Uniooon::X`
--> $DIR/intra-links-warning.rs:10:14
|
LL | //! , [Uniooon::X] and [Qux::Z].
| ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning`
| ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon`
warning: unresolved link to `Qux::Z`
--> $DIR/intra-links-warning.rs:10:31
|
LL | //! , [Uniooon::X] and [Qux::Z].
| ^^^^^^ no item named `Qux` in `intra_links_warning`
| ^^^^^^ the module `intra_links_warning` contains no item named `Qux`
warning: unresolved link to `Qux:Y`
--> $DIR/intra-links-warning.rs:14:13
|
LL | /// [Qux:Y]
| ^^^^^ no item named `Qux:Y` in `intra_links_warning`
| ^^^^^ the module `intra_links_warning` contains no item named `Qux:Y`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -48,7 +48,7 @@ warning: unresolved link to `error`
--> $DIR/intra-links-warning.rs:58:30
|
LL | * time to introduce a link [error]*/
| ^^^^^ no item named `error` in `intra_links_warning`
| ^^^^^ the module `intra_links_warning` contains no item named `error`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -56,7 +56,7 @@ warning: unresolved link to `error`
--> $DIR/intra-links-warning.rs:64:30
|
LL | * time to introduce a link [error]
| ^^^^^ no item named `error` in `intra_links_warning`
| ^^^^^ the module `intra_links_warning` contains no item named `error`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -70,7 +70,7 @@ LL | #[doc = "single line [error]"]
single line [error]
^^^^^
= note: no item named `error` in `intra_links_warning`
= note: the module `intra_links_warning` contains no item named `error`
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
warning: unresolved link to `error`
@ -83,7 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"]
single line with "escaping" [error]
^^^^^
= note: no item named `error` in `intra_links_warning`
= note: the module `intra_links_warning` contains no item named `error`
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
warning: unresolved link to `error`
@ -98,14 +98,14 @@ LL | | /// [error]
[error]
^^^^^
= note: no item named `error` in `intra_links_warning`
= note: the module `intra_links_warning` contains no item named `error`
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
warning: unresolved link to `error1`
--> $DIR/intra-links-warning.rs:80:11
|
LL | /// docs [error1]
| ^^^^^^ no item named `error1` in `intra_links_warning`
| ^^^^^^ the module `intra_links_warning` contains no item named `error1`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -113,7 +113,7 @@ warning: unresolved link to `error2`
--> $DIR/intra-links-warning.rs:82:11
|
LL | /// docs [error2]
| ^^^^^^ no item named `error2` in `intra_links_warning`
| ^^^^^^ the module `intra_links_warning` contains no item named `error2`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -121,7 +121,7 @@ warning: unresolved link to `BarA`
--> $DIR/intra-links-warning.rs:21:10
|
LL | /// bar [BarA] bar
| ^^^^ no item named `BarA` in `intra_links_warning`
| ^^^^ the module `intra_links_warning` contains no item named `BarA`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -129,7 +129,7 @@ warning: unresolved link to `BarB`
--> $DIR/intra-links-warning.rs:27:9
|
LL | * bar [BarB] bar
| ^^^^ no item named `BarB` in `intra_links_warning`
| ^^^^ the module `intra_links_warning` contains no item named `BarB`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -137,7 +137,7 @@ warning: unresolved link to `BarC`
--> $DIR/intra-links-warning.rs:34:6
|
LL | bar [BarC] bar
| ^^^^ no item named `BarC` in `intra_links_warning`
| ^^^^ the module `intra_links_warning` contains no item named `BarC`
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
@ -151,7 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"]
bar [BarD] bar
^^^^
= note: no item named `BarD` in `intra_links_warning`
= note: the module `intra_links_warning` contains no item named `BarD`
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
warning: unresolved link to `BarF`
@ -167,7 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz");
bar [BarF] bar
^^^^
= note: no item named `BarF` in `intra_links_warning`
= note: the module `intra_links_warning` contains no item named `BarF`
= 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)

View File

@ -32,7 +32,7 @@ error: unresolved link to `error`
--> $DIR/lint-group.rs:9:29
|
LL | /// what up, let's make an [error]
| ^^^^^ no item named `error` in `lint_group`
| ^^^^^ the module `lint_group` contains no item named `error`
|
note: the lint level is defined here
--> $DIR/lint-group.rs:7:9

View File

@ -3,8 +3,10 @@
/// [`std::collections::BTreeMap::into_iter`]
/// [`String::from`] is ambiguous as to which `From` impl
/// [Vec::into_iter()] uses a disambiguator
// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/collections/btree/map/struct.BTreeMap.html#method.into_iter"]' 'std::collections::BTreeMap::into_iter'
// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from"]' 'String::from'
// @has 'intra_link_associated_items/fn.foo.html' '//a[@href="https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.into_iter"]' 'Vec::into_iter'
pub fn foo() {}
/// Link to [MyStruct], [link from struct][MyStruct::method], [MyStruct::clone], [MyStruct::Input]

View File

@ -4,6 +4,13 @@
// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.u32.html"]' 'u32'
// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i64.html"]' 'i64'
// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html"]' 'std::primitive::i32'
// @has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.str.html"]' 'std::primitive::str'
// FIXME: this doesn't resolve
// @ has foo/struct.Foo.html '//*[@class="docblock"]/p/a[@href="https://doc.rust-lang.org/nightly/std/primitive.i32.html#associatedconstant.MAX"]' 'std::primitive::i32::MAX'
/// It contains [`u32`] and [i64].
/// It also links to [std::primitive::i32], [std::primitive::str],
/// and [`std::primitive::i32::MAX`].
pub struct Foo;