Auto merge of #75815 - jyn514:ambiguous-primitives, r=guillaumegomez

Report an ambiguity if both modules and primitives are in scope for intra-doc links

Closes https://github.com/rust-lang/rust/issues/75381

- Add a new `prim@` disambiguator, since both modules and primitives are in the same namespace
- Refactor `report_ambiguity` into a closure

Additionally, I noticed that rustdoc would previously allow `[struct@char]` if `char` resolved to a primitive (not if it had a DefId). I fixed that and added a test case.

I also need to update libstd to use `prim@char` instead of `type@char`. If possible I would also like to refactor `ambiguity_error` to use `Disambiguator` instead of its own hand-rolled match - that ran into issues with `prim@` (I updated one and not the other) and it would be better for them to be in sync.
This commit is contained in:
bors 2020-08-24 10:29:29 +00:00
commit aa7010df90
11 changed files with 253 additions and 70 deletions

View File

@ -268,8 +268,8 @@ use crate::vec::Vec;
///
/// Here, there's no need to allocate more memory inside the loop.
///
/// [`str`]: type@str
/// [`&str`]: type@str
/// [`str`]: prim@str
/// [`&str`]: prim@str
/// [`Deref`]: core::ops::Deref
/// [`as_str()`]: String::as_str
#[derive(PartialOrd, Eq, Ord)]
@ -296,7 +296,7 @@ pub struct String {
///
/// [`Utf8Error`]: core::str::Utf8Error
/// [`std::str`]: core::str
/// [`&str`]: str
/// [`&str`]: prim@str
/// [`utf8_error`]: Self::utf8_error
///
/// # Examples
@ -472,7 +472,7 @@ impl String {
///
/// [`from_utf8_unchecked`]: String::from_utf8_unchecked
/// [`Vec<u8>`]: crate::vec::Vec
/// [`&str`]: str
/// [`&str`]: prim@str
/// [`into_bytes`]: String::into_bytes
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
@ -1576,6 +1576,8 @@ impl String {
///
/// This will drop any excess capacity.
///
/// [`str`]: prim@str
///
/// # Examples
///
/// Basic usage:
@ -1644,7 +1646,7 @@ impl FromUtf8Error {
/// on using it.
///
/// [`std::str`]: core::str
/// [`&str`]: str
/// [`&str`]: prim@str
///
/// # Examples
///

View File

@ -476,6 +476,7 @@ Section: Iterators
/// This struct is created by the [`chars`] method on [`str`].
/// See its documentation for more.
///
/// [`char`]: prim@char
/// [`chars`]: str::chars
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
@ -673,6 +674,7 @@ impl<'a> Chars<'a> {
/// This struct is created by the [`char_indices`] method on [`str`].
/// See its documentation for more.
///
/// [`char`]: prim@char
/// [`char_indices`]: str::char_indices
#[derive(Clone, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
@ -2270,6 +2272,8 @@ impl str {
/// This length is in bytes, not [`char`]s or graphemes. In other words,
/// it may not be what a human considers the length of the string.
///
/// [`char`]: prim@char
///
/// # Examples
///
/// Basic usage:
@ -2791,7 +2795,9 @@ impl str {
/// assert_eq!(None, chars.next());
/// ```
///
/// Remember, [`char`]s may not match your human intuition about characters:
/// Remember, [`char`]s may not match your intuition about characters:
///
/// [`char`]: prim@char
///
/// ```
/// let y = "y̆";
@ -2842,7 +2848,9 @@ impl str {
/// assert_eq!(None, char_indices.next());
/// ```
///
/// Remember, [`char`]s may not match your human intuition about characters:
/// Remember, [`char`]s may not match your intuition about characters:
///
/// [`char`]: prim@char
///
/// ```
/// let yes = "y̆es";
@ -3053,6 +3061,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3079,6 +3088,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3104,6 +3114,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3132,6 +3143,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3179,6 +3191,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3225,6 +3238,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3344,6 +3358,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -3383,6 +3398,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3434,6 +3450,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// Equivalent to [`split`], except that the trailing substring
@ -3478,6 +3495,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// Equivalent to [`split`], except that the trailing substring is
@ -3526,6 +3544,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3578,6 +3597,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3666,6 +3686,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3702,6 +3723,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3743,6 +3765,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -3785,6 +3808,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Iterator behavior
@ -4003,6 +4027,7 @@ impl str {
/// The [pattern] can be a [`char`], a slice of [`char`]s, or a function
/// or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -4050,6 +4075,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Text directionality
@ -4094,6 +4120,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -4121,6 +4148,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Examples
@ -4147,6 +4175,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Text directionality
@ -4195,6 +4224,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Text directionality
@ -4231,6 +4261,7 @@ impl str {
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
///
/// [`char`]: prim@char
/// [pattern]: self::pattern
///
/// # Text directionality

View File

@ -296,6 +296,8 @@ impl From<String> for Box<dyn Error> {
impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
/// Converts a [`str`] into a box of dyn [`Error`] + [`Send`] + [`Sync`].
///
/// [`str`]: prim@str
///
/// # Examples
///
/// ```
@ -317,6 +319,8 @@ impl<'a> From<&str> for Box<dyn Error + Send + Sync + 'a> {
impl From<&str> for Box<dyn Error> {
/// Converts a [`str`] into a box of dyn [`Error`].
///
/// [`str`]: prim@str
///
/// # Examples
///
/// ```

View File

@ -69,7 +69,7 @@ use crate::sys;
/// extern functions. See the documentation for that function for a
/// discussion on ensuring the lifetime of the raw pointer.
///
/// [`&str`]: str
/// [`&str`]: prim@str
/// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr
/// [slice.len]: ../primitive.slice.html#method.len
/// [`Deref`]: ops::Deref
@ -180,7 +180,7 @@ pub struct CString {
/// println!("string: {}", my_string_safe());
/// ```
///
/// [`&str`]: str
/// [`&str`]: prim@str
#[derive(Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
// FIXME:
@ -1351,7 +1351,7 @@ impl CStr {
/// function will return the corresponding [`&str`] slice. Otherwise,
/// it will return an error with details of where UTF-8 validation failed.
///
/// [`&str`]: str
/// [`&str`]: prim@str
///
/// # Examples
///
@ -1379,6 +1379,7 @@ impl CStr {
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a
/// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result.
///
/// [`str`]: prim@str
/// [`Borrowed`]: Cow::Borrowed
/// [`Owned`]: Cow::Owned
/// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER

View File

@ -480,7 +480,7 @@ where
/// ```
///
/// [`read()`]: Read::read
/// [`&str`]: str
/// [`&str`]: prim@str
/// [`std::io`]: self
/// [`File`]: crate::fs::File
/// [slice]: ../../std/primitive.slice.html

View File

@ -172,6 +172,7 @@
//! [`Vec<T>`]: vec::Vec
//! [`atomic`]: sync::atomic
//! [`for`]: ../book/ch03-05-control-flow.html#looping-through-a-collection-with-for
//! [`str`]: prim@str
//! [`mpsc`]: sync::mpsc
//! [`std::cmp`]: cmp
//! [`std::slice`]: slice

View File

@ -81,7 +81,7 @@ impl<T> AsyncReceiver<T> {
}
```
Paths in Rust have three namespaces: type, value, and macro. Items from these namespaces are allowed to overlap. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `function@`, `mod@`, `fn@`, `module@`, `method@` , `macro@`, or `derive@`:
Paths in Rust have three namespaces: type, value, and macro. Items from these namespaces are allowed to overlap. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `function@`, `mod@`, `fn@`, `module@`, `method@`, `prim@`, `primitive@`, `macro@`, or `derive@`:
```rust
/// See also: [`Foo`](struct@Foo)

View File

@ -181,7 +181,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
fn resolve(
&self,
path_str: &str,
disambiguator: Option<Disambiguator>,
ns: Namespace,
current_item: &Option<String>,
parent_id: Option<DefId>,
@ -218,18 +217,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
return Ok((res, Some(path_str.to_owned())));
}
Res::Def(DefKind::Mod, _) => {
// This resolved to a module, but we want primitive types to take precedence instead.
if matches!(
disambiguator,
None | Some(Disambiguator::Namespace(Namespace::TypeNS))
) {
if let Some((path, prim)) = is_primitive(path_str, ns) {
if extra_fragment.is_some() {
return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
}
return Ok((prim, Some(path.to_owned())));
}
}
return Ok((res, extra_fragment.clone()));
}
_ => {
@ -713,7 +700,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
let resolved_self;
let mut path_str;
let disambiguator;
let (res, fragment) = {
let (mut res, mut fragment) = {
path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
disambiguator = Some(d);
path
@ -754,14 +741,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
match disambiguator.map(Disambiguator::ns) {
Some(ns @ (ValueNS | TypeNS)) => {
match self.resolve(
path_str,
disambiguator,
ns,
&current_item,
base_node,
&extra_fragment,
) {
match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
{
Ok(res) => res,
Err(ErrorKind::ResolutionFailure) => {
resolution_failure(cx, &item, path_str, &dox, link_range);
@ -784,7 +765,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
.map(|res| (res, extra_fragment.clone())),
type_ns: match self.resolve(
path_str,
disambiguator,
TypeNS,
&current_item,
base_node,
@ -802,7 +782,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
},
value_ns: match self.resolve(
path_str,
disambiguator,
ValueNS,
&current_item,
base_node,
@ -848,13 +827,18 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
if is_derive_trait_collision(&candidates) {
candidates.macro_ns = None;
}
let candidates =
candidates.map(|candidate| candidate.map(|(res, _)| res));
let candidates = [TypeNS, ValueNS, MacroNS]
.iter()
.filter_map(|&ns| candidates[ns].map(|res| (res, ns)));
ambiguity_error(
cx,
&item,
path_str,
&dox,
link_range,
candidates.map(|candidate| candidate.map(|(res, _)| res)),
candidates.collect(),
);
continue;
}
@ -870,13 +854,81 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
}
};
// Check for a primitive which might conflict with a module
// Report the ambiguity and require that the user specify which one they meant.
// FIXME: could there ever be a primitive not in the type namespace?
if matches!(
disambiguator,
None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
) && !matches!(res, Res::PrimTy(_))
{
if let Some((path, prim)) = is_primitive(path_str, TypeNS) {
// `prim@char`
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
if fragment.is_some() {
anchor_failure(
cx,
&item,
path_str,
&dox,
link_range,
AnchorFailure::Primitive,
);
continue;
}
res = prim;
fragment = Some(path.to_owned());
} else {
// `[char]` when a `char` module is in scope
let candidates = vec![(res, TypeNS), (prim, TypeNS)];
ambiguity_error(cx, &item, path_str, &dox, link_range, candidates);
continue;
}
}
}
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
// The resolved item did not match the disambiguator; give a better error than 'not found'
let msg = format!("incompatible link kind for `{}`", path_str);
report_diagnostic(cx, &msg, &item, &dox, link_range.clone(), |diag, sp| {
let note = format!(
"this link resolved to {} {}, which is not {} {}",
resolved.article(),
resolved.descr(),
specified.article(),
specified.descr()
);
let suggestion = resolved.display_for(path_str);
let help_msg =
format!("to link to the {}, use its disambiguator", resolved.descr());
diag.note(&note);
if let Some(sp) = sp {
diag.span_suggestion(
sp,
&help_msg,
suggestion,
Applicability::MaybeIncorrect,
);
} else {
diag.help(&format!("{}: {}", help_msg, suggestion));
}
});
};
if let Res::PrimTy(_) = res {
item.attrs.links.push((ori_link, None, fragment));
match disambiguator {
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
item.attrs.links.push((ori_link, None, fragment))
}
Some(other) => {
report_mismatch(other, Disambiguator::Primitive);
continue;
}
}
} else {
debug!("intra-doc link to {} resolved to {:?}", path_str, res);
// Disallow e.g. linking to enums with `struct@`
if let Res::Def(kind, id) = res {
if let Res::Def(kind, _) = res {
debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
| (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
@ -890,22 +942,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
// All of these are valid, so do nothing
=> {}
(actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
(_, Some(Disambiguator::Kind(expected))) => {
// The resolved item did not match the disambiguator; give a better error than 'not found'
let msg = format!("incompatible link kind for `{}`", path_str);
report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
// HACK(jynelson): by looking at the source I saw the DefId we pass
// for `expected.descr()` doesn't matter, since it's not a crate
let note = format!("this link resolved to {} {}, which is not {} {}", kind.article(), kind.descr(id), expected.article(), expected.descr(id));
let suggestion = Disambiguator::display_for(kind, path_str);
let help_msg = format!("to link to the {}, use its disambiguator", kind.descr(id));
diag.note(&note);
if let Some(sp) = sp {
diag.span_suggestion(sp, &help_msg, suggestion, Applicability::MaybeIncorrect);
} else {
diag.help(&format!("{}: {}", help_msg, suggestion));
}
});
(_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
report_mismatch(specified, Disambiguator::Kind(kind));
continue;
}
}
@ -961,6 +999,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Disambiguator {
Primitive,
Kind(DefKind),
Namespace(Namespace),
}
@ -968,7 +1007,7 @@ enum Disambiguator {
impl Disambiguator {
/// (disambiguator, path_str)
fn from_str(link: &str) -> Result<(Self, &str), ()> {
use Disambiguator::{Kind, Namespace as NS};
use Disambiguator::{Kind, Namespace as NS, Primitive};
let find_suffix = || {
let suffixes = [
@ -999,6 +1038,7 @@ impl Disambiguator {
"type" => NS(Namespace::TypeNS),
"value" => NS(Namespace::ValueNS),
"macro" => NS(Namespace::MacroNS),
"prim" | "primitive" => Primitive,
_ => return find_suffix(),
};
Ok((d, &rest[1..]))
@ -1007,7 +1047,12 @@ impl Disambiguator {
}
}
fn display_for(kind: DefKind, path_str: &str) -> String {
fn display_for(self, path_str: &str) -> String {
let kind = match self {
Disambiguator::Primitive => return format!("prim@{}", path_str),
Disambiguator::Kind(kind) => kind,
Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
};
if kind == DefKind::Macro(MacroKind::Bang) {
return format!("{}!", path_str);
} else if kind == DefKind::Fn || kind == DefKind::AssocFn {
@ -1043,6 +1088,25 @@ impl Disambiguator {
Self::Kind(k) => {
k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
}
Self::Primitive => TypeNS,
}
}
fn article(self) -> &'static str {
match self {
Self::Namespace(_) => panic!("article() doesn't make sense for namespaces"),
Self::Kind(k) => k.article(),
Self::Primitive => "a",
}
}
fn descr(self) -> &'static str {
match self {
Self::Namespace(n) => n.descr(),
// HACK(jynelson): by looking at the source I saw the DefId we pass
// for `expected.descr()` doesn't matter, since it's not a crate
Self::Kind(k) => k.descr(DefId::local(hir::def_id::DefIndex::from_usize(0))),
Self::Primitive => "builtin type",
}
}
}
@ -1183,14 +1247,10 @@ fn ambiguity_error(
path_str: &str,
dox: &str,
link_range: Option<Range<usize>>,
candidates: PerNS<Option<Res>>,
candidates: Vec<(Res, Namespace)>,
) {
let mut msg = format!("`{}` is ", path_str);
let candidates = [TypeNS, ValueNS, MacroNS]
.iter()
.filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
.collect::<Vec<_>>();
match candidates.as_slice() {
[(first_def, _), (second_def, _)] => {
msg += &format!(
@ -1229,6 +1289,7 @@ fn ambiguity_error(
}
_ => {
let type_ = match (res, ns) {
(Res::PrimTy(_), _) => "prim",
(Res::Def(DefKind::Const, _), _) => "const",
(Res::Def(DefKind::Static, _), _) => "static",
(Res::Def(DefKind::Struct, _), _) => "struct",

View File

@ -0,0 +1,30 @@
#![deny(broken_intra_doc_links)]
//~^ NOTE lint level is defined
/// [char]
//~^ ERROR both a module and a builtin type
//~| NOTE ambiguous link
//~| HELP to link to the module
//~| HELP to link to the builtin type
/// [type@char]
//~^ ERROR both a module and a builtin type
//~| NOTE ambiguous link
//~| HELP to link to the module
//~| HELP to link to the builtin type
/// [mod@char] // ok
/// [prim@char] // ok
/// [struct@char]
//~^ ERROR incompatible link
//~| HELP use its disambiguator
//~| NOTE resolved to a module
pub mod char {}
pub mod inner {
//! [struct@char]
//~^ ERROR incompatible link
//~| HELP use its disambiguator
//~| NOTE resolved to a builtin type
}

View File

@ -0,0 +1,53 @@
error: `char` is both a module and a builtin type
--> $DIR/intra-link-prim-conflict.rs:4:6
|
LL | /// [char]
| ^^^^ ambiguous link
|
note: the lint level is defined here
--> $DIR/intra-link-prim-conflict.rs:1:9
|
LL | #![deny(broken_intra_doc_links)]
| ^^^^^^^^^^^^^^^^^^^^^^
help: to link to the module, prefix with the item type
|
LL | /// [module@char]
| ^^^^^^^^^^^
help: to link to the builtin type, prefix with the item type
|
LL | /// [prim@char]
| ^^^^^^^^^
error: `char` is both a module and a builtin type
--> $DIR/intra-link-prim-conflict.rs:10:6
|
LL | /// [type@char]
| ^^^^^^^^^ ambiguous link
|
help: to link to the module, prefix with the item type
|
LL | /// [module@char]
| ^^^^^^^^^^^
help: to link to the builtin type, prefix with the item type
|
LL | /// [prim@char]
| ^^^^^^^^^
error: incompatible link kind for `char`
--> $DIR/intra-link-prim-conflict.rs:19:6
|
LL | /// [struct@char]
| ^^^^^^^^^^^ help: to link to the module, use its disambiguator: `mod@char`
|
= note: this link resolved to a module, which is not a struct
error: incompatible link kind for `char`
--> $DIR/intra-link-prim-conflict.rs:26:10
|
LL | //! [struct@char]
| ^^^^^^^^^^^ help: to link to the builtin type, use its disambiguator: `prim@char`
|
= note: this link resolved to a builtin type, which is not a struct
error: aborting due to 4 previous errors

View File

@ -1,17 +1,17 @@
// ignore-tidy-linelength
#![deny(broken_intra_doc_links)]
pub mod char {}
pub mod char {
/// [char]
// @has intra_link_prim_precedence/char/struct.Inner.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html'
pub struct Inner;
}
/// See also [type@char]
/// See [prim@char]
// @has intra_link_prim_precedence/struct.MyString.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html'
pub struct MyString;
/// See also [char]
// @has intra_link_prim_precedence/struct.MyString2.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html'
pub struct MyString2;
/// See also [crate::char] and [mod@char]
// @has intra_link_prim_precedence/struct.MyString3.html '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'crate::char'
// @has intra_link_prim_precedence/struct.MyString2.html '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'crate::char'
// @has - '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'mod@char'
pub struct MyString3;
pub struct MyString2;