1 //! This module implements [RFC 1946]: Intra-rustdoc-links
3 //! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
6 use rustc_data_structures
::{fx::FxHashMap, stable_set::FxHashSet}
;
7 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
8 use rustc_expand
::base
::SyntaxExtensionKind
;
15 use rustc_hir
::def_id
::{CrateNum, DefId}
;
16 use rustc_middle
::ty
::TyCtxt
;
17 use rustc_middle
::{bug, span_bug, ty}
;
18 use rustc_resolve
::ParentScope
;
19 use rustc_session
::lint
::Lint
;
20 use rustc_span
::hygiene
::{MacroKind, SyntaxContext}
;
21 use rustc_span
::symbol
::{sym, Ident, Symbol}
;
22 use rustc_span
::{BytePos, DUMMY_SP}
;
23 use smallvec
::{smallvec, SmallVec}
;
25 use pulldown_cmark
::LinkType
;
29 use std
::convert
::{TryFrom, TryInto}
;
33 use crate::clean
::{self, utils::find_nearest_parent_module, Crate, Item, ItemLink, PrimitiveType}
;
34 use crate::core
::DocContext
;
35 use crate::fold
::DocFolder
;
36 use crate::html
::markdown
::{markdown_links, MarkdownLink}
;
37 use crate::lint
::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}
;
38 use crate::passes
::Pass
;
41 crate use early
::load_intra_link_crates
;
43 crate const COLLECT_INTRA_DOC_LINKS
: Pass
= Pass
{
44 name
: "collect-intra-doc-links",
45 run
: collect_intra_doc_links
,
46 description
: "resolves intra-doc links",
49 fn collect_intra_doc_links(krate
: Crate
, cx
: &mut DocContext
<'_
>) -> Crate
{
53 kind_side_channel
: Cell
::new(None
),
54 visited_links
: FxHashMap
::default(),
59 /// Top-level errors emitted by this pass.
61 Resolve(Box
<ResolutionFailure
<'a
>>),
62 AnchorFailure(AnchorFailure
),
65 impl<'a
> From
<ResolutionFailure
<'a
>> for ErrorKind
<'a
> {
66 fn from(err
: ResolutionFailure
<'a
>) -> Self {
67 ErrorKind
::Resolve(box err
)
71 #[derive(Copy, Clone, Debug, Hash)]
74 Primitive(PrimitiveType
),
77 type ResolveRes
= rustc_hir
::def
::Res
<rustc_ast
::NodeId
>;
80 fn descr(self) -> &'
static str {
82 Res
::Def(kind
, id
) => ResolveRes
::Def(kind
, id
).descr(),
83 Res
::Primitive(_
) => "builtin type",
87 fn article(self) -> &'
static str {
89 Res
::Def(kind
, id
) => ResolveRes
::Def(kind
, id
).article(),
90 Res
::Primitive(_
) => "a",
94 fn name(self, tcx
: TyCtxt
<'_
>) -> Symbol
{
96 Res
::Def(_
, id
) => tcx
.item_name(id
),
97 Res
::Primitive(prim
) => prim
.as_sym(),
101 fn def_id(self, tcx
: TyCtxt
<'_
>) -> DefId
{
103 Res
::Def(_
, id
) => id
,
104 Res
::Primitive(prim
) => *PrimitiveType
::primitive_locations(tcx
).get(&prim
).unwrap(),
108 fn as_hir_res(self) -> Option
<rustc_hir
::def
::Res
> {
110 Res
::Def(kind
, id
) => Some(rustc_hir
::def
::Res
::Def(kind
, id
)),
111 // FIXME: maybe this should handle the subset of PrimitiveType that fits into hir::PrimTy?
112 Res
::Primitive(_
) => None
,
117 impl TryFrom
<ResolveRes
> for Res
{
120 fn try_from(res
: ResolveRes
) -> Result
<Self, ()> {
121 use rustc_hir
::def
::Res
::*;
123 Def(kind
, id
) => Ok(Res
::Def(kind
, id
)),
124 PrimTy(prim
) => Ok(Res
::Primitive(PrimitiveType
::from_hir(prim
))),
126 NonMacroAttr(..) | Err
=> Result
::Err(()),
127 other
=> bug
!("unrecognized res {:?}", other
),
132 /// A link failed to resolve.
134 enum ResolutionFailure
<'a
> {
135 /// This resolved, but with the wrong namespace.
137 /// What the link resolved to.
139 /// The expected namespace for the resolution, determined from the link's disambiguator.
141 /// E.g., for `[fn@Result]` this is [`Namespace::ValueNS`],
142 /// even though `Result`'s actual namespace is [`Namespace::TypeNS`].
143 expected_ns
: Namespace
,
145 /// The link failed to resolve. [`resolution_failure`] should look to see if there's
146 /// a more helpful error that can be given.
148 /// The scope the link was resolved in.
150 /// If part of the link resolved, this has the `Res`.
152 /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution.
153 partial_res
: Option
<Res
>,
154 /// The remaining unresolved path segments.
156 /// In `[std::io::Error::x]`, `x` would be unresolved.
157 unresolved
: Cow
<'a
, str>,
159 /// This happens when rustdoc can't determine the parent scope for an item.
160 /// It is always a bug in rustdoc.
162 /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
163 MalformedGenerics(MalformedGenerics
),
164 /// Used to communicate that this should be ignored, but shouldn't be reported to the user.
166 /// This happens when there is no disambiguator and one of the namespaces
167 /// failed to resolve.
172 enum MalformedGenerics
{
173 /// This link has unbalanced angle brackets.
175 /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
176 UnbalancedAngleBrackets
,
177 /// The generics are not attached to a type.
179 /// For example, `<T>` should trigger this.
181 /// This is detected by checking if the path is empty after the generics are stripped.
183 /// The link uses fully-qualified syntax, which is currently unsupported.
185 /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
187 /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
189 HasFullyQualifiedSyntax
,
190 /// The link has an invalid path separator.
192 /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not**
193 /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be
196 /// Note that this will also **not** be triggered if the invalid path separator is inside angle
197 /// brackets because rustdoc mostly ignores what's inside angle brackets (except for
198 /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)).
200 /// This is detected by checking if there is a colon followed by a non-colon in the link.
201 InvalidPathSeparator
,
202 /// The link has too many angle brackets.
204 /// For example, `Vec<<T>>` should trigger this.
205 TooManyAngleBrackets
,
206 /// The link has empty angle brackets.
208 /// For example, `Vec<>` should trigger this.
212 impl ResolutionFailure
<'a
> {
213 /// This resolved fully (not just partially) but is erroneous for some other reason
215 /// Returns the full resolution of the link, if present.
216 fn full_res(&self) -> Option
<Res
> {
218 Self::WrongNamespace { res, expected_ns: _ }
=> Some(*res
),
225 /// User error: `[std#x#y]` is not valid
227 /// The anchor provided by the user conflicts with Rustdoc's generated anchor.
229 /// This is an unfortunate state of affairs. Not every item that can be
230 /// linked to has its own page; sometimes it is a subheading within a page,
231 /// like for associated items. In those cases, rustdoc uses an anchor to
232 /// link to the subheading. Since you can't have two anchors for the same
233 /// link, Rustdoc disallows having a user-specified anchor.
235 /// Most of the time this is fine, because you can just link to the page of
236 /// the item if you want to provide your own anchor.
237 RustdocAnchorConflict(Res
),
240 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
241 struct ResolutionInfo
{
243 dis
: Option
<Disambiguator
>,
245 extra_fragment
: Option
<String
>,
249 struct DiagnosticInfo
<'a
> {
253 link_range
: Range
<usize>,
256 #[derive(Clone, Debug, Hash)]
258 pub res
: (Res
, Option
<String
>),
259 pub side_channel
: Option
<(DefKind
, DefId
)>,
262 struct LinkCollector
<'a
, 'tcx
> {
263 cx
: &'a
mut DocContext
<'tcx
>,
264 /// A stack of modules used to decide what scope to resolve in.
266 /// The last module will be used if the parent scope of the current item is
269 /// This is used to store the kind of associated items,
270 /// because `clean` and the disambiguator code expect them to be different.
271 /// See the code for associated items on inherent impls for details.
272 kind_side_channel
: Cell
<Option
<(DefKind
, DefId
)>>,
273 /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
274 /// The link will be `None` if it could not be resolved (i.e. the error was cached).
275 visited_links
: FxHashMap
<ResolutionInfo
, Option
<CachedLink
>>,
278 impl<'a
, 'tcx
> LinkCollector
<'a
, 'tcx
> {
279 /// Given a full link, parse it as an [enum struct variant].
281 /// In particular, this will return an error whenever there aren't three
282 /// full path segments left in the link.
284 /// [enum struct variant]: hir::VariantData::Struct
287 path_str
: &'path
str,
289 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'path
>> {
290 let tcx
= self.cx
.tcx
;
291 let no_res
= || ResolutionFailure
::NotResolved
{
292 module_id
: module_id
,
294 unresolved
: path_str
.into(),
297 debug
!("looking for enum variant {}", path_str
);
298 let mut split
= path_str
.rsplitn(3, "::");
299 let (variant_field_str
, variant_field_name
) = split
301 .map(|f
| (f
, Symbol
::intern(f
)))
302 .expect("fold_item should ensure link is non-empty");
303 let (variant_str
, variant_name
) =
304 // we're not sure this is a variant at all, so use the full string
305 // If there's no second component, the link looks like `[path]`.
306 // So there's no partial res and we should say the whole link failed to resolve.
307 split
.next().map(|f
| (f
, Symbol
::intern(f
))).ok_or_else(no_res
)?
;
310 .map(|f
| f
.to_owned())
311 // If there's no third component, we saw `[a::b]` before and it failed to resolve.
312 // So there's no partial res.
313 .ok_or_else(no_res
)?
;
316 .enter_resolver(|resolver
| {
317 resolver
.resolve_str_path_error(DUMMY_SP
, &path
, TypeNS
, module_id
)
319 .and_then(|(_
, res
)| res
.try_into())
320 .map_err(|()| no_res())?
;
323 Res
::Def(DefKind
::Enum
, did
) => {
327 .flat_map(|imp
| tcx
.associated_items(*imp
).in_definition_order())
328 .any(|item
| item
.ident
.name
== variant_name
)
330 // This is just to let `fold_item` know that this shouldn't be considered;
331 // it's a bug for the error to make it to the user
332 return Err(ResolutionFailure
::Dummy
.into());
334 match tcx
.type_of(did
).kind() {
335 ty
::Adt(def
, _
) if def
.is_enum() => {
336 if def
.all_fields().any(|item
| item
.ident
.name
== variant_field_name
) {
340 "variant.{}.field.{}",
341 variant_str
, variant_field_name
345 Err(ResolutionFailure
::NotResolved
{
347 partial_res
: Some(Res
::Def(DefKind
::Enum
, def
.did
)),
348 unresolved
: variant_field_str
.into(),
356 _
=> Err(ResolutionFailure
::NotResolved
{
358 partial_res
: Some(ty_res
),
359 unresolved
: variant_str
.into(),
365 /// Given a primitive type, try to resolve an associated item.
366 fn resolve_primitive_associated_item(
368 prim_ty
: PrimitiveType
,
371 ) -> Option
<(Res
, String
, Option
<(DefKind
, DefId
)>)> {
372 let tcx
= self.cx
.tcx
;
374 prim_ty
.impls(tcx
).into_iter().find_map(|&impl_
| {
375 tcx
.associated_items(impl_
)
376 .find_by_name_and_namespace(tcx
, Ident
::with_dummy_span(item_name
), ns
, impl_
)
378 let kind
= item
.kind
;
379 let out
= match kind
{
380 ty
::AssocKind
::Fn
=> "method",
381 ty
::AssocKind
::Const
=> "associatedconstant",
382 ty
::AssocKind
::Type
=> "associatedtype",
384 let fragment
= format
!("{}.{}", out
, item_name
);
385 (Res
::Primitive(prim_ty
), fragment
, Some((kind
.as_def_kind(), item
.def_id
)))
390 /// Resolves a string as a macro.
392 /// FIXME(jynelson): Can this be unified with `resolve()`?
397 ) -> Result
<Res
, ResolutionFailure
<'a
>> {
398 let path
= ast
::Path
::from_ident(Ident
::from_str(path_str
));
399 self.cx
.enter_resolver(|resolver
| {
400 // FIXME(jynelson): does this really need 3 separate lookups?
401 if let Ok((Some(ext
), res
)) = resolver
.resolve_macro_path(
404 &ParentScope
::module(resolver
.graph_root(), resolver
),
408 if let SyntaxExtensionKind
::LegacyBang { .. }
= ext
.kind
{
409 return Ok(res
.try_into().unwrap());
412 if let Some(&res
) = resolver
.all_macros().get(&Symbol
::intern(path_str
)) {
413 return Ok(res
.try_into().unwrap());
415 debug
!("resolving {} as a macro in the module {:?}", path_str
, module_id
);
416 if let Ok((_
, res
)) =
417 resolver
.resolve_str_path_error(DUMMY_SP
, path_str
, MacroNS
, module_id
)
419 // don't resolve builtins like `#[derive]`
420 if let Ok(res
) = res
.try_into() {
424 Err(ResolutionFailure
::NotResolved
{
427 unresolved
: path_str
.into(),
432 /// Convenience wrapper around `resolve_str_path_error`.
434 /// This also handles resolving `true` and `false` as booleans.
435 /// NOTE: `resolve_str_path_error` knows only about paths, not about types.
436 /// Associated items will never be resolved by this function.
437 fn resolve_path(&self, path_str
: &str, ns
: Namespace
, module_id
: DefId
) -> Option
<Res
> {
438 let result
= self.cx
.enter_resolver(|resolver
| {
440 .resolve_str_path_error(DUMMY_SP
, &path_str
, ns
, module_id
)
441 .and_then(|(_
, res
)| res
.try_into())
443 debug
!("{} resolved to {:?} in namespace {:?}", path_str
, result
, ns
);
445 // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
447 Err(()) => resolve_primitive(path_str
, ns
),
448 Ok(res
) => Some(res
),
452 /// Resolves a string as a path within a particular namespace. Returns an
453 /// optional URL fragment in the case of variants and methods.
456 path_str
: &'path
str,
459 extra_fragment
: &Option
<String
>,
460 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'path
>> {
461 if let Some(res
) = self.resolve_path(path_str
, ns
, module_id
) {
463 // FIXME(#76467): make this fallthrough to lookup the associated
464 // item a separate function.
465 Res
::Def(DefKind
::AssocFn
| DefKind
::AssocConst
, _
) => assert_eq
!(ns
, ValueNS
),
466 Res
::Def(DefKind
::AssocTy
, _
) => assert_eq
!(ns
, TypeNS
),
467 Res
::Def(DefKind
::Variant
, _
) => {
468 return handle_variant(self.cx
, res
, extra_fragment
);
470 // Not a trait item; just return what we found.
471 _
=> return Ok((res
, extra_fragment
.clone())),
475 // Try looking for methods and associated items.
476 let mut split
= path_str
.rsplitn(2, "::");
477 // NB: `split`'s first element is always defined, even if the delimiter was not present.
478 // NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`).
479 let item_str
= split
.next().unwrap();
480 let item_name
= Symbol
::intern(item_str
);
481 let path_root
= split
483 .map(|f
| f
.to_owned())
484 // If there's no `::`, it's not an associated item.
485 // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
487 debug
!("found no `::`, assumming {} was correctly not in scope", item_name
);
488 ResolutionFailure
::NotResolved
{
491 unresolved
: item_str
.into(),
495 // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
496 // links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
497 // error instead and special case *only* modules with `#[doc(primitive)]`, not all
499 resolve_primitive(&path_root
, TypeNS
)
500 .or_else(|| self.resolve_path(&path_root
, TypeNS
, module_id
))
502 let (res
, fragment
, side_channel
) =
503 self.resolve_associated_item(ty_res
, item_name
, ns
, module_id
)?
;
504 let result
= if extra_fragment
.is_some() {
505 // NOTE: can never be a primitive since `side_channel.is_none()` only when `res`
506 // is a trait (and the side channel DefId is always an associated item).
507 let diag_res
= side_channel
.map_or(res
, |(k
, r
)| Res
::Def(k
, r
));
508 Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(diag_res
)))
510 // HACK(jynelson): `clean` expects the type, not the associated item
511 // but the disambiguator logic expects the associated item.
512 // Store the kind in a side channel so that only the disambiguator logic looks at it.
513 if let Some((kind
, id
)) = side_channel
{
514 self.kind_side_channel
.set(Some((kind
, id
)));
516 Ok((res
, Some(fragment
)))
521 if ns
== Namespace
::ValueNS
{
522 self.variant_field(path_str
, module_id
)
524 Err(ResolutionFailure
::NotResolved
{
527 unresolved
: path_root
.into(),
534 /// Convert a DefId to a Res, where possible.
536 /// This is used for resolving type aliases.
537 fn def_id_to_res(&self, ty_id
: DefId
) -> Option
<Res
> {
538 use PrimitiveType
::*;
539 Some(match *self.cx
.tcx
.type_of(ty_id
).kind() {
540 ty
::Bool
=> Res
::Primitive(Bool
),
541 ty
::Char
=> Res
::Primitive(Char
),
542 ty
::Int(ity
) => Res
::Primitive(ity
.into()),
543 ty
::Uint(uty
) => Res
::Primitive(uty
.into()),
544 ty
::Float(fty
) => Res
::Primitive(fty
.into()),
545 ty
::Str
=> Res
::Primitive(Str
),
546 ty
::Tuple(ref tys
) if tys
.is_empty() => Res
::Primitive(Unit
),
547 ty
::Tuple(_
) => Res
::Primitive(Tuple
),
548 ty
::Array(..) => Res
::Primitive(Array
),
549 ty
::Slice(_
) => Res
::Primitive(Slice
),
550 ty
::RawPtr(_
) => Res
::Primitive(RawPointer
),
551 ty
::Ref(..) => Res
::Primitive(Reference
),
552 ty
::FnDef(..) => panic
!("type alias to a function definition"),
553 ty
::FnPtr(_
) => Res
::Primitive(Fn
),
554 ty
::Never
=> Res
::Primitive(Never
),
555 ty
::Adt(&ty
::AdtDef { did, .. }
, _
) | ty
::Foreign(did
) => {
556 Res
::Def(self.cx
.tcx
.def_kind(did
), did
)
561 | ty
::GeneratorWitness(_
)
568 | ty
::Error(_
) => return None
,
573 /// - None if no associated item was found
574 /// - Some((_, _, Some(_))) if an item was found and should go through a side channel
575 /// - Some((_, _, None)) otherwise
576 fn resolve_associated_item(
582 ) -> Option
<(Res
, String
, Option
<(DefKind
, DefId
)>)> {
583 let tcx
= self.cx
.tcx
;
586 Res
::Primitive(prim
) => self.resolve_primitive_associated_item(prim
, ns
, item_name
),
587 Res
::Def(DefKind
::TyAlias
, did
) => {
588 // Resolve the link on the type the alias points to.
589 // FIXME: if the associated item is defined directly on the type alias,
590 // it will show up on its documentation page, we should link there instead.
591 let res
= self.def_id_to_res(did
)?
;
592 self.resolve_associated_item(res
, item_name
, ns
, module_id
)
595 DefKind
::Struct
| DefKind
::Union
| DefKind
::Enum
| DefKind
::ForeignTy
,
598 debug
!("looking for associated item named {} for item {:?}", item_name
, did
);
599 // Checks if item_name belongs to `impl SomeItem`
604 tcx
.associated_items(imp
).find_by_name_and_namespace(
606 Ident
::with_dummy_span(item_name
),
611 .map(|item
| (item
.kind
, item
.def_id
))
612 // There should only ever be one associated item that matches from any inherent impl
614 // Check if item_name belongs to `impl SomeTrait for SomeItem`
615 // FIXME(#74563): This gives precedence to `impl SomeItem`:
616 // Although having both would be ambiguous, use impl version for compatibility's sake.
617 // To handle that properly resolve() would have to support
618 // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
621 resolve_associated_trait_item(did
, module_id
, item_name
, ns
, self.cx
);
622 debug
!("got associated item kind {:?}", kind
);
626 if let Some((kind
, id
)) = assoc_item
{
627 let out
= match kind
{
628 ty
::AssocKind
::Fn
=> "method",
629 ty
::AssocKind
::Const
=> "associatedconstant",
630 ty
::AssocKind
::Type
=> "associatedtype",
632 // HACK(jynelson): `clean` expects the type, not the associated item
633 // but the disambiguator logic expects the associated item.
634 // Store the kind in a side channel so that only the disambiguator logic looks at it.
637 format
!("{}.{}", out
, item_name
),
638 Some((kind
.as_def_kind(), id
)),
642 if ns
!= Namespace
::ValueNS
{
645 debug
!("looking for variants or fields named {} for {:?}", item_name
, did
);
646 // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?)
647 // NOTE: it's different from variant_field because it resolves fields and variants,
648 // not variant fields (2 path segments, not 3).
649 let def
= match tcx
.type_of(did
).kind() {
650 ty
::Adt(def
, _
) => def
,
653 let field
= if def
.is_enum() {
654 def
.all_fields().find(|item
| item
.ident
.name
== item_name
)
656 def
.non_enum_variant().fields
.iter().find(|item
| item
.ident
.name
== item_name
)
658 let kind
= if def
.is_enum() { DefKind::Variant }
else { DefKind::Field }
;
663 if def
.is_enum() { "variant" }
else { "structfield" }
,
666 Some((kind
, field
.did
)),
669 Res
::Def(DefKind
::Trait
, did
) => tcx
670 .associated_items(did
)
671 .find_by_name_and_namespace(tcx
, Ident
::with_dummy_span(item_name
), ns
, did
)
673 let kind
= match item
.kind
{
674 ty
::AssocKind
::Const
=> "associatedconstant",
675 ty
::AssocKind
::Type
=> "associatedtype",
676 ty
::AssocKind
::Fn
=> {
677 if item
.defaultness
.has_value() {
685 let res
= Res
::Def(item
.kind
.as_def_kind(), item
.def_id
);
686 (res
, format
!("{}.{}", kind
, item_name
), None
)
692 /// Used for reporting better errors.
694 /// Returns whether the link resolved 'fully' in another namespace.
695 /// 'fully' here means that all parts of the link resolved, not just some path segments.
696 /// This returns the `Res` even if it was erroneous for some reason
697 /// (such as having invalid URL fragments or being in the wrong namespace).
703 extra_fragment
: &Option
<String
>,
705 // resolve() can't be used for macro namespace
706 let result
= match ns
{
707 Namespace
::MacroNS
=> self.resolve_macro(path_str
, module_id
).map_err(ErrorKind
::from
),
708 Namespace
::TypeNS
| Namespace
::ValueNS
=> {
709 self.resolve(path_str
, ns
, module_id
, extra_fragment
).map(|(res
, _
)| res
)
713 let res
= match result
{
714 Ok(res
) => Some(res
),
715 Err(ErrorKind
::Resolve(box kind
)) => kind
.full_res(),
716 Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(res
))) => Some(res
),
717 Err(ErrorKind
::AnchorFailure(AnchorFailure
::MultipleAnchors
)) => None
,
719 self.kind_side_channel
.take().map(|(kind
, id
)| Res
::Def(kind
, id
)).or(res
)
723 /// Look to see if a resolved item has an associated item named `item_name`.
725 /// Given `[std::io::Error::source]`, where `source` is unresolved, this would
726 /// find `std::error::Error::source` and return
727 /// `<io::Error as error::Error>::source`.
728 fn resolve_associated_trait_item(
733 cx
: &mut DocContext
<'_
>,
734 ) -> Option
<(ty
::AssocKind
, DefId
)> {
735 // FIXME: this should also consider blanket impls (`impl<T> X for T`). Unfortunately
736 // `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the
737 // meantime, just don't look for these blanket impls.
739 // Next consider explicit impls: `impl MyTrait for MyType`
740 // Give precedence to inherent impls.
741 let traits
= traits_implemented_by(cx
, did
, module
);
742 debug
!("considering traits {:?}", traits
);
743 let mut candidates
= traits
.iter().filter_map(|&trait_
| {
745 .associated_items(trait_
)
746 .find_by_name_and_namespace(cx
.tcx
, Ident
::with_dummy_span(item_name
), ns
, trait_
)
747 .map(|assoc
| (assoc
.kind
, assoc
.def_id
))
749 // FIXME(#74563): warn about ambiguity
750 debug
!("the candidates were {:?}", candidates
.clone().collect
::<Vec
<_
>>());
754 /// Given a type, return all traits in scope in `module` implemented by that type.
756 /// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
757 /// So it is not stable to serialize cross-crate.
758 fn traits_implemented_by(cx
: &mut DocContext
<'_
>, type_
: DefId
, module
: DefId
) -> FxHashSet
<DefId
> {
759 let mut resolver
= cx
.resolver
.borrow_mut();
760 let in_scope_traits
= cx
.module_trait_cache
.entry(module
).or_insert_with(|| {
761 resolver
.access(|resolver
| {
762 let parent_scope
= &ParentScope
::module(resolver
.expect_module(module
), resolver
);
764 .traits_in_scope(None
, parent_scope
, SyntaxContext
::root(), None
)
766 .map(|candidate
| candidate
.def_id
)
772 let ty
= tcx
.type_of(type_
);
773 let iter
= in_scope_traits
.iter().flat_map(|&trait_
| {
774 trace
!("considering explicit impl for trait {:?}", trait_
);
776 // Look at each trait implementation to see if it's an impl for `did`
777 tcx
.find_map_relevant_impl(trait_
, ty
, |impl_
| {
778 let trait_ref
= tcx
.impl_trait_ref(impl_
).expect("this is not an inherent impl");
779 // Check if these are the same type.
780 let impl_type
= trait_ref
.self_ty();
782 "comparing type {} with kind {:?} against type {:?}",
787 // Fast path: if this is a primitive simple `==` will work
788 let saw_impl
= impl_type
== ty
789 || match impl_type
.kind() {
790 // Check if these are the same def_id
792 debug
!("adt def_id: {:?}", def
.did
);
795 ty
::Foreign(def_id
) => *def_id
== type_
,
799 if saw_impl { Some(trait_) }
else { None }
805 /// Check for resolve collisions between a trait and its derive.
807 /// These are common and we should just resolve to the trait in that case.
808 fn is_derive_trait_collision
<T
>(ns
: &PerNS
<Result
<(Res
, T
), ResolutionFailure
<'_
>>>) -> bool
{
812 type_ns
: Ok((Res
::Def(DefKind
::Trait
, _
), _
)),
813 macro_ns
: Ok((Res
::Def(DefKind
::Macro(MacroKind
::Derive
), _
), _
)),
819 impl<'a
, 'tcx
> DocFolder
for LinkCollector
<'a
, 'tcx
> {
820 fn fold_item(&mut self, item
: Item
) -> Option
<Item
> {
821 use rustc_middle
::ty
::DefIdTree
;
824 item
.def_id
.as_def_id().and_then(|did
| find_nearest_parent_module(self.cx
.tcx
, did
));
825 if parent_node
.is_some() {
826 trace
!("got parent node for {:?} {:?}, id {:?}", item
.type_(), item
.name
, item
.def_id
);
829 // find item's parent to resolve `Self` in item's docs below
830 debug
!("looking for the `Self` type");
831 let self_id
= match item
.def_id
.as_def_id() {
834 if (matches
!(self.cx
.tcx
.def_kind(did
), DefKind
::Field
)
836 self.cx
.tcx
.def_kind(self.cx
.tcx
.parent(did
).unwrap()),
840 self.cx
.tcx
.parent(did
).and_then(|item_id
| self.cx
.tcx
.parent(item_id
))
844 self.cx
.tcx
.def_kind(did
),
852 self.cx
.tcx
.parent(did
)
854 Some(did
) => match self.cx
.tcx
.parent(did
) {
855 // HACK(jynelson): `clean` marks associated types as `TypedefItem`, not as `AssocTypeItem`.
856 // Fixing this breaks `fn render_deref_methods`.
857 // As a workaround, see if the parent of the item is an `impl`; if so this must be an associated item,
858 // regardless of what rustdoc wants to call it.
860 let parent_kind
= self.cx
.tcx
.def_kind(parent
);
861 Some(if parent_kind
== DefKind
::Impl { parent }
else { did }
)
867 // FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
868 let self_name
= self_id
.and_then(|self_id
| {
869 if matches
!(self.cx
.tcx
.def_kind(self_id
), DefKind
::Impl
) {
870 // using `ty.to_string()` (or any variant) has issues with raw idents
871 let ty
= self.cx
.tcx
.type_of(self_id
);
872 let name
= match ty
.kind() {
873 ty
::Adt(def
, _
) => Some(self.cx
.tcx
.item_name(def
.did
).to_string()),
874 other
if other
.is_primitive() => Some(ty
.to_string()),
877 debug
!("using type_of(): {:?}", name
);
880 let name
= self.cx
.tcx
.opt_item_name(self_id
).map(|sym
| sym
.to_string());
881 debug
!("using item_name(): {:?}", name
);
886 let inner_docs
= item
.inner_docs(self.cx
.tcx
);
888 if item
.is_mod() && inner_docs
{
889 self.mod_ids
.push(item
.def_id
.expect_def_id());
892 // We want to resolve in the lexical scope of the documentation.
893 // In the presence of re-exports, this is not the same as the module of the item.
894 // Rather than merging all documentation into one, resolve it one attribute at a time
895 // so we know which module it came from.
896 for (parent_module
, doc
) in item
.attrs
.collapsed_doc_value_by_module_level() {
897 debug
!("combined_docs={}", doc
);
899 let (krate
, parent_node
) = if let Some(id
) = parent_module
{
902 (item
.def_id
.krate(), parent_node
)
904 // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
905 // This is a degenerate case and it's not supported by rustdoc.
906 for md_link
in markdown_links(&doc
) {
907 let link
= self.resolve_link(&item
, &doc
, &self_name
, parent_node
, krate
, md_link
);
908 if let Some(link
) = link
{
909 self.cx
.cache
.intra_doc_links
.entry(item
.def_id
).or_default().push(link
);
914 Some(if item
.is_mod() {
916 self.mod_ids
.push(item
.def_id
.expect_def_id());
919 let ret
= self.fold_item_recur(item
);
923 self.fold_item_recur(item
)
928 enum PreprocessingError
<'a
> {
929 Anchor(AnchorFailure
),
930 Disambiguator(Range
<usize>, String
),
931 Resolution(ResolutionFailure
<'a
>, String
, Option
<Disambiguator
>),
934 impl From
<AnchorFailure
> for PreprocessingError
<'_
> {
935 fn from(err
: AnchorFailure
) -> Self {
940 struct PreprocessingInfo
{
942 disambiguator
: Option
<Disambiguator
>,
943 extra_fragment
: Option
<String
>,
948 /// - `None` if the link should be ignored.
949 /// - `Some(Err)` if the link should emit an error
950 /// - `Some(Ok)` if the link is valid
952 /// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
953 fn preprocess_link
<'a
>(
954 ori_link
: &'a MarkdownLink
,
955 ) -> Option
<Result
<PreprocessingInfo
, PreprocessingError
<'a
>>> {
956 // [] is mostly likely not supposed to be a link
957 if ori_link
.link
.is_empty() {
961 // Bail early for real links.
962 if ori_link
.link
.contains('
/'
) {
966 let stripped
= ori_link
.link
.replace("`", "");
967 let mut parts
= stripped
.split('
#');
969 let link
= parts
.next().unwrap();
970 if link
.trim().is_empty() {
971 // This is an anchor to an element of the current page, nothing to do in here!
974 let extra_fragment
= parts
.next();
975 if parts
.next().is_some() {
976 // A valid link can't have multiple #'s
977 return Some(Err(AnchorFailure
::MultipleAnchors
.into()));
980 // Parse and strip the disambiguator from the link, if present.
981 let (disambiguator
, path_str
, link_text
) = match Disambiguator
::from_str(&link
) {
982 Ok(Some((d
, path
, link_text
))) => (Some(d
), path
.trim(), link_text
.trim()),
983 Ok(None
) => (None
, link
.trim(), link
.trim()),
984 Err((err_msg
, relative_range
)) => {
985 // Only report error if we would not have ignored this link. See issue #83859.
986 if !should_ignore_link_with_disambiguators(link
) {
987 let no_backticks_range
= range_between_backticks(&ori_link
);
988 let disambiguator_range
= (no_backticks_range
.start
+ relative_range
.start
)
989 ..(no_backticks_range
.start
+ relative_range
.end
);
990 return Some(Err(PreprocessingError
::Disambiguator(disambiguator_range
, err_msg
)));
997 if should_ignore_link(path_str
) {
1001 // Strip generics from the path.
1002 let path_str
= if path_str
.contains(['
<'
, '
>'
].as_slice()) {
1003 match strip_generics_from_path(&path_str
) {
1006 debug
!("link has malformed generics: {}", path_str
);
1007 return Some(Err(PreprocessingError
::Resolution(
1009 path_str
.to_owned(),
1018 // Sanity check to make sure we don't have any angle brackets after stripping generics.
1019 assert
!(!path_str
.contains(['
<'
, '
>'
].as_slice()));
1021 // The link is not an intra-doc link if it still contains spaces after stripping generics.
1022 if path_str
.contains(' '
) {
1026 Some(Ok(PreprocessingInfo
{
1029 extra_fragment
: extra_fragment
.map(String
::from
),
1030 link_text
: link_text
.to_owned(),
1034 impl LinkCollector
<'_
, '_
> {
1035 /// This is the entry point for resolving an intra-doc link.
1037 /// FIXME(jynelson): this is way too many arguments
1042 self_name
: &Option
<String
>,
1043 parent_node
: Option
<DefId
>,
1045 ori_link
: MarkdownLink
,
1046 ) -> Option
<ItemLink
> {
1047 trace
!("considering link '{}'", ori_link
.link
);
1049 let diag_info
= DiagnosticInfo
{
1052 ori_link
: &ori_link
.link
,
1053 link_range
: ori_link
.range
.clone(),
1056 let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text }
=
1057 match preprocess_link(&ori_link
)?
{
1061 PreprocessingError
::Anchor(err
) => anchor_failure(self.cx
, diag_info
, err
),
1062 PreprocessingError
::Disambiguator(range
, msg
) => {
1063 disambiguator_error(self.cx
, diag_info
, range
, &msg
)
1065 PreprocessingError
::Resolution(err
, path_str
, disambiguator
) => {
1078 let mut path_str
= &*path_str
;
1080 let inner_docs
= item
.inner_docs(self.cx
.tcx
);
1082 // In order to correctly resolve intra-doc links we need to
1083 // pick a base AST node to work from. If the documentation for
1084 // this module came from an inner comment (//!) then we anchor
1085 // our name resolution *inside* the module. If, on the other
1086 // hand it was an outer comment (///) then we anchor the name
1087 // resolution in the parent module on the basis that the names
1088 // used are more likely to be intended to be parent names. For
1089 // this, we set base_node to None for inner comments since
1090 // we've already pushed this node onto the resolution stack but
1091 // for outer comments we explicitly try and resolve against the
1092 // parent_node first.
1094 if item
.is_mod() && inner_docs { self.mod_ids.last().copied() }
else { parent_node }
;
1096 let mut module_id
= if let Some(id
) = base_node
{
1100 debug
!("attempting to resolve item without parent module: {}", path_str
);
1106 smallvec
![ResolutionFailure
::NoParentItem
],
1112 // replace `Self` with suitable item's parent name
1113 let is_lone_self
= path_str
== "Self";
1114 let is_lone_crate
= path_str
== "crate";
1115 if path_str
.starts_with("Self::") || is_lone_self
{
1116 if let Some(ref name
) = self_name
{
1120 resolved_self
= format
!("{}::{}", name
, &path_str
[6..]);
1121 path_str
= &resolved_self
;
1124 } else if path_str
.starts_with("crate::") || is_lone_crate
{
1125 use rustc_span
::def_id
::CRATE_DEF_INDEX
;
1127 // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
1128 // But rustdoc wants it to mean the crate this item was originally present in.
1129 // To work around this, remove it and resolve relative to the crate root instead.
1130 // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
1131 // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
1132 // FIXME(#78696): This doesn't always work.
1136 resolved_self
= format
!("self::{}", &path_str
["crate::".len()..]);
1137 path_str
= &resolved_self
;
1139 module_id
= DefId { krate, index: CRATE_DEF_INDEX }
;
1142 let (mut res
, fragment
) = self.resolve_with_disambiguator_cached(
1146 path_str
: path_str
.to_owned(),
1147 extra_fragment
: extra_fragment
.map(String
::from
),
1149 diag_info
.clone(), // this struct should really be Copy, but Range is not :(
1150 matches
!(ori_link
.kind
, LinkType
::Reference
| LinkType
::Shortcut
),
1153 // Check for a primitive which might conflict with a module
1154 // Report the ambiguity and require that the user specify which one they meant.
1155 // FIXME: could there ever be a primitive not in the type namespace?
1158 None
| Some(Disambiguator
::Namespace(Namespace
::TypeNS
) | Disambiguator
::Primitive
)
1159 ) && !matches
!(res
, Res
::Primitive(_
))
1161 if let Some(prim
) = resolve_primitive(path_str
, TypeNS
) {
1163 if matches
!(disambiguator
, Some(Disambiguator
::Primitive
)) {
1166 // `[char]` when a `char` module is in scope
1167 let candidates
= vec
![res
, prim
];
1168 ambiguity_error(self.cx
, diag_info
, path_str
, candidates
);
1174 let report_mismatch
= |specified
: Disambiguator
, resolved
: Disambiguator
| {
1175 // The resolved item did not match the disambiguator; give a better error than 'not found'
1176 let msg
= format
!("incompatible link kind for `{}`", path_str
);
1177 let callback
= |diag
: &mut DiagnosticBuilder
<'_
>, sp
: Option
<rustc_span
::Span
>| {
1179 "this link resolved to {} {}, which is not {} {}",
1182 specified
.article(),
1185 if let Some(sp
) = sp
{
1186 diag
.span_label(sp
, ¬e
);
1190 suggest_disambiguator(resolved
, diag
, path_str
, &ori_link
.link
, sp
);
1192 report_diagnostic(self.cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, callback
);
1195 let verify
= |kind
: DefKind
, id
: DefId
| {
1196 let (kind
, id
) = self.kind_side_channel
.take().unwrap_or((kind
, id
));
1197 debug
!("intra-doc link to {} resolved to {:?} (id: {:?})", path_str
, res
, id
);
1199 // Disallow e.g. linking to enums with `struct@`
1200 debug
!("saw kind {:?} with disambiguator {:?}", kind
, disambiguator
);
1201 match (kind
, disambiguator
) {
1202 | (DefKind
::Const
| DefKind
::ConstParam
| DefKind
::AssocConst
| DefKind
::AnonConst
, Some(Disambiguator
::Kind(DefKind
::Const
)))
1203 // NOTE: this allows 'method' to mean both normal functions and associated functions
1204 // This can't cause ambiguity because both are in the same namespace.
1205 | (DefKind
::Fn
| DefKind
::AssocFn
, Some(Disambiguator
::Kind(DefKind
::Fn
)))
1206 // These are namespaces; allow anything in the namespace to match
1207 | (_
, Some(Disambiguator
::Namespace(_
)))
1208 // If no disambiguator given, allow anything
1210 // All of these are valid, so do nothing
1212 (actual
, Some(Disambiguator
::Kind(expected
))) if actual
== expected
=> {}
1213 (_
, Some(specified @ Disambiguator
::Kind(_
) | specified @ Disambiguator
::Primitive
)) => {
1214 report_mismatch(specified
, Disambiguator
::Kind(kind
));
1219 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
1220 if let Some((src_id
, dst_id
)) = id
1222 // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
1223 // would presumably panic if a fake `DefIndex` were passed.
1224 .and_then(|dst_id
| {
1225 item
.def_id
.expect_def_id().as_local().map(|src_id
| (src_id
, dst_id
))
1228 if self.cx
.tcx
.privacy_access_levels(()).is_exported(src_id
)
1229 && !self.cx
.tcx
.privacy_access_levels(()).is_exported(dst_id
)
1231 privacy_error(self.cx
, &diag_info
, &path_str
);
1239 Res
::Primitive(prim
) => {
1240 if let Some((kind
, id
)) = self.kind_side_channel
.take() {
1241 // We're actually resolving an associated item of a primitive, so we need to
1242 // verify the disambiguator (if any) matches the type of the associated item.
1243 // This case should really follow the same flow as the `Res::Def` branch below,
1244 // but attempting to add a call to `clean::register_res` causes an ICE. @jyn514
1245 // thinks `register_res` is only needed for cross-crate re-exports, but Rust
1246 // doesn't allow statements like `use str::trim;`, making this a (hopefully)
1247 // valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677
1248 // for discussion on the matter.
1251 // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether.
1252 // However I'm not sure how to check that across crates.
1253 if prim
== PrimitiveType
::RawPointer
1254 && item
.def_id
.is_local()
1255 && !self.cx
.tcx
.features().intra_doc_pointers
1257 let span
= super::source_span_for_markdown_range(
1263 .unwrap_or_else(|| item
.attr_span(self.cx
.tcx
));
1265 rustc_session
::parse
::feature_err(
1266 &self.cx
.tcx
.sess
.parse_sess
,
1267 sym
::intra_doc_pointers
,
1269 "linking to associated items of raw pointers is experimental",
1271 .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does")
1275 match disambiguator
{
1276 Some(Disambiguator
::Primitive
| Disambiguator
::Namespace(_
)) | None
=> {}
1278 report_mismatch(other
, Disambiguator
::Primitive
);
1285 link
: ori_link
.link
,
1287 did
: res
.def_id(self.cx
.tcx
),
1291 Res
::Def(kind
, id
) => {
1293 let id
= clean
::register_res(self.cx
, rustc_hir
::def
::Res
::Def(kind
, id
));
1294 Some(ItemLink { link: ori_link.link, link_text, did: id, fragment }
)
1299 fn resolve_with_disambiguator_cached(
1301 key
: ResolutionInfo
,
1302 diag
: DiagnosticInfo
<'_
>,
1303 cache_resolution_failure
: bool
,
1304 ) -> Option
<(Res
, Option
<String
>)> {
1305 // Try to look up both the result and the corresponding side channel value
1306 if let Some(ref cached
) = self.visited_links
.get(&key
) {
1309 self.kind_side_channel
.set(cached
.side_channel
);
1310 return Some(cached
.res
.clone());
1312 None
if cache_resolution_failure
=> return None
,
1314 // Although we hit the cache and found a resolution error, this link isn't
1315 // supposed to cache those. Run link resolution again to emit the expected
1316 // resolution error.
1321 let res
= self.resolve_with_disambiguator(&key
, diag
);
1323 // Cache only if resolved successfully - don't silence duplicate errors
1324 if let Some(res
) = res
{
1325 // Store result for the actual namespace
1326 self.visited_links
.insert(
1330 side_channel
: self.kind_side_channel
.clone().into_inner(),
1336 if cache_resolution_failure
{
1337 // For reference-style links we only want to report one resolution error
1338 // so let's cache them as well.
1339 self.visited_links
.insert(key
, None
);
1346 /// After parsing the disambiguator, resolve the main part of the link.
1347 // FIXME(jynelson): wow this is just so much
1348 fn resolve_with_disambiguator(
1350 key
: &ResolutionInfo
,
1351 diag
: DiagnosticInfo
<'_
>,
1352 ) -> Option
<(Res
, Option
<String
>)> {
1353 let disambiguator
= key
.dis
;
1354 let path_str
= &key
.path_str
;
1355 let base_node
= key
.module_id
;
1356 let extra_fragment
= &key
.extra_fragment
;
1358 match disambiguator
.map(Disambiguator
::ns
) {
1359 Some(expected_ns @
(ValueNS
| TypeNS
)) => {
1360 match self.resolve(path_str
, expected_ns
, base_node
, extra_fragment
) {
1361 Ok(res
) => Some(res
),
1362 Err(ErrorKind
::Resolve(box mut kind
)) => {
1363 // We only looked in one namespace. Try to give a better error if possible.
1364 if kind
.full_res().is_none() {
1365 let other_ns
= if expected_ns
== ValueNS { TypeNS }
else { ValueNS }
;
1366 // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1367 // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1368 for new_ns
in [other_ns
, MacroNS
] {
1370 self.check_full_res(new_ns
, path_str
, base_node
, extra_fragment
)
1372 kind
= ResolutionFailure
::WrongNamespace { res, expected_ns }
;
1377 resolution_failure(self, diag
, path_str
, disambiguator
, smallvec
![kind
]);
1378 // This could just be a normal link or a broken link
1379 // we could potentially check if something is
1380 // "intra-doc-link-like" and warn in that case.
1383 Err(ErrorKind
::AnchorFailure(msg
)) => {
1384 anchor_failure(self.cx
, diag
, msg
);
1391 let mut candidates
= PerNS
{
1393 .resolve_macro(path_str
, base_node
)
1394 .map(|res
| (res
, extra_fragment
.clone())),
1395 type_ns
: match self.resolve(path_str
, TypeNS
, base_node
, extra_fragment
) {
1397 debug
!("got res in TypeNS: {:?}", res
);
1400 Err(ErrorKind
::AnchorFailure(msg
)) => {
1401 anchor_failure(self.cx
, diag
, msg
);
1404 Err(ErrorKind
::Resolve(box kind
)) => Err(kind
),
1406 value_ns
: match self.resolve(path_str
, ValueNS
, base_node
, extra_fragment
) {
1408 Err(ErrorKind
::AnchorFailure(msg
)) => {
1409 anchor_failure(self.cx
, diag
, msg
);
1412 Err(ErrorKind
::Resolve(box kind
)) => Err(kind
),
1414 .and_then(|(res
, fragment
)| {
1415 // Constructors are picked up in the type namespace.
1417 Res
::Def(DefKind
::Ctor(..), _
) => {
1418 Err(ResolutionFailure
::WrongNamespace { res, expected_ns: TypeNS }
)
1421 match (fragment
, extra_fragment
.clone()) {
1422 (Some(fragment
), Some(_
)) => {
1423 // Shouldn't happen but who knows?
1424 Ok((res
, Some(fragment
)))
1426 (fragment
, None
) | (None
, fragment
) => Ok((res
, fragment
)),
1433 let len
= candidates
.iter().filter(|res
| res
.is_ok()).count();
1441 candidates
.into_iter().filter_map(|res
| res
.err()).collect(),
1443 // this could just be a normal link
1448 Some(candidates
.into_iter().find_map(|res
| res
.ok()).unwrap())
1449 } else if len
== 2 && is_derive_trait_collision(&candidates
) {
1450 Some(candidates
.type_ns
.unwrap())
1452 if is_derive_trait_collision(&candidates
) {
1453 candidates
.macro_ns
= Err(ResolutionFailure
::Dummy
);
1455 // If we're reporting an ambiguity, don't mention the namespaces that failed
1456 let candidates
= candidates
.map(|candidate
| candidate
.ok().map(|(res
, _
)| res
));
1457 ambiguity_error(self.cx
, diag
, path_str
, candidates
.present_items().collect());
1462 match self.resolve_macro(path_str
, base_node
) {
1463 Ok(res
) => Some((res
, extra_fragment
.clone())),
1465 // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
1466 for ns
in [TypeNS
, ValueNS
] {
1468 self.check_full_res(ns
, path_str
, base_node
, extra_fragment
)
1471 ResolutionFailure
::WrongNamespace { res, expected_ns: MacroNS }
;
1475 resolution_failure(self, diag
, path_str
, disambiguator
, smallvec
![kind
]);
1484 /// Get the section of a link between the backticks,
1485 /// or the whole link if there aren't any backticks.
1493 fn range_between_backticks(ori_link
: &MarkdownLink
) -> Range
<usize> {
1494 let after_first_backtick_group
= ori_link
.link
.bytes().position(|b
| b
!= b'`'
).unwrap_or(0);
1495 let before_second_backtick_group
= ori_link
1498 .skip(after_first_backtick_group
)
1499 .position(|b
| b
== b'`'
)
1500 .unwrap_or(ori_link
.link
.len());
1501 (ori_link
.range
.start
+ after_first_backtick_group
)
1502 ..(ori_link
.range
.start
+ before_second_backtick_group
)
1505 /// Returns true if we should ignore `link` due to it being unlikely
1506 /// that it is an intra-doc link. `link` should still have disambiguators
1507 /// if there were any.
1509 /// The difference between this and [`should_ignore_link()`] is that this
1510 /// check should only be used on links that still have disambiguators.
1511 fn should_ignore_link_with_disambiguators(link
: &str) -> bool
{
1512 link
.contains(|ch
: char| !(ch
.is_alphanumeric() || ":_<>, !*&;@()".contains(ch
)))
1515 /// Returns true if we should ignore `path_str` due to it being unlikely
1516 /// that it is an intra-doc link.
1517 fn should_ignore_link(path_str
: &str) -> bool
{
1518 path_str
.contains(|ch
: char| !(ch
.is_alphanumeric() || ":_<>, !*&;".contains(ch
)))
1521 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1522 /// Disambiguators for a link.
1523 enum Disambiguator
{
1526 /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
1528 /// `struct@` or `f()`
1531 Namespace(Namespace
),
1534 impl Disambiguator
{
1535 /// Given a link, parse and return `(disambiguator, path_str, link_text)`.
1537 /// This returns `Ok(Some(...))` if a disambiguator was found,
1538 /// `Ok(None)` if no disambiguator was found, or `Err(...)`
1539 /// if there was a problem with the disambiguator.
1540 fn from_str(link
: &str) -> Result
<Option
<(Self, &str, &str)>, (String
, Range
<usize>)> {
1541 use Disambiguator
::{Kind, Namespace as NS, Primitive}
;
1543 if let Some(idx
) = link
.find('@'
) {
1544 let (prefix
, rest
) = link
.split_at(idx
);
1545 let d
= match prefix
{
1546 "struct" => Kind(DefKind
::Struct
),
1547 "enum" => Kind(DefKind
::Enum
),
1548 "trait" => Kind(DefKind
::Trait
),
1549 "union" => Kind(DefKind
::Union
),
1550 "module" | "mod" => Kind(DefKind
::Mod
),
1551 "const" | "constant" => Kind(DefKind
::Const
),
1552 "static" => Kind(DefKind
::Static
),
1553 "function" | "fn" | "method" => Kind(DefKind
::Fn
),
1554 "derive" => Kind(DefKind
::Macro(MacroKind
::Derive
)),
1555 "type" => NS(Namespace
::TypeNS
),
1556 "value" => NS(Namespace
::ValueNS
),
1557 "macro" => NS(Namespace
::MacroNS
),
1558 "prim" | "primitive" => Primitive
,
1559 _
=> return Err((format
!("unknown disambiguator `{}`", prefix
), 0..idx
)),
1561 Ok(Some((d
, &rest
[1..], &rest
[1..])))
1564 ("!()", DefKind
::Macro(MacroKind
::Bang
)),
1565 ("!{}", DefKind
::Macro(MacroKind
::Bang
)),
1566 ("![]", DefKind
::Macro(MacroKind
::Bang
)),
1567 ("()", DefKind
::Fn
),
1568 ("!", DefKind
::Macro(MacroKind
::Bang
)),
1570 for (suffix
, kind
) in suffixes
{
1571 if let Some(path_str
) = link
.strip_suffix(suffix
) {
1572 // Avoid turning `!` or `()` into an empty string
1573 if !path_str
.is_empty() {
1574 return Ok(Some((Kind(kind
), path_str
, link
)));
1582 fn from_res(res
: Res
) -> Self {
1584 Res
::Def(kind
, _
) => Disambiguator
::Kind(kind
),
1585 Res
::Primitive(_
) => Disambiguator
::Primitive
,
1589 /// Used for error reporting.
1590 fn suggestion(self) -> Suggestion
{
1591 let kind
= match self {
1592 Disambiguator
::Primitive
=> return Suggestion
::Prefix("prim"),
1593 Disambiguator
::Kind(kind
) => kind
,
1594 Disambiguator
::Namespace(_
) => panic
!("display_for cannot be used on namespaces"),
1596 if kind
== DefKind
::Macro(MacroKind
::Bang
) {
1597 return Suggestion
::Macro
;
1598 } else if kind
== DefKind
::Fn
|| kind
== DefKind
::AssocFn
{
1599 return Suggestion
::Function
;
1600 } else if kind
== DefKind
::Field
{
1601 return Suggestion
::RemoveDisambiguator
;
1604 let prefix
= match kind
{
1605 DefKind
::Struct
=> "struct",
1606 DefKind
::Enum
=> "enum",
1607 DefKind
::Trait
=> "trait",
1608 DefKind
::Union
=> "union",
1609 DefKind
::Mod
=> "mod",
1610 DefKind
::Const
| DefKind
::ConstParam
| DefKind
::AssocConst
| DefKind
::AnonConst
=> {
1613 DefKind
::Static
=> "static",
1614 DefKind
::Macro(MacroKind
::Derive
) => "derive",
1615 // Now handle things that don't have a specific disambiguator
1618 .expect("tried to calculate a disambiguator for a def without a namespace?")
1620 Namespace
::TypeNS
=> "type",
1621 Namespace
::ValueNS
=> "value",
1622 Namespace
::MacroNS
=> "macro",
1626 Suggestion
::Prefix(prefix
)
1629 fn ns(self) -> Namespace
{
1631 Self::Namespace(n
) => n
,
1633 k
.ns().expect("only DefKinds with a valid namespace can be disambiguators")
1635 Self::Primitive
=> TypeNS
,
1639 fn article(self) -> &'
static str {
1641 Self::Namespace(_
) => panic
!("article() doesn't make sense for namespaces"),
1642 Self::Kind(k
) => k
.article(),
1643 Self::Primitive
=> "a",
1647 fn descr(self) -> &'
static str {
1649 Self::Namespace(n
) => n
.descr(),
1650 // HACK(jynelson): by looking at the source I saw the DefId we pass
1651 // for `expected.descr()` doesn't matter, since it's not a crate
1652 Self::Kind(k
) => k
.descr(DefId
::local(hir
::def_id
::DefIndex
::from_usize(0))),
1653 Self::Primitive
=> "builtin type",
1658 /// A suggestion to show in a diagnostic.
1661 Prefix(&'
static str),
1666 /// `foo` without any disambiguator
1667 RemoveDisambiguator
,
1671 fn descr(&self) -> Cow
<'
static, str> {
1673 Self::Prefix(x
) => format
!("prefix with `{}@`", x
).into(),
1674 Self::Function
=> "add parentheses".into(),
1675 Self::Macro
=> "add an exclamation mark".into(),
1676 Self::RemoveDisambiguator
=> "remove the disambiguator".into(),
1680 fn as_help(&self, path_str
: &str) -> String
{
1681 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1683 Self::Prefix(prefix
) => format
!("{}@{}", prefix
, path_str
),
1684 Self::Function
=> format
!("{}()", path_str
),
1685 Self::Macro
=> format
!("{}!", path_str
),
1686 Self::RemoveDisambiguator
=> path_str
.into(),
1694 sp
: rustc_span
::Span
,
1695 ) -> Vec
<(rustc_span
::Span
, String
)> {
1696 let inner_sp
= match ori_link
.find('
('
) {
1697 Some(index
) => sp
.with_hi(sp
.lo() + BytePos(index
as _
)),
1700 let inner_sp
= match ori_link
.find('
!'
) {
1701 Some(index
) => inner_sp
.with_hi(inner_sp
.lo() + BytePos(index
as _
)),
1704 let inner_sp
= match ori_link
.find('@'
) {
1705 Some(index
) => inner_sp
.with_lo(inner_sp
.lo() + BytePos(index
as u32 + 1)),
1709 Self::Prefix(prefix
) => {
1710 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1711 let mut sugg
= vec
![(sp
.with_hi(inner_sp
.lo()), format
!("{}@", prefix
))];
1712 if sp
.hi() != inner_sp
.hi() {
1713 sugg
.push((inner_sp
.shrink_to_hi().with_hi(sp
.hi()), String
::new()));
1718 let mut sugg
= vec
![(inner_sp
.shrink_to_hi().with_hi(sp
.hi()), "()".to_string())];
1719 if sp
.lo() != inner_sp
.lo() {
1720 sugg
.push((inner_sp
.shrink_to_lo().with_lo(sp
.lo()), String
::new()));
1725 let mut sugg
= vec
![(inner_sp
.shrink_to_hi(), "!".to_string())];
1726 if sp
.lo() != inner_sp
.lo() {
1727 sugg
.push((inner_sp
.shrink_to_lo().with_lo(sp
.lo()), String
::new()));
1731 Self::RemoveDisambiguator
=> return vec
![(sp
, path_str
.into())],
1736 /// Reports a diagnostic for an intra-doc link.
1738 /// If no link range is provided, or the source span of the link cannot be determined, the span of
1739 /// the entire documentation block is used for the lint. If a range is provided but the span
1740 /// calculation fails, a note is added to the diagnostic pointing to the link in the markdown.
1742 /// The `decorate` callback is invoked in all cases to allow further customization of the
1743 /// diagnostic before emission. If the span of the link was able to be determined, the second
1744 /// parameter of the callback will contain it, and the primary span of the diagnostic will be set
1746 fn report_diagnostic(
1748 lint
: &'
static Lint
,
1750 DiagnosticInfo { item, ori_link: _, dox, link_range }
: &DiagnosticInfo
<'_
>,
1751 decorate
: impl FnOnce(&mut DiagnosticBuilder
<'_
>, Option
<rustc_span
::Span
>),
1753 let hir_id
= match DocContext
::as_local_hir_id(tcx
, item
.def_id
) {
1754 Some(hir_id
) => hir_id
,
1756 // If non-local, no need to check anything.
1757 info
!("ignoring warning from parent crate: {}", msg
);
1762 let sp
= item
.attr_span(tcx
);
1764 tcx
.struct_span_lint_hir(lint
, hir_id
, sp
, |lint
| {
1765 let mut diag
= lint
.build(msg
);
1768 super::source_span_for_markdown_range(tcx
, dox
, link_range
, &item
.attrs
).map(|sp
| {
1769 if dox
.bytes().nth(link_range
.start
) == Some(b'`'
)
1770 && dox
.bytes().nth(link_range
.end
- 1) == Some(b'`'
)
1772 sp
.with_lo(sp
.lo() + BytePos(1)).with_hi(sp
.hi() - BytePos(1))
1778 if let Some(sp
) = span
{
1781 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1784 // last_new_line_offset
1785 let last_new_line_offset
= dox
[..link_range
.start
].rfind('
\n'
).map_or(0, |n
| n
+ 1);
1786 let line
= dox
[last_new_line_offset
..].lines().next().unwrap_or("");
1788 // Print the line containing the `link_range` and manually mark it with '^'s.
1790 "the link appears in this line:\n\n{line}\n\
1791 {indicator: <before$}{indicator:^<found$}",
1794 before
= link_range
.start
- last_new_line_offset
,
1795 found
= link_range
.len(),
1799 decorate(&mut diag
, span
);
1805 /// Reports a link that failed to resolve.
1807 /// This also tries to resolve any intermediate path segments that weren't
1808 /// handled earlier. For example, if passed `Item::Crate(std)` and `path_str`
1809 /// `std::io::Error::x`, this will resolve `std::io::Error`.
1810 fn resolution_failure(
1811 collector
: &mut LinkCollector
<'_
, '_
>,
1812 diag_info
: DiagnosticInfo
<'_
>,
1814 disambiguator
: Option
<Disambiguator
>,
1815 kinds
: SmallVec
<[ResolutionFailure
<'_
>; 3]>,
1817 let tcx
= collector
.cx
.tcx
;
1820 BROKEN_INTRA_DOC_LINKS
,
1821 &format
!("unresolved link to `{}`", path_str
),
1824 let item
= |res
: Res
| format
!("the {} `{}`", res
.descr(), res
.name(tcx
),);
1825 let assoc_item_not_allowed
= |res
: Res
| {
1826 let name
= res
.name(tcx
);
1828 "`{}` is {} {}, not a module or type, and cannot have associated items",
1834 // ignore duplicates
1835 let mut variants_seen
= SmallVec
::<[_
; 3]>::new();
1836 for mut failure
in kinds
{
1837 let variant
= std
::mem
::discriminant(&failure
);
1838 if variants_seen
.contains(&variant
) {
1841 variants_seen
.push(variant
);
1843 if let ResolutionFailure
::NotResolved { module_id, partial_res, unresolved }
=
1848 let module_id
= *module_id
;
1849 // FIXME(jynelson): this might conflict with my `Self` fix in #76467
1850 // FIXME: maybe use itertools `collect_tuple` instead?
1851 fn split(path
: &str) -> Option
<(&str, &str)> {
1852 let mut splitter
= path
.rsplitn(2, "::");
1853 splitter
.next().and_then(|right
| splitter
.next().map(|left
| (left
, right
)))
1856 // Check if _any_ parent of the path gets resolved.
1857 // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1858 let mut name
= path_str
;
1860 let (start
, end
) = if let Some(x
) = split(name
) {
1863 // avoid bug that marked [Quux::Z] as missing Z, not Quux
1864 if partial_res
.is_none() {
1865 *unresolved
= name
.into();
1870 for ns
in [TypeNS
, ValueNS
, MacroNS
] {
1872 collector
.check_full_res(ns
, &start
, module_id
, &None
)
1874 debug
!("found partial_res={:?}", res
);
1875 *partial_res
= Some(res
);
1876 *unresolved
= end
.into();
1880 *unresolved
= end
.into();
1883 let last_found_module
= match *partial_res
{
1884 Some(Res
::Def(DefKind
::Mod
, id
)) => Some(id
),
1885 None
=> Some(module_id
),
1888 // See if this was a module: `[path]` or `[std::io::nope]`
1889 if let Some(module
) = last_found_module
{
1890 let note
= if partial_res
.is_some() {
1891 // Part of the link resolved; e.g. `std::io::nonexistent`
1892 let module_name
= tcx
.item_name(module
);
1893 format
!("no item named `{}` in module `{}`", unresolved
, module_name
)
1895 // None of the link resolved; e.g. `Notimported`
1896 format
!("no item named `{}` in scope", unresolved
)
1898 if let Some(span
) = sp
{
1899 diag
.span_label(span
, ¬e
);
1904 // If the link has `::` in it, assume it was meant to be an intra-doc link.
1905 // Otherwise, the `[]` might be unrelated.
1906 // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
1907 if !path_str
.contains("::") {
1908 diag
.help(r
#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1914 // Otherwise, it must be an associated item or variant
1915 let res
= partial_res
.expect("None case was handled by `last_found_module`");
1916 let name
= res
.name(tcx
);
1917 let kind
= match res
{
1918 Res
::Def(kind
, _
) => Some(kind
),
1919 Res
::Primitive(_
) => None
,
1921 let path_description
= if let Some(kind
) = kind
{
1923 Mod
| ForeignMod
=> "inner item",
1924 Struct
=> "field or associated item",
1925 Enum
| Union
=> "variant or associated item",
1942 let note
= assoc_item_not_allowed(res
);
1943 if let Some(span
) = sp
{
1944 diag
.span_label(span
, ¬e
);
1950 Trait
| TyAlias
| ForeignTy
| OpaqueTy
| TraitAlias
| TyParam
1951 | Static
=> "associated item",
1952 Impl
| GlobalAsm
=> unreachable
!("not a path"),
1958 "the {} `{}` has no {} named `{}`",
1961 disambiguator
.map_or(path_description
, |d
| d
.descr()),
1964 if let Some(span
) = sp
{
1965 diag
.span_label(span
, ¬e
);
1972 let note
= match failure
{
1973 ResolutionFailure
::NotResolved { .. }
=> unreachable
!("handled above"),
1974 ResolutionFailure
::Dummy
=> continue,
1975 ResolutionFailure
::WrongNamespace { res, expected_ns }
=> {
1976 if let Res
::Def(kind
, _
) = res
{
1977 let disambiguator
= Disambiguator
::Kind(kind
);
1978 suggest_disambiguator(
1988 "this link resolves to {}, which is not in the {} namespace",
1993 ResolutionFailure
::NoParentItem
=> {
1994 diag
.level
= rustc_errors
::Level
::Bug
;
1995 "all intra-doc links should have a parent item".to_owned()
1997 ResolutionFailure
::MalformedGenerics(variant
) => match variant
{
1998 MalformedGenerics
::UnbalancedAngleBrackets
=> {
1999 String
::from("unbalanced angle brackets")
2001 MalformedGenerics
::MissingType
=> {
2002 String
::from("missing type for generic parameters")
2004 MalformedGenerics
::HasFullyQualifiedSyntax
=> {
2005 diag
.note("see https://github.com/rust-lang/rust/issues/74563 for more information");
2006 String
::from("fully-qualified syntax is unsupported")
2008 MalformedGenerics
::InvalidPathSeparator
=> {
2009 String
::from("has invalid path separator")
2011 MalformedGenerics
::TooManyAngleBrackets
=> {
2012 String
::from("too many angle brackets")
2014 MalformedGenerics
::EmptyAngleBrackets
=> {
2015 String
::from("empty angle brackets")
2019 if let Some(span
) = sp
{
2020 diag
.span_label(span
, ¬e
);
2029 /// Report an anchor failure.
2030 fn anchor_failure(cx
: &DocContext
<'_
>, diag_info
: DiagnosticInfo
<'_
>, failure
: AnchorFailure
) {
2031 let (msg
, anchor_idx
) = match failure
{
2032 AnchorFailure
::MultipleAnchors
=> {
2033 (format
!("`{}` contains multiple anchors", diag_info
.ori_link
), 1)
2035 AnchorFailure
::RustdocAnchorConflict(res
) => (
2037 "`{}` contains an anchor, but links to {kind}s are already anchored",
2045 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, |diag
, sp
| {
2046 if let Some(mut sp
) = sp
{
2047 if let Some((fragment_offset
, _
)) =
2048 diag_info
.ori_link
.char_indices().filter(|(_
, x
)| *x
== '
#').nth(anchor_idx)
2050 sp
= sp
.with_lo(sp
.lo() + BytePos(fragment_offset
as _
));
2052 diag
.span_label(sp
, "invalid anchor");
2054 if let AnchorFailure
::RustdocAnchorConflict(Res
::Primitive(_
)) = failure
{
2055 if let Some(sp
) = sp
{
2056 span_bug
!(sp
, "anchors should be allowed now");
2058 bug
!("anchors should be allowed now");
2064 /// Report an error in the link disambiguator.
2065 fn disambiguator_error(
2066 cx
: &DocContext
<'_
>,
2067 mut diag_info
: DiagnosticInfo
<'_
>,
2068 disambiguator_range
: Range
<usize>,
2071 diag_info
.link_range
= disambiguator_range
;
2072 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, msg
, &diag_info
, |diag
, _sp
| {
2074 "see {}/rustdoc/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators",
2075 crate::DOC_RUST_LANG_ORG_CHANNEL
2081 /// Report an ambiguity error, where there were multiple possible resolutions.
2083 cx
: &DocContext
<'_
>,
2084 diag_info
: DiagnosticInfo
<'_
>,
2086 candidates
: Vec
<Res
>,
2088 let mut msg
= format
!("`{}` is ", path_str
);
2090 match candidates
.as_slice() {
2091 [first_def
, second_def
] => {
2093 "both {} {} and {} {}",
2094 first_def
.article(),
2096 second_def
.article(),
2101 let mut candidates
= candidates
.iter().peekable();
2102 while let Some(res
) = candidates
.next() {
2103 if candidates
.peek().is_some() {
2104 msg
+= &format
!("{} {}, ", res
.article(), res
.descr());
2106 msg
+= &format
!("and {} {}", res
.article(), res
.descr());
2112 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, |diag
, sp
| {
2113 if let Some(sp
) = sp
{
2114 diag
.span_label(sp
, "ambiguous link");
2116 diag
.note("ambiguous link");
2119 for res
in candidates
{
2120 let disambiguator
= Disambiguator
::from_res(res
);
2121 suggest_disambiguator(disambiguator
, diag
, path_str
, diag_info
.ori_link
, sp
);
2126 /// In case of an ambiguity or mismatched disambiguator, suggest the correct
2128 fn suggest_disambiguator(
2129 disambiguator
: Disambiguator
,
2130 diag
: &mut DiagnosticBuilder
<'_
>,
2133 sp
: Option
<rustc_span
::Span
>,
2135 let suggestion
= disambiguator
.suggestion();
2136 let help
= format
!("to link to the {}, {}", disambiguator
.descr(), suggestion
.descr());
2138 if let Some(sp
) = sp
{
2139 let mut spans
= suggestion
.as_help_span(path_str
, ori_link
, sp
);
2140 if spans
.len() > 1 {
2141 diag
.multipart_suggestion(&help
, spans
, Applicability
::MaybeIncorrect
);
2143 let (sp
, suggestion_text
) = spans
.pop().unwrap();
2144 diag
.span_suggestion_verbose(sp
, &help
, suggestion_text
, Applicability
::MaybeIncorrect
);
2147 diag
.help(&format
!("{}: {}", help
, suggestion
.as_help(path_str
)));
2151 /// Report a link from a public item to a private one.
2152 fn privacy_error(cx
: &DocContext
<'_
>, diag_info
: &DiagnosticInfo
<'_
>, path_str
: &str) {
2154 let item_name
= match diag_info
.item
.name
{
2156 sym
= name
.as_str();
2159 None
=> "<unknown>",
2162 format
!("public documentation for `{}` links to private item `{}`", item_name
, path_str
);
2164 report_diagnostic(cx
.tcx
, PRIVATE_INTRA_DOC_LINKS
, &msg
, diag_info
, |diag
, sp
| {
2165 if let Some(sp
) = sp
{
2166 diag
.span_label(sp
, "this item is private");
2169 let note_msg
= if cx
.render_options
.document_private
{
2170 "this link resolves only because you passed `--document-private-items`, but will break without"
2172 "this link will resolve properly if you pass `--document-private-items`"
2174 diag
.note(note_msg
);
2178 /// Given an enum variant's res, return the res of its enum and the associated fragment.
2180 cx
: &DocContext
<'_
>,
2182 extra_fragment
: &Option
<String
>,
2183 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'
static>> {
2184 use rustc_middle
::ty
::DefIdTree
;
2186 if extra_fragment
.is_some() {
2187 // NOTE: `res` can never be a primitive since this function is only called when `tcx.def_kind(res) == DefKind::Variant`.
2188 return Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(res
)));
2191 .parent(res
.def_id(cx
.tcx
))
2193 let parent_def
= Res
::Def(DefKind
::Enum
, parent
);
2194 let variant
= cx
.tcx
.expect_variant_res(res
.as_hir_res().unwrap());
2195 (parent_def
, Some(format
!("variant.{}", variant
.ident
.name
)))
2197 .ok_or_else(|| ResolutionFailure
::NoParentItem
.into())
2200 /// Resolve a primitive type or value.
2201 fn resolve_primitive(path_str
: &str, ns
: Namespace
) -> Option
<Res
> {
2205 use PrimitiveType
::*;
2206 let prim
= match path_str
{
2222 "bool" | "true" | "false" => Bool
,
2223 "str" | "&str" => Str
,
2224 // See #80181 for why these don't have symbols associated.
2229 "pointer" | "*const" | "*mut" => RawPointer
,
2230 "reference" | "&" | "&mut" => Reference
,
2232 "never" | "!" => Never
,
2235 debug
!("resolved primitives {:?}", prim
);
2236 Some(Res
::Primitive(prim
))
2239 fn strip_generics_from_path(path_str
: &str) -> Result
<String
, ResolutionFailure
<'
static>> {
2240 let mut stripped_segments
= vec
![];
2241 let mut path
= path_str
.chars().peekable();
2242 let mut segment
= Vec
::new();
2244 while let Some(chr
) = path
.next() {
2247 if path
.next_if_eq(&'
:'
).is_some() {
2248 let stripped_segment
=
2249 strip_generics_from_path_segment(mem
::take(&mut segment
))?
;
2250 if !stripped_segment
.is_empty() {
2251 stripped_segments
.push(stripped_segment
);
2254 return Err(ResolutionFailure
::MalformedGenerics(
2255 MalformedGenerics
::InvalidPathSeparator
,
2264 return Err(ResolutionFailure
::MalformedGenerics(
2265 MalformedGenerics
::TooManyAngleBrackets
,
2269 return Err(ResolutionFailure
::MalformedGenerics(
2270 MalformedGenerics
::EmptyAngleBrackets
,
2276 while let Some(chr
) = path
.next_if(|c
| *c
!= '
>'
) {
2283 _
=> segment
.push(chr
),
2285 trace
!("raw segment: {:?}", segment
);
2288 if !segment
.is_empty() {
2289 let stripped_segment
= strip_generics_from_path_segment(segment
)?
;
2290 if !stripped_segment
.is_empty() {
2291 stripped_segments
.push(stripped_segment
);
2295 debug
!("path_str: {:?}\nstripped segments: {:?}", path_str
, &stripped_segments
);
2297 let stripped_path
= stripped_segments
.join("::");
2299 if !stripped_path
.is_empty() {
2302 Err(ResolutionFailure
::MalformedGenerics(MalformedGenerics
::MissingType
))
2306 fn strip_generics_from_path_segment(
2308 ) -> Result
<String
, ResolutionFailure
<'
static>> {
2309 let mut stripped_segment
= String
::new();
2310 let mut param_depth
= 0;
2312 let mut latest_generics_chunk
= String
::new();
2317 latest_generics_chunk
.clear();
2318 } else if c
== '
>'
{
2320 if latest_generics_chunk
.contains(" as ") {
2321 // The segment tries to use fully-qualified syntax, which is currently unsupported.
2322 // Give a helpful error message instead of completely ignoring the angle brackets.
2323 return Err(ResolutionFailure
::MalformedGenerics(
2324 MalformedGenerics
::HasFullyQualifiedSyntax
,
2328 if param_depth
== 0 {
2329 stripped_segment
.push(c
);
2331 latest_generics_chunk
.push(c
);
2336 if param_depth
== 0 {
2337 Ok(stripped_segment
)
2339 // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
2340 Err(ResolutionFailure
::MalformedGenerics(MalformedGenerics
::UnbalancedAngleBrackets
))