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, 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
::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
::IntraLinkCrateLoader
;
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
<'_
>) -> String
{
96 Res
::Def(_
, id
) => tcx
.item_name(id
).to_string(),
97 Res
::Primitive(prim
) => prim
.as_str().to_string(),
101 fn def_id(self) -> DefId
{
102 self.opt_def_id().expect("called def_id() on a primitive")
105 fn opt_def_id(self) -> Option
<DefId
> {
107 Res
::Def(_
, id
) => Some(id
),
108 Res
::Primitive(_
) => None
,
112 fn as_hir_res(self) -> Option
<rustc_hir
::def
::Res
> {
114 Res
::Def(kind
, id
) => Some(rustc_hir
::def
::Res
::Def(kind
, id
)),
115 // FIXME: maybe this should handle the subset of PrimitiveType that fits into hir::PrimTy?
116 Res
::Primitive(_
) => None
,
121 impl TryFrom
<ResolveRes
> for Res
{
124 fn try_from(res
: ResolveRes
) -> Result
<Self, ()> {
125 use rustc_hir
::def
::Res
::*;
127 Def(kind
, id
) => Ok(Res
::Def(kind
, id
)),
128 PrimTy(prim
) => Ok(Res
::Primitive(PrimitiveType
::from_hir(prim
))),
130 NonMacroAttr(..) | Err
=> Result
::Err(()),
131 other
=> bug
!("unrecognized res {:?}", other
),
136 /// A link failed to resolve.
138 enum ResolutionFailure
<'a
> {
139 /// This resolved, but with the wrong namespace.
141 /// What the link resolved to.
143 /// The expected namespace for the resolution, determined from the link's disambiguator.
145 /// E.g., for `[fn@Result]` this is [`Namespace::ValueNS`],
146 /// even though `Result`'s actual namespace is [`Namespace::TypeNS`].
147 expected_ns
: Namespace
,
149 /// The link failed to resolve. [`resolution_failure`] should look to see if there's
150 /// a more helpful error that can be given.
152 /// The scope the link was resolved in.
154 /// If part of the link resolved, this has the `Res`.
156 /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution.
157 partial_res
: Option
<Res
>,
158 /// The remaining unresolved path segments.
160 /// In `[std::io::Error::x]`, `x` would be unresolved.
161 unresolved
: Cow
<'a
, str>,
163 /// This happens when rustdoc can't determine the parent scope for an item.
164 /// It is always a bug in rustdoc.
166 /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced.
167 MalformedGenerics(MalformedGenerics
),
168 /// Used to communicate that this should be ignored, but shouldn't be reported to the user.
170 /// This happens when there is no disambiguator and one of the namespaces
171 /// failed to resolve.
176 enum MalformedGenerics
{
177 /// This link has unbalanced angle brackets.
179 /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
180 UnbalancedAngleBrackets
,
181 /// The generics are not attached to a type.
183 /// For example, `<T>` should trigger this.
185 /// This is detected by checking if the path is empty after the generics are stripped.
187 /// The link uses fully-qualified syntax, which is currently unsupported.
189 /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
191 /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
193 HasFullyQualifiedSyntax
,
194 /// The link has an invalid path separator.
196 /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not**
197 /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be
200 /// Note that this will also **not** be triggered if the invalid path separator is inside angle
201 /// brackets because rustdoc mostly ignores what's inside angle brackets (except for
202 /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)).
204 /// This is detected by checking if there is a colon followed by a non-colon in the link.
205 InvalidPathSeparator
,
206 /// The link has too many angle brackets.
208 /// For example, `Vec<<T>>` should trigger this.
209 TooManyAngleBrackets
,
210 /// The link has empty angle brackets.
212 /// For example, `Vec<>` should trigger this.
216 impl ResolutionFailure
<'a
> {
217 /// This resolved fully (not just partially) but is erroneous for some other reason
219 /// Returns the full resolution of the link, if present.
220 fn full_res(&self) -> Option
<Res
> {
222 Self::WrongNamespace { res, expected_ns: _ }
=> Some(*res
),
229 /// User error: `[std#x#y]` is not valid
231 /// The anchor provided by the user conflicts with Rustdoc's generated anchor.
233 /// This is an unfortunate state of affairs. Not every item that can be
234 /// linked to has its own page; sometimes it is a subheading within a page,
235 /// like for associated items. In those cases, rustdoc uses an anchor to
236 /// link to the subheading. Since you can't have two anchors for the same
237 /// link, Rustdoc disallows having a user-specified anchor.
239 /// Most of the time this is fine, because you can just link to the page of
240 /// the item if you want to provide your own anchor. For primitives, though,
241 /// rustdoc uses the anchor as a side channel to know which page to link to;
242 /// it doesn't show up in the generated link. Ideally, rustdoc would remove
243 /// this limitation, allowing you to link to subheaders on primitives.
244 RustdocAnchorConflict(Res
),
247 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
248 struct ResolutionInfo
{
250 dis
: Option
<Disambiguator
>,
252 extra_fragment
: Option
<String
>,
256 struct DiagnosticInfo
<'a
> {
260 link_range
: Range
<usize>,
263 #[derive(Clone, Debug, Hash)]
265 pub res
: (Res
, Option
<String
>),
266 pub side_channel
: Option
<(DefKind
, DefId
)>,
269 struct LinkCollector
<'a
, 'tcx
> {
270 cx
: &'a
mut DocContext
<'tcx
>,
271 /// A stack of modules used to decide what scope to resolve in.
273 /// The last module will be used if the parent scope of the current item is
276 /// This is used to store the kind of associated items,
277 /// because `clean` and the disambiguator code expect them to be different.
278 /// See the code for associated items on inherent impls for details.
279 kind_side_channel
: Cell
<Option
<(DefKind
, DefId
)>>,
280 /// Cache the resolved links so we can avoid resolving (and emitting errors for) the same link.
281 /// The link will be `None` if it could not be resolved (i.e. the error was cached).
282 visited_links
: FxHashMap
<ResolutionInfo
, Option
<CachedLink
>>,
285 impl<'a
, 'tcx
> LinkCollector
<'a
, 'tcx
> {
286 /// Given a full link, parse it as an [enum struct variant].
288 /// In particular, this will return an error whenever there aren't three
289 /// full path segments left in the link.
291 /// [enum struct variant]: hir::VariantData::Struct
294 path_str
: &'path
str,
296 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'path
>> {
297 let tcx
= self.cx
.tcx
;
298 let no_res
= || ResolutionFailure
::NotResolved
{
301 unresolved
: path_str
.into(),
304 debug
!("looking for enum variant {}", path_str
);
305 let mut split
= path_str
.rsplitn(3, "::");
306 let (variant_field_str
, variant_field_name
) = split
308 .map(|f
| (f
, Symbol
::intern(f
)))
309 .expect("fold_item should ensure link is non-empty");
310 let (variant_str
, variant_name
) =
311 // we're not sure this is a variant at all, so use the full string
312 // If there's no second component, the link looks like `[path]`.
313 // So there's no partial res and we should say the whole link failed to resolve.
314 split
.next().map(|f
| (f
, Symbol
::intern(f
))).ok_or_else(no_res
)?
;
317 .map(|f
| f
.to_owned())
318 // If there's no third component, we saw `[a::b]` before and it failed to resolve.
319 // So there's no partial res.
320 .ok_or_else(no_res
)?
;
323 .enter_resolver(|resolver
| {
324 resolver
.resolve_str_path_error(DUMMY_SP
, &path
, TypeNS
, module_id
)
326 .and_then(|(_
, res
)| res
.try_into())
327 .map_err(|()| no_res())?
;
330 Res
::Def(DefKind
::Enum
, did
) => {
334 .flat_map(|imp
| tcx
.associated_items(*imp
).in_definition_order())
335 .any(|item
| item
.ident
.name
== variant_name
)
337 // This is just to let `fold_item` know that this shouldn't be considered;
338 // it's a bug for the error to make it to the user
339 return Err(ResolutionFailure
::Dummy
.into());
341 match tcx
.type_of(did
).kind() {
342 ty
::Adt(def
, _
) if def
.is_enum() => {
343 if def
.all_fields().any(|item
| item
.ident
.name
== variant_field_name
) {
347 "variant.{}.field.{}",
348 variant_str
, variant_field_name
352 Err(ResolutionFailure
::NotResolved
{
354 partial_res
: Some(Res
::Def(DefKind
::Enum
, def
.did
)),
355 unresolved
: variant_field_str
.into(),
363 _
=> Err(ResolutionFailure
::NotResolved
{
365 partial_res
: Some(ty_res
),
366 unresolved
: variant_str
.into(),
372 /// Given a primitive type, try to resolve an associated item.
373 fn resolve_primitive_associated_item(
375 prim_ty
: PrimitiveType
,
378 ) -> Option
<(Res
, String
, Option
<(DefKind
, DefId
)>)> {
379 let tcx
= self.cx
.tcx
;
381 prim_ty
.impls(tcx
).into_iter().find_map(|&impl_
| {
382 tcx
.associated_items(impl_
)
383 .find_by_name_and_namespace(tcx
, Ident
::with_dummy_span(item_name
), ns
, impl_
)
385 let kind
= item
.kind
;
386 let out
= match kind
{
387 ty
::AssocKind
::Fn
=> "method",
388 ty
::AssocKind
::Const
=> "associatedconstant",
389 ty
::AssocKind
::Type
=> "associatedtype",
391 let fragment
= format
!("{}#{}.{}", prim_ty
.as_str(), out
, item_name
);
392 (Res
::Primitive(prim_ty
), fragment
, Some((kind
.as_def_kind(), item
.def_id
)))
397 /// Resolves a string as a macro.
399 /// FIXME(jynelson): Can this be unified with `resolve()`?
404 ) -> Result
<Res
, ResolutionFailure
<'a
>> {
405 let path
= ast
::Path
::from_ident(Ident
::from_str(path_str
));
406 self.cx
.enter_resolver(|resolver
| {
407 // FIXME(jynelson): does this really need 3 separate lookups?
408 if let Ok((Some(ext
), res
)) = resolver
.resolve_macro_path(
411 &ParentScope
::module(resolver
.graph_root(), resolver
),
415 if let SyntaxExtensionKind
::LegacyBang { .. }
= ext
.kind
{
416 return Ok(res
.try_into().unwrap());
419 if let Some(&res
) = resolver
.all_macros().get(&Symbol
::intern(path_str
)) {
420 return Ok(res
.try_into().unwrap());
422 debug
!("resolving {} as a macro in the module {:?}", path_str
, module_id
);
423 if let Ok((_
, res
)) =
424 resolver
.resolve_str_path_error(DUMMY_SP
, path_str
, MacroNS
, module_id
)
426 // don't resolve builtins like `#[derive]`
427 if let Ok(res
) = res
.try_into() {
431 Err(ResolutionFailure
::NotResolved
{
434 unresolved
: path_str
.into(),
439 /// Convenience wrapper around `resolve_str_path_error`.
441 /// This also handles resolving `true` and `false` as booleans.
442 /// NOTE: `resolve_str_path_error` knows only about paths, not about types.
443 /// Associated items will never be resolved by this function.
444 fn resolve_path(&self, path_str
: &str, ns
: Namespace
, module_id
: DefId
) -> Option
<Res
> {
445 let result
= self.cx
.enter_resolver(|resolver
| {
447 .resolve_str_path_error(DUMMY_SP
, &path_str
, ns
, module_id
)
448 .and_then(|(_
, res
)| res
.try_into())
450 debug
!("{} resolved to {:?} in namespace {:?}", path_str
, result
, ns
);
452 // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
454 Err(()) => resolve_primitive(path_str
, ns
),
455 Ok(res
) => Some(res
),
459 /// Resolves a string as a path within a particular namespace. Returns an
460 /// optional URL fragment in the case of variants and methods.
463 path_str
: &'path
str,
466 extra_fragment
: &Option
<String
>,
467 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'path
>> {
468 if let Some(res
) = self.resolve_path(path_str
, ns
, module_id
) {
470 // FIXME(#76467): make this fallthrough to lookup the associated
471 // item a separate function.
472 Res
::Def(DefKind
::AssocFn
| DefKind
::AssocConst
, _
) => assert_eq
!(ns
, ValueNS
),
473 Res
::Def(DefKind
::AssocTy
, _
) => assert_eq
!(ns
, TypeNS
),
474 Res
::Def(DefKind
::Variant
, _
) => {
475 return handle_variant(self.cx
, res
, extra_fragment
);
477 // Not a trait item; just return what we found.
478 Res
::Primitive(ty
) => {
479 if extra_fragment
.is_some() {
480 return Err(ErrorKind
::AnchorFailure(
481 AnchorFailure
::RustdocAnchorConflict(res
),
484 return Ok((res
, Some(ty
.as_str().to_owned())));
486 _
=> return Ok((res
, extra_fragment
.clone())),
490 // Try looking for methods and associated items.
491 let mut split
= path_str
.rsplitn(2, "::");
492 // NB: `split`'s first element is always defined, even if the delimiter was not present.
493 // NB: `item_str` could be empty when resolving in the root namespace (e.g. `::std`).
494 let item_str
= split
.next().unwrap();
495 let item_name
= Symbol
::intern(item_str
);
496 let path_root
= split
498 .map(|f
| f
.to_owned())
499 // If there's no `::`, it's not an associated item.
500 // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
502 debug
!("found no `::`, assumming {} was correctly not in scope", item_name
);
503 ResolutionFailure
::NotResolved
{
506 unresolved
: item_str
.into(),
510 // FIXME(#83862): this arbitrarily gives precedence to primitives over modules to support
511 // links to primitives when `#[doc(primitive)]` is present. It should give an ambiguity
512 // error instead and special case *only* modules with `#[doc(primitive)]`, not all
514 resolve_primitive(&path_root
, TypeNS
)
515 .or_else(|| self.resolve_path(&path_root
, TypeNS
, module_id
))
517 let (res
, fragment
, side_channel
) =
518 self.resolve_associated_item(ty_res
, item_name
, ns
, module_id
)?
;
519 let result
= if extra_fragment
.is_some() {
520 let diag_res
= side_channel
.map_or(res
, |(k
, r
)| Res
::Def(k
, r
));
521 Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(diag_res
)))
523 // HACK(jynelson): `clean` expects the type, not the associated item
524 // but the disambiguator logic expects the associated item.
525 // Store the kind in a side channel so that only the disambiguator logic looks at it.
526 if let Some((kind
, id
)) = side_channel
{
527 self.kind_side_channel
.set(Some((kind
, id
)));
529 Ok((res
, Some(fragment
)))
534 if ns
== Namespace
::ValueNS
{
535 self.variant_field(path_str
, module_id
)
537 Err(ResolutionFailure
::NotResolved
{
540 unresolved
: path_root
.into(),
548 /// - None if no associated item was found
549 /// - Some((_, _, Some(_))) if an item was found and should go through a side channel
550 /// - Some((_, _, None)) otherwise
551 fn resolve_associated_item(
557 ) -> Option
<(Res
, String
, Option
<(DefKind
, DefId
)>)> {
558 let tcx
= self.cx
.tcx
;
561 Res
::Primitive(prim
) => self.resolve_primitive_associated_item(prim
, ns
, item_name
),
567 | DefKind
::ForeignTy
,
570 debug
!("looking for associated item named {} for item {:?}", item_name
, did
);
571 // Checks if item_name belongs to `impl SomeItem`
576 tcx
.associated_items(imp
).find_by_name_and_namespace(
578 Ident
::with_dummy_span(item_name
),
583 .map(|item
| (item
.kind
, item
.def_id
))
584 // There should only ever be one associated item that matches from any inherent impl
586 // Check if item_name belongs to `impl SomeTrait for SomeItem`
587 // FIXME(#74563): This gives precedence to `impl SomeItem`:
588 // Although having both would be ambiguous, use impl version for compatibility's sake.
589 // To handle that properly resolve() would have to support
590 // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
593 resolve_associated_trait_item(did
, module_id
, item_name
, ns
, self.cx
);
594 debug
!("got associated item kind {:?}", kind
);
598 if let Some((kind
, id
)) = assoc_item
{
599 let out
= match kind
{
600 ty
::AssocKind
::Fn
=> "method",
601 ty
::AssocKind
::Const
=> "associatedconstant",
602 ty
::AssocKind
::Type
=> "associatedtype",
604 // HACK(jynelson): `clean` expects the type, not the associated item
605 // but the disambiguator logic expects the associated item.
606 // Store the kind in a side channel so that only the disambiguator logic looks at it.
609 format
!("{}.{}", out
, item_name
),
610 Some((kind
.as_def_kind(), id
)),
614 if ns
!= Namespace
::ValueNS
{
617 debug
!("looking for variants or fields named {} for {:?}", item_name
, did
);
618 // FIXME: this doesn't really belong in `associated_item` (maybe `variant_field` is better?)
619 // NOTE: it's different from variant_field because it resolves fields and variants,
620 // not variant fields (2 path segments, not 3).
621 let def
= match tcx
.type_of(did
).kind() {
622 ty
::Adt(def
, _
) => def
,
625 let field
= if def
.is_enum() {
626 def
.all_fields().find(|item
| item
.ident
.name
== item_name
)
628 def
.non_enum_variant().fields
.iter().find(|item
| item
.ident
.name
== item_name
)
630 let kind
= if def
.is_enum() { DefKind::Variant }
else { DefKind::Field }
;
635 if def
.is_enum() { "variant" }
else { "structfield" }
,
638 Some((kind
, field
.did
)),
641 Res
::Def(DefKind
::Trait
, did
) => tcx
642 .associated_items(did
)
643 .find_by_name_and_namespace(tcx
, Ident
::with_dummy_span(item_name
), ns
, did
)
645 let kind
= match item
.kind
{
646 ty
::AssocKind
::Const
=> "associatedconstant",
647 ty
::AssocKind
::Type
=> "associatedtype",
648 ty
::AssocKind
::Fn
=> {
649 if item
.defaultness
.has_value() {
657 let res
= Res
::Def(item
.kind
.as_def_kind(), item
.def_id
);
658 (res
, format
!("{}.{}", kind
, item_name
), None
)
664 /// Used for reporting better errors.
666 /// Returns whether the link resolved 'fully' in another namespace.
667 /// 'fully' here means that all parts of the link resolved, not just some path segments.
668 /// This returns the `Res` even if it was erroneous for some reason
669 /// (such as having invalid URL fragments or being in the wrong namespace).
675 extra_fragment
: &Option
<String
>,
677 // resolve() can't be used for macro namespace
678 let result
= match ns
{
679 Namespace
::MacroNS
=> self.resolve_macro(path_str
, module_id
).map_err(ErrorKind
::from
),
680 Namespace
::TypeNS
| Namespace
::ValueNS
=> {
681 self.resolve(path_str
, ns
, module_id
, extra_fragment
).map(|(res
, _
)| res
)
685 let res
= match result
{
686 Ok(res
) => Some(res
),
687 Err(ErrorKind
::Resolve(box kind
)) => kind
.full_res(),
688 Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(res
))) => Some(res
),
689 Err(ErrorKind
::AnchorFailure(AnchorFailure
::MultipleAnchors
)) => None
,
691 self.kind_side_channel
.take().map(|(kind
, id
)| Res
::Def(kind
, id
)).or(res
)
695 /// Look to see if a resolved item has an associated item named `item_name`.
697 /// Given `[std::io::Error::source]`, where `source` is unresolved, this would
698 /// find `std::error::Error::source` and return
699 /// `<io::Error as error::Error>::source`.
700 fn resolve_associated_trait_item(
705 cx
: &mut DocContext
<'_
>,
706 ) -> Option
<(ty
::AssocKind
, DefId
)> {
707 // FIXME: this should also consider blanket impls (`impl<T> X for T`). Unfortunately
708 // `get_auto_trait_and_blanket_impls` is broken because the caching behavior is wrong. In the
709 // meantime, just don't look for these blanket impls.
711 // Next consider explicit impls: `impl MyTrait for MyType`
712 // Give precedence to inherent impls.
713 let traits
= traits_implemented_by(cx
, did
, module
);
714 debug
!("considering traits {:?}", traits
);
715 let mut candidates
= traits
.iter().filter_map(|&trait_
| {
717 .associated_items(trait_
)
718 .find_by_name_and_namespace(cx
.tcx
, Ident
::with_dummy_span(item_name
), ns
, trait_
)
719 .map(|assoc
| (assoc
.kind
, assoc
.def_id
))
721 // FIXME(#74563): warn about ambiguity
722 debug
!("the candidates were {:?}", candidates
.clone().collect
::<Vec
<_
>>());
726 /// Given a type, return all traits in scope in `module` implemented by that type.
728 /// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
729 /// So it is not stable to serialize cross-crate.
730 fn traits_implemented_by(cx
: &mut DocContext
<'_
>, type_
: DefId
, module
: DefId
) -> FxHashSet
<DefId
> {
731 let mut resolver
= cx
.resolver
.borrow_mut();
732 let in_scope_traits
= cx
.module_trait_cache
.entry(module
).or_insert_with(|| {
733 resolver
.access(|resolver
| {
734 let parent_scope
= &ParentScope
::module(resolver
.get_module(module
), resolver
);
736 .traits_in_scope(None
, parent_scope
, SyntaxContext
::root(), None
)
738 .map(|candidate
| candidate
.def_id
)
744 let ty
= tcx
.type_of(type_
);
745 let iter
= in_scope_traits
.iter().flat_map(|&trait_
| {
746 trace
!("considering explicit impl for trait {:?}", trait_
);
748 // Look at each trait implementation to see if it's an impl for `did`
749 tcx
.find_map_relevant_impl(trait_
, ty
, |impl_
| {
750 let trait_ref
= tcx
.impl_trait_ref(impl_
).expect("this is not an inherent impl");
751 // Check if these are the same type.
752 let impl_type
= trait_ref
.self_ty();
754 "comparing type {} with kind {:?} against type {:?}",
759 // Fast path: if this is a primitive simple `==` will work
760 let saw_impl
= impl_type
== ty
761 || match impl_type
.kind() {
762 // Check if these are the same def_id
764 debug
!("adt def_id: {:?}", def
.did
);
767 ty
::Foreign(def_id
) => *def_id
== type_
,
771 if saw_impl { Some(trait_) }
else { None }
777 /// Check for resolve collisions between a trait and its derive.
779 /// These are common and we should just resolve to the trait in that case.
780 fn is_derive_trait_collision
<T
>(ns
: &PerNS
<Result
<(Res
, T
), ResolutionFailure
<'_
>>>) -> bool
{
784 type_ns
: Ok((Res
::Def(DefKind
::Trait
, _
), _
)),
785 macro_ns
: Ok((Res
::Def(DefKind
::Macro(MacroKind
::Derive
), _
), _
)),
791 impl<'a
, 'tcx
> DocFolder
for LinkCollector
<'a
, 'tcx
> {
792 fn fold_item(&mut self, item
: Item
) -> Option
<Item
> {
793 use rustc_middle
::ty
::DefIdTree
;
795 let parent_node
= if item
.is_fake() {
798 find_nearest_parent_module(self.cx
.tcx
, item
.def_id
)
801 if parent_node
.is_some() {
802 trace
!("got parent node for {:?} {:?}, id {:?}", item
.type_(), item
.name
, item
.def_id
);
805 // find item's parent to resolve `Self` in item's docs below
806 debug
!("looking for the `Self` type");
807 let self_id
= if item
.is_fake() {
809 // Checking if the item is a field in an enum variant
810 } else if (matches
!(self.cx
.tcx
.def_kind(item
.def_id
), DefKind
::Field
)
812 self.cx
.tcx
.def_kind(self.cx
.tcx
.parent(item
.def_id
).unwrap()),
816 self.cx
.tcx
.parent(item
.def_id
).and_then(|item_id
| self.cx
.tcx
.parent(item_id
))
818 self.cx
.tcx
.def_kind(item
.def_id
),
825 self.cx
.tcx
.parent(item
.def_id
)
826 // HACK(jynelson): `clean` marks associated types as `TypedefItem`, not as `AssocTypeItem`.
827 // Fixing this breaks `fn render_deref_methods`.
828 // As a workaround, see if the parent of the item is an `impl`; if so this must be an associated item,
829 // regardless of what rustdoc wants to call it.
830 } else if let Some(parent
) = self.cx
.tcx
.parent(item
.def_id
) {
831 let parent_kind
= self.cx
.tcx
.def_kind(parent
);
832 Some(if parent_kind
== DefKind
::Impl { parent }
else { item.def_id }
)
837 // FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
838 let self_name
= self_id
.and_then(|self_id
| {
839 if matches
!(self.cx
.tcx
.def_kind(self_id
), DefKind
::Impl
) {
840 // using `ty.to_string()` (or any variant) has issues with raw idents
841 let ty
= self.cx
.tcx
.type_of(self_id
);
842 let name
= match ty
.kind() {
843 ty
::Adt(def
, _
) => Some(self.cx
.tcx
.item_name(def
.did
).to_string()),
844 other
if other
.is_primitive() => Some(ty
.to_string()),
847 debug
!("using type_of(): {:?}", name
);
850 let name
= self.cx
.tcx
.opt_item_name(self_id
).map(|sym
| sym
.to_string());
851 debug
!("using item_name(): {:?}", name
);
856 let inner_docs
= item
.inner_docs(self.cx
.tcx
);
858 if item
.is_mod() && inner_docs
{
859 self.mod_ids
.push(item
.def_id
);
862 // We want to resolve in the lexical scope of the documentation.
863 // In the presence of re-exports, this is not the same as the module of the item.
864 // Rather than merging all documentation into one, resolve it one attribute at a time
865 // so we know which module it came from.
866 for (parent_module
, doc
) in item
.attrs
.collapsed_doc_value_by_module_level() {
867 debug
!("combined_docs={}", doc
);
869 let (krate
, parent_node
) = if let Some(id
) = parent_module
{
872 (item
.def_id
.krate
, parent_node
)
874 // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
875 // This is a degenerate case and it's not supported by rustdoc.
876 for md_link
in markdown_links(&doc
) {
877 let link
= self.resolve_link(&item
, &doc
, &self_name
, parent_node
, krate
, md_link
);
878 if let Some(link
) = link
{
879 self.cx
.cache
.intra_doc_links
.entry(item
.def_id
).or_default().push(link
);
884 Some(if item
.is_mod() {
886 self.mod_ids
.push(item
.def_id
);
889 let ret
= self.fold_item_recur(item
);
893 self.fold_item_recur(item
)
898 enum PreprocessingError
<'a
> {
899 Anchor(AnchorFailure
),
900 Disambiguator(Range
<usize>, String
),
901 Resolution(ResolutionFailure
<'a
>, String
, Option
<Disambiguator
>),
904 impl From
<AnchorFailure
> for PreprocessingError
<'_
> {
905 fn from(err
: AnchorFailure
) -> Self {
910 struct PreprocessingInfo
{
912 disambiguator
: Option
<Disambiguator
>,
913 extra_fragment
: Option
<String
>,
918 /// - `None` if the link should be ignored.
919 /// - `Some(Err)` if the link should emit an error
920 /// - `Some(Ok)` if the link is valid
922 /// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
923 fn preprocess_link
<'a
>(
924 ori_link
: &'a MarkdownLink
,
925 ) -> Option
<Result
<PreprocessingInfo
, PreprocessingError
<'a
>>> {
926 // [] is mostly likely not supposed to be a link
927 if ori_link
.link
.is_empty() {
931 // Bail early for real links.
932 if ori_link
.link
.contains('
/'
) {
936 let stripped
= ori_link
.link
.replace("`", "");
937 let mut parts
= stripped
.split('
#');
939 let link
= parts
.next().unwrap();
940 if link
.trim().is_empty() {
941 // This is an anchor to an element of the current page, nothing to do in here!
944 let extra_fragment
= parts
.next();
945 if parts
.next().is_some() {
946 // A valid link can't have multiple #'s
947 return Some(Err(AnchorFailure
::MultipleAnchors
.into()));
950 // Parse and strip the disambiguator from the link, if present.
951 let (path_str
, disambiguator
) = match Disambiguator
::from_str(&link
) {
952 Ok(Some((d
, path
))) => (path
.trim(), Some(d
)),
953 Ok(None
) => (link
.trim(), None
),
954 Err((err_msg
, relative_range
)) => {
955 // Only report error if we would not have ignored this link. See issue #83859.
956 if !should_ignore_link_with_disambiguators(link
) {
957 let no_backticks_range
= range_between_backticks(&ori_link
);
958 let disambiguator_range
= (no_backticks_range
.start
+ relative_range
.start
)
959 ..(no_backticks_range
.start
+ relative_range
.end
);
960 return Some(Err(PreprocessingError
::Disambiguator(disambiguator_range
, err_msg
)));
967 if should_ignore_link(path_str
) {
971 // We stripped `()` and `!` when parsing the disambiguator.
972 // Add them back to be displayed, but not prefix disambiguators.
974 disambiguator
.map(|d
| d
.display_for(path_str
)).unwrap_or_else(|| path_str
.to_owned());
976 // Strip generics from the path.
977 let path_str
= if path_str
.contains(['
<'
, '
>'
].as_slice()) {
978 match strip_generics_from_path(&path_str
) {
981 debug
!("link has malformed generics: {}", path_str
);
982 return Some(Err(PreprocessingError
::Resolution(
993 // Sanity check to make sure we don't have any angle brackets after stripping generics.
994 assert
!(!path_str
.contains(['
<'
, '
>'
].as_slice()));
996 // The link is not an intra-doc link if it still contains spaces after stripping generics.
997 if path_str
.contains(' '
) {
1001 Some(Ok(PreprocessingInfo
{
1004 extra_fragment
: extra_fragment
.map(String
::from
),
1009 impl LinkCollector
<'_
, '_
> {
1010 /// This is the entry point for resolving an intra-doc link.
1012 /// FIXME(jynelson): this is way too many arguments
1017 self_name
: &Option
<String
>,
1018 parent_node
: Option
<DefId
>,
1020 ori_link
: MarkdownLink
,
1021 ) -> Option
<ItemLink
> {
1022 trace
!("considering link '{}'", ori_link
.link
);
1024 let diag_info
= DiagnosticInfo
{
1027 ori_link
: &ori_link
.link
,
1028 link_range
: ori_link
.range
.clone(),
1031 let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text }
=
1032 match preprocess_link(&ori_link
)?
{
1036 PreprocessingError
::Anchor(err
) => anchor_failure(self.cx
, diag_info
, err
),
1037 PreprocessingError
::Disambiguator(range
, msg
) => {
1038 disambiguator_error(self.cx
, diag_info
, range
, &msg
)
1040 PreprocessingError
::Resolution(err
, path_str
, disambiguator
) => {
1053 let mut path_str
= &*path_str
;
1055 let inner_docs
= item
.inner_docs(self.cx
.tcx
);
1057 // In order to correctly resolve intra-doc links we need to
1058 // pick a base AST node to work from. If the documentation for
1059 // this module came from an inner comment (//!) then we anchor
1060 // our name resolution *inside* the module. If, on the other
1061 // hand it was an outer comment (///) then we anchor the name
1062 // resolution in the parent module on the basis that the names
1063 // used are more likely to be intended to be parent names. For
1064 // this, we set base_node to None for inner comments since
1065 // we've already pushed this node onto the resolution stack but
1066 // for outer comments we explicitly try and resolve against the
1067 // parent_node first.
1069 if item
.is_mod() && inner_docs { self.mod_ids.last().copied() }
else { parent_node }
;
1071 let mut module_id
= if let Some(id
) = base_node
{
1075 debug
!("attempting to resolve item without parent module: {}", path_str
);
1081 smallvec
![ResolutionFailure
::NoParentItem
],
1087 // replace `Self` with suitable item's parent name
1088 let is_lone_self
= path_str
== "Self";
1089 let is_lone_crate
= path_str
== "crate";
1090 if path_str
.starts_with("Self::") || is_lone_self
{
1091 if let Some(ref name
) = self_name
{
1095 resolved_self
= format
!("{}::{}", name
, &path_str
[6..]);
1096 path_str
= &resolved_self
;
1099 } else if path_str
.starts_with("crate::") || is_lone_crate
{
1100 use rustc_span
::def_id
::CRATE_DEF_INDEX
;
1102 // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
1103 // But rustdoc wants it to mean the crate this item was originally present in.
1104 // To work around this, remove it and resolve relative to the crate root instead.
1105 // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
1106 // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
1107 // FIXME(#78696): This doesn't always work.
1111 resolved_self
= format
!("self::{}", &path_str
["crate::".len()..]);
1112 path_str
= &resolved_self
;
1114 module_id
= DefId { krate, index: CRATE_DEF_INDEX }
;
1117 let (mut res
, mut fragment
) = self.resolve_with_disambiguator_cached(
1121 path_str
: path_str
.to_owned(),
1122 extra_fragment
: extra_fragment
.map(String
::from
),
1124 diag_info
.clone(), // this struct should really be Copy, but Range is not :(
1125 matches
!(ori_link
.kind
, LinkType
::Reference
| LinkType
::Shortcut
),
1128 // Check for a primitive which might conflict with a module
1129 // Report the ambiguity and require that the user specify which one they meant.
1130 // FIXME: could there ever be a primitive not in the type namespace?
1133 None
| Some(Disambiguator
::Namespace(Namespace
::TypeNS
) | Disambiguator
::Primitive
)
1134 ) && !matches
!(res
, Res
::Primitive(_
))
1136 if let Some(prim
) = resolve_primitive(path_str
, TypeNS
) {
1138 if matches
!(disambiguator
, Some(Disambiguator
::Primitive
)) {
1139 if fragment
.is_some() {
1143 AnchorFailure
::RustdocAnchorConflict(prim
),
1148 fragment
= Some(prim
.name(self.cx
.tcx
));
1150 // `[char]` when a `char` module is in scope
1151 let candidates
= vec
![res
, prim
];
1152 ambiguity_error(self.cx
, diag_info
, path_str
, candidates
);
1158 let report_mismatch
= |specified
: Disambiguator
, resolved
: Disambiguator
| {
1159 // The resolved item did not match the disambiguator; give a better error than 'not found'
1160 let msg
= format
!("incompatible link kind for `{}`", path_str
);
1161 let callback
= |diag
: &mut DiagnosticBuilder
<'_
>, sp
| {
1163 "this link resolved to {} {}, which is not {} {}",
1166 specified
.article(),
1170 suggest_disambiguator(resolved
, diag
, path_str
, dox
, sp
, &ori_link
.range
);
1172 report_diagnostic(self.cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, callback
);
1175 let verify
= |kind
: DefKind
, id
: DefId
| {
1176 let (kind
, id
) = self.kind_side_channel
.take().unwrap_or((kind
, id
));
1177 debug
!("intra-doc link to {} resolved to {:?} (id: {:?})", path_str
, res
, id
);
1179 // Disallow e.g. linking to enums with `struct@`
1180 debug
!("saw kind {:?} with disambiguator {:?}", kind
, disambiguator
);
1181 match (kind
, disambiguator
) {
1182 | (DefKind
::Const
| DefKind
::ConstParam
| DefKind
::AssocConst
| DefKind
::AnonConst
, Some(Disambiguator
::Kind(DefKind
::Const
)))
1183 // NOTE: this allows 'method' to mean both normal functions and associated functions
1184 // This can't cause ambiguity because both are in the same namespace.
1185 | (DefKind
::Fn
| DefKind
::AssocFn
, Some(Disambiguator
::Kind(DefKind
::Fn
)))
1186 // These are namespaces; allow anything in the namespace to match
1187 | (_
, Some(Disambiguator
::Namespace(_
)))
1188 // If no disambiguator given, allow anything
1190 // All of these are valid, so do nothing
1192 (actual
, Some(Disambiguator
::Kind(expected
))) if actual
== expected
=> {}
1193 (_
, Some(specified @ Disambiguator
::Kind(_
) | specified @ Disambiguator
::Primitive
)) => {
1194 report_mismatch(specified
, Disambiguator
::Kind(kind
));
1199 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
1200 if let Some((src_id
, dst_id
)) = id
1202 .and_then(|dst_id
| item
.def_id
.as_local().map(|src_id
| (src_id
, dst_id
)))
1204 use rustc_hir
::def_id
::LOCAL_CRATE
;
1206 let hir_src
= self.cx
.tcx
.hir().local_def_id_to_hir_id(src_id
);
1207 let hir_dst
= self.cx
.tcx
.hir().local_def_id_to_hir_id(dst_id
);
1209 if self.cx
.tcx
.privacy_access_levels(LOCAL_CRATE
).is_exported(hir_src
)
1210 && !self.cx
.tcx
.privacy_access_levels(LOCAL_CRATE
).is_exported(hir_dst
)
1212 privacy_error(self.cx
, &diag_info
, &path_str
);
1220 Res
::Primitive(prim
) => {
1221 if let Some((kind
, id
)) = self.kind_side_channel
.take() {
1222 // We're actually resolving an associated item of a primitive, so we need to
1223 // verify the disambiguator (if any) matches the type of the associated item.
1224 // This case should really follow the same flow as the `Res::Def` branch below,
1225 // but attempting to add a call to `clean::register_res` causes an ICE. @jyn514
1226 // thinks `register_res` is only needed for cross-crate re-exports, but Rust
1227 // doesn't allow statements like `use str::trim;`, making this a (hopefully)
1228 // valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677
1229 // for discussion on the matter.
1232 // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether.
1233 // However I'm not sure how to check that across crates.
1234 if prim
== PrimitiveType
::RawPointer
1235 && item
.def_id
.is_local()
1236 && !self.cx
.tcx
.features().intra_doc_pointers
1238 let span
= super::source_span_for_markdown_range(
1244 .unwrap_or_else(|| item
.attr_span(self.cx
.tcx
));
1246 rustc_session
::parse
::feature_err(
1247 &self.cx
.tcx
.sess
.parse_sess
,
1248 sym
::intra_doc_pointers
,
1250 "linking to associated items of raw pointers is experimental",
1252 .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does")
1256 match disambiguator
{
1257 Some(Disambiguator
::Primitive
| Disambiguator
::Namespace(_
)) | None
=> {}
1259 report_mismatch(other
, Disambiguator
::Primitive
);
1265 Some(ItemLink { link: ori_link.link, link_text, did: None, fragment }
)
1267 Res
::Def(kind
, id
) => {
1269 let id
= clean
::register_res(self.cx
, rustc_hir
::def
::Res
::Def(kind
, id
));
1270 Some(ItemLink { link: ori_link.link, link_text, did: Some(id), fragment }
)
1275 fn resolve_with_disambiguator_cached(
1277 key
: ResolutionInfo
,
1278 diag
: DiagnosticInfo
<'_
>,
1279 cache_resolution_failure
: bool
,
1280 ) -> Option
<(Res
, Option
<String
>)> {
1281 // Try to look up both the result and the corresponding side channel value
1282 if let Some(ref cached
) = self.visited_links
.get(&key
) {
1285 self.kind_side_channel
.set(cached
.side_channel
.clone());
1286 return Some(cached
.res
.clone());
1288 None
if cache_resolution_failure
=> return None
,
1290 // Although we hit the cache and found a resolution error, this link isn't
1291 // supposed to cache those. Run link resolution again to emit the expected
1292 // resolution error.
1297 let res
= self.resolve_with_disambiguator(&key
, diag
);
1299 // Cache only if resolved successfully - don't silence duplicate errors
1300 if let Some(res
) = res
{
1301 // Store result for the actual namespace
1302 self.visited_links
.insert(
1306 side_channel
: self.kind_side_channel
.clone().into_inner(),
1312 if cache_resolution_failure
{
1313 // For reference-style links we only want to report one resolution error
1314 // so let's cache them as well.
1315 self.visited_links
.insert(key
, None
);
1322 /// After parsing the disambiguator, resolve the main part of the link.
1323 // FIXME(jynelson): wow this is just so much
1324 fn resolve_with_disambiguator(
1326 key
: &ResolutionInfo
,
1327 diag
: DiagnosticInfo
<'_
>,
1328 ) -> Option
<(Res
, Option
<String
>)> {
1329 let disambiguator
= key
.dis
;
1330 let path_str
= &key
.path_str
;
1331 let base_node
= key
.module_id
;
1332 let extra_fragment
= &key
.extra_fragment
;
1334 match disambiguator
.map(Disambiguator
::ns
) {
1335 Some(expected_ns @
(ValueNS
| TypeNS
)) => {
1336 match self.resolve(path_str
, expected_ns
, base_node
, extra_fragment
) {
1337 Ok(res
) => Some(res
),
1338 Err(ErrorKind
::Resolve(box mut kind
)) => {
1339 // We only looked in one namespace. Try to give a better error if possible.
1340 if kind
.full_res().is_none() {
1341 let other_ns
= if expected_ns
== ValueNS { TypeNS }
else { ValueNS }
;
1342 // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1343 // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1344 for &new_ns
in &[other_ns
, MacroNS
] {
1346 self.check_full_res(new_ns
, path_str
, base_node
, extra_fragment
)
1348 kind
= ResolutionFailure
::WrongNamespace { res, expected_ns }
;
1353 resolution_failure(self, diag
, path_str
, disambiguator
, smallvec
![kind
]);
1354 // This could just be a normal link or a broken link
1355 // we could potentially check if something is
1356 // "intra-doc-link-like" and warn in that case.
1359 Err(ErrorKind
::AnchorFailure(msg
)) => {
1360 anchor_failure(self.cx
, diag
, msg
);
1367 let mut candidates
= PerNS
{
1369 .resolve_macro(path_str
, base_node
)
1370 .map(|res
| (res
, extra_fragment
.clone())),
1371 type_ns
: match self.resolve(path_str
, TypeNS
, base_node
, extra_fragment
) {
1373 debug
!("got res in TypeNS: {:?}", res
);
1376 Err(ErrorKind
::AnchorFailure(msg
)) => {
1377 anchor_failure(self.cx
, diag
, msg
);
1380 Err(ErrorKind
::Resolve(box kind
)) => Err(kind
),
1382 value_ns
: match self.resolve(path_str
, ValueNS
, base_node
, extra_fragment
) {
1384 Err(ErrorKind
::AnchorFailure(msg
)) => {
1385 anchor_failure(self.cx
, diag
, msg
);
1388 Err(ErrorKind
::Resolve(box kind
)) => Err(kind
),
1390 .and_then(|(res
, fragment
)| {
1391 // Constructors are picked up in the type namespace.
1393 Res
::Def(DefKind
::Ctor(..), _
) => {
1394 Err(ResolutionFailure
::WrongNamespace { res, expected_ns: TypeNS }
)
1397 match (fragment
, extra_fragment
.clone()) {
1398 (Some(fragment
), Some(_
)) => {
1399 // Shouldn't happen but who knows?
1400 Ok((res
, Some(fragment
)))
1402 (fragment
, None
) | (None
, fragment
) => Ok((res
, fragment
)),
1409 let len
= candidates
.iter().filter(|res
| res
.is_ok()).count();
1417 candidates
.into_iter().filter_map(|res
| res
.err()).collect(),
1419 // this could just be a normal link
1424 Some(candidates
.into_iter().filter_map(|res
| res
.ok()).next().unwrap())
1425 } else if len
== 2 && is_derive_trait_collision(&candidates
) {
1426 Some(candidates
.type_ns
.unwrap())
1428 if is_derive_trait_collision(&candidates
) {
1429 candidates
.macro_ns
= Err(ResolutionFailure
::Dummy
);
1431 // If we're reporting an ambiguity, don't mention the namespaces that failed
1432 let candidates
= candidates
.map(|candidate
| candidate
.ok().map(|(res
, _
)| res
));
1433 ambiguity_error(self.cx
, diag
, path_str
, candidates
.present_items().collect());
1438 match self.resolve_macro(path_str
, base_node
) {
1439 Ok(res
) => Some((res
, extra_fragment
.clone())),
1441 // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
1442 for &ns
in &[TypeNS
, ValueNS
] {
1444 self.check_full_res(ns
, path_str
, base_node
, extra_fragment
)
1447 ResolutionFailure
::WrongNamespace { res, expected_ns: MacroNS }
;
1451 resolution_failure(self, diag
, path_str
, disambiguator
, smallvec
![kind
]);
1460 /// Get the section of a link between the backticks,
1461 /// or the whole link if there aren't any backticks.
1469 fn range_between_backticks(ori_link
: &MarkdownLink
) -> Range
<usize> {
1470 let after_first_backtick_group
= ori_link
.link
.bytes().position(|b
| b
!= b'`'
).unwrap_or(0);
1471 let before_second_backtick_group
= ori_link
1474 .skip(after_first_backtick_group
)
1475 .position(|b
| b
== b'`'
)
1476 .unwrap_or(ori_link
.link
.len());
1477 (ori_link
.range
.start
+ after_first_backtick_group
)
1478 ..(ori_link
.range
.start
+ before_second_backtick_group
)
1481 /// Returns true if we should ignore `link` due to it being unlikely
1482 /// that it is an intra-doc link. `link` should still have disambiguators
1483 /// if there were any.
1485 /// The difference between this and [`should_ignore_link()`] is that this
1486 /// check should only be used on links that still have disambiguators.
1487 fn should_ignore_link_with_disambiguators(link
: &str) -> bool
{
1488 link
.contains(|ch
: char| !(ch
.is_alphanumeric() || ":_<>, !*&;@()".contains(ch
)))
1491 /// Returns true if we should ignore `path_str` due to it being unlikely
1492 /// that it is an intra-doc link.
1493 fn should_ignore_link(path_str
: &str) -> bool
{
1494 path_str
.contains(|ch
: char| !(ch
.is_alphanumeric() || ":_<>, !*&;".contains(ch
)))
1497 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1498 /// Disambiguators for a link.
1499 enum Disambiguator
{
1502 /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
1504 /// `struct@` or `f()`
1507 Namespace(Namespace
),
1510 impl Disambiguator
{
1511 /// The text that should be displayed when the path is rendered as HTML.
1513 /// NOTE: `path` is not the original link given by the user, but a name suitable for passing to `resolve`.
1514 fn display_for(&self, path
: &str) -> String
{
1516 // FIXME: this will have different output if the user had `m!()` originally.
1517 Self::Kind(DefKind
::Macro(MacroKind
::Bang
)) => format
!("{}!", path
),
1518 Self::Kind(DefKind
::Fn
) => format
!("{}()", path
),
1519 _
=> path
.to_owned(),
1523 /// Given a link, parse and return `(disambiguator, path_str)`.
1525 /// This returns `Ok(Some(...))` if a disambiguator was found,
1526 /// `Ok(None)` if no disambiguator was found, or `Err(...)`
1527 /// if there was a problem with the disambiguator.
1528 fn from_str(link
: &str) -> Result
<Option
<(Self, &str)>, (String
, Range
<usize>)> {
1529 use Disambiguator
::{Kind, Namespace as NS, Primitive}
;
1531 if let Some(idx
) = link
.find('@'
) {
1532 let (prefix
, rest
) = link
.split_at(idx
);
1533 let d
= match prefix
{
1534 "struct" => Kind(DefKind
::Struct
),
1535 "enum" => Kind(DefKind
::Enum
),
1536 "trait" => Kind(DefKind
::Trait
),
1537 "union" => Kind(DefKind
::Union
),
1538 "module" | "mod" => Kind(DefKind
::Mod
),
1539 "const" | "constant" => Kind(DefKind
::Const
),
1540 "static" => Kind(DefKind
::Static
),
1541 "function" | "fn" | "method" => Kind(DefKind
::Fn
),
1542 "derive" => Kind(DefKind
::Macro(MacroKind
::Derive
)),
1543 "type" => NS(Namespace
::TypeNS
),
1544 "value" => NS(Namespace
::ValueNS
),
1545 "macro" => NS(Namespace
::MacroNS
),
1546 "prim" | "primitive" => Primitive
,
1547 _
=> return Err((format
!("unknown disambiguator `{}`", prefix
), 0..idx
)),
1549 Ok(Some((d
, &rest
[1..])))
1552 ("!()", DefKind
::Macro(MacroKind
::Bang
)),
1553 ("()", DefKind
::Fn
),
1554 ("!", DefKind
::Macro(MacroKind
::Bang
)),
1556 for &(suffix
, kind
) in &suffixes
{
1557 if let Some(link
) = link
.strip_suffix(suffix
) {
1558 // Avoid turning `!` or `()` into an empty string
1559 if !link
.is_empty() {
1560 return Ok(Some((Kind(kind
), link
)));
1568 fn from_res(res
: Res
) -> Self {
1570 Res
::Def(kind
, _
) => Disambiguator
::Kind(kind
),
1571 Res
::Primitive(_
) => Disambiguator
::Primitive
,
1575 /// Used for error reporting.
1576 fn suggestion(self) -> Suggestion
{
1577 let kind
= match self {
1578 Disambiguator
::Primitive
=> return Suggestion
::Prefix("prim"),
1579 Disambiguator
::Kind(kind
) => kind
,
1580 Disambiguator
::Namespace(_
) => panic
!("display_for cannot be used on namespaces"),
1582 if kind
== DefKind
::Macro(MacroKind
::Bang
) {
1583 return Suggestion
::Macro
;
1584 } else if kind
== DefKind
::Fn
|| kind
== DefKind
::AssocFn
{
1585 return Suggestion
::Function
;
1588 let prefix
= match kind
{
1589 DefKind
::Struct
=> "struct",
1590 DefKind
::Enum
=> "enum",
1591 DefKind
::Trait
=> "trait",
1592 DefKind
::Union
=> "union",
1593 DefKind
::Mod
=> "mod",
1594 DefKind
::Const
| DefKind
::ConstParam
| DefKind
::AssocConst
| DefKind
::AnonConst
=> {
1597 DefKind
::Static
=> "static",
1598 DefKind
::Macro(MacroKind
::Derive
) => "derive",
1599 // Now handle things that don't have a specific disambiguator
1602 .expect("tried to calculate a disambiguator for a def without a namespace?")
1604 Namespace
::TypeNS
=> "type",
1605 Namespace
::ValueNS
=> "value",
1606 Namespace
::MacroNS
=> "macro",
1610 Suggestion
::Prefix(prefix
)
1613 fn ns(self) -> Namespace
{
1615 Self::Namespace(n
) => n
,
1617 k
.ns().expect("only DefKinds with a valid namespace can be disambiguators")
1619 Self::Primitive
=> TypeNS
,
1623 fn article(self) -> &'
static str {
1625 Self::Namespace(_
) => panic
!("article() doesn't make sense for namespaces"),
1626 Self::Kind(k
) => k
.article(),
1627 Self::Primitive
=> "a",
1631 fn descr(self) -> &'
static str {
1633 Self::Namespace(n
) => n
.descr(),
1634 // HACK(jynelson): by looking at the source I saw the DefId we pass
1635 // for `expected.descr()` doesn't matter, since it's not a crate
1636 Self::Kind(k
) => k
.descr(DefId
::local(hir
::def_id
::DefIndex
::from_usize(0))),
1637 Self::Primitive
=> "builtin type",
1642 /// A suggestion to show in a diagnostic.
1645 Prefix(&'
static str),
1653 fn descr(&self) -> Cow
<'
static, str> {
1655 Self::Prefix(x
) => format
!("prefix with `{}@`", x
).into(),
1656 Self::Function
=> "add parentheses".into(),
1657 Self::Macro
=> "add an exclamation mark".into(),
1661 fn as_help(&self, path_str
: &str) -> String
{
1662 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1664 Self::Prefix(prefix
) => format
!("{}@{}", prefix
, path_str
),
1665 Self::Function
=> format
!("{}()", path_str
),
1666 Self::Macro
=> format
!("{}!", path_str
),
1671 /// Reports a diagnostic for an intra-doc link.
1673 /// If no link range is provided, or the source span of the link cannot be determined, the span of
1674 /// the entire documentation block is used for the lint. If a range is provided but the span
1675 /// calculation fails, a note is added to the diagnostic pointing to the link in the markdown.
1677 /// The `decorate` callback is invoked in all cases to allow further customization of the
1678 /// diagnostic before emission. If the span of the link was able to be determined, the second
1679 /// parameter of the callback will contain it, and the primary span of the diagnostic will be set
1681 fn report_diagnostic(
1683 lint
: &'
static Lint
,
1685 DiagnosticInfo { item, ori_link: _, dox, link_range }
: &DiagnosticInfo
<'_
>,
1686 decorate
: impl FnOnce(&mut DiagnosticBuilder
<'_
>, Option
<rustc_span
::Span
>),
1688 let hir_id
= match DocContext
::as_local_hir_id(tcx
, item
.def_id
) {
1689 Some(hir_id
) => hir_id
,
1691 // If non-local, no need to check anything.
1692 info
!("ignoring warning from parent crate: {}", msg
);
1697 let sp
= item
.attr_span(tcx
);
1699 tcx
.struct_span_lint_hir(lint
, hir_id
, sp
, |lint
| {
1700 let mut diag
= lint
.build(msg
);
1702 let span
= super::source_span_for_markdown_range(tcx
, dox
, link_range
, &item
.attrs
);
1704 if let Some(sp
) = span
{
1707 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1710 // last_new_line_offset
1711 let last_new_line_offset
= dox
[..link_range
.start
].rfind('
\n'
).map_or(0, |n
| n
+ 1);
1712 let line
= dox
[last_new_line_offset
..].lines().next().unwrap_or("");
1714 // Print the line containing the `link_range` and manually mark it with '^'s.
1716 "the link appears in this line:\n\n{line}\n\
1717 {indicator: <before$}{indicator:^<found$}",
1720 before
= link_range
.start
- last_new_line_offset
,
1721 found
= link_range
.len(),
1725 decorate(&mut diag
, span
);
1731 /// Reports a link that failed to resolve.
1733 /// This also tries to resolve any intermediate path segments that weren't
1734 /// handled earlier. For example, if passed `Item::Crate(std)` and `path_str`
1735 /// `std::io::Error::x`, this will resolve `std::io::Error`.
1736 fn resolution_failure(
1737 collector
: &mut LinkCollector
<'_
, '_
>,
1738 diag_info
: DiagnosticInfo
<'_
>,
1740 disambiguator
: Option
<Disambiguator
>,
1741 kinds
: SmallVec
<[ResolutionFailure
<'_
>; 3]>,
1743 let tcx
= collector
.cx
.tcx
;
1746 BROKEN_INTRA_DOC_LINKS
,
1747 &format
!("unresolved link to `{}`", path_str
),
1750 let item
= |res
: Res
| format
!("the {} `{}`", res
.descr(), res
.name(tcx
),);
1751 let assoc_item_not_allowed
= |res
: Res
| {
1752 let name
= res
.name(tcx
);
1754 "`{}` is {} {}, not a module or type, and cannot have associated items",
1760 // ignore duplicates
1761 let mut variants_seen
= SmallVec
::<[_
; 3]>::new();
1762 for mut failure
in kinds
{
1763 let variant
= std
::mem
::discriminant(&failure
);
1764 if variants_seen
.contains(&variant
) {
1767 variants_seen
.push(variant
);
1769 if let ResolutionFailure
::NotResolved { module_id, partial_res, unresolved }
=
1774 let module_id
= *module_id
;
1775 // FIXME(jynelson): this might conflict with my `Self` fix in #76467
1776 // FIXME: maybe use itertools `collect_tuple` instead?
1777 fn split(path
: &str) -> Option
<(&str, &str)> {
1778 let mut splitter
= path
.rsplitn(2, "::");
1779 splitter
.next().and_then(|right
| splitter
.next().map(|left
| (left
, right
)))
1782 // Check if _any_ parent of the path gets resolved.
1783 // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1784 let mut name
= path_str
;
1786 let (start
, end
) = if let Some(x
) = split(name
) {
1789 // avoid bug that marked [Quux::Z] as missing Z, not Quux
1790 if partial_res
.is_none() {
1791 *unresolved
= name
.into();
1796 for &ns
in &[TypeNS
, ValueNS
, MacroNS
] {
1798 collector
.check_full_res(ns
, &start
, module_id
, &None
)
1800 debug
!("found partial_res={:?}", res
);
1801 *partial_res
= Some(res
);
1802 *unresolved
= end
.into();
1806 *unresolved
= end
.into();
1809 let last_found_module
= match *partial_res
{
1810 Some(Res
::Def(DefKind
::Mod
, id
)) => Some(id
),
1811 None
=> Some(module_id
),
1814 // See if this was a module: `[path]` or `[std::io::nope]`
1815 if let Some(module
) = last_found_module
{
1816 let note
= if partial_res
.is_some() {
1817 // Part of the link resolved; e.g. `std::io::nonexistent`
1818 let module_name
= tcx
.item_name(module
);
1819 format
!("no item named `{}` in module `{}`", unresolved
, module_name
)
1821 // None of the link resolved; e.g. `Notimported`
1822 format
!("no item named `{}` in scope", unresolved
)
1824 if let Some(span
) = sp
{
1825 diag
.span_label(span
, ¬e
);
1830 // If the link has `::` in it, assume it was meant to be an intra-doc link.
1831 // Otherwise, the `[]` might be unrelated.
1832 // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
1833 if !path_str
.contains("::") {
1834 diag
.help(r
#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1840 // Otherwise, it must be an associated item or variant
1841 let res
= partial_res
.expect("None case was handled by `last_found_module`");
1842 let name
= res
.name(tcx
);
1843 let kind
= match res
{
1844 Res
::Def(kind
, _
) => Some(kind
),
1845 Res
::Primitive(_
) => None
,
1847 let path_description
= if let Some(kind
) = kind
{
1849 Mod
| ForeignMod
=> "inner item",
1850 Struct
=> "field or associated item",
1851 Enum
| Union
=> "variant or associated item",
1868 let note
= assoc_item_not_allowed(res
);
1869 if let Some(span
) = sp
{
1870 diag
.span_label(span
, ¬e
);
1876 Trait
| TyAlias
| ForeignTy
| OpaqueTy
| TraitAlias
| TyParam
1877 | Static
=> "associated item",
1878 Impl
| GlobalAsm
=> unreachable
!("not a path"),
1884 "the {} `{}` has no {} named `{}`",
1887 disambiguator
.map_or(path_description
, |d
| d
.descr()),
1890 if let Some(span
) = sp
{
1891 diag
.span_label(span
, ¬e
);
1898 let note
= match failure
{
1899 ResolutionFailure
::NotResolved { .. }
=> unreachable
!("handled above"),
1900 ResolutionFailure
::Dummy
=> continue,
1901 ResolutionFailure
::WrongNamespace { res, expected_ns }
=> {
1902 if let Res
::Def(kind
, _
) = res
{
1903 let disambiguator
= Disambiguator
::Kind(kind
);
1904 suggest_disambiguator(
1910 &diag_info
.link_range
,
1915 "this link resolves to {}, which is not in the {} namespace",
1920 ResolutionFailure
::NoParentItem
=> {
1921 diag
.level
= rustc_errors
::Level
::Bug
;
1922 "all intra-doc links should have a parent item".to_owned()
1924 ResolutionFailure
::MalformedGenerics(variant
) => match variant
{
1925 MalformedGenerics
::UnbalancedAngleBrackets
=> {
1926 String
::from("unbalanced angle brackets")
1928 MalformedGenerics
::MissingType
=> {
1929 String
::from("missing type for generic parameters")
1931 MalformedGenerics
::HasFullyQualifiedSyntax
=> {
1932 diag
.note("see https://github.com/rust-lang/rust/issues/74563 for more information");
1933 String
::from("fully-qualified syntax is unsupported")
1935 MalformedGenerics
::InvalidPathSeparator
=> {
1936 String
::from("has invalid path separator")
1938 MalformedGenerics
::TooManyAngleBrackets
=> {
1939 String
::from("too many angle brackets")
1941 MalformedGenerics
::EmptyAngleBrackets
=> {
1942 String
::from("empty angle brackets")
1946 if let Some(span
) = sp
{
1947 diag
.span_label(span
, ¬e
);
1956 /// Report an anchor failure.
1957 fn anchor_failure(cx
: &DocContext
<'_
>, diag_info
: DiagnosticInfo
<'_
>, failure
: AnchorFailure
) {
1958 let (msg
, anchor_idx
) = match failure
{
1959 AnchorFailure
::MultipleAnchors
=> {
1960 (format
!("`{}` contains multiple anchors", diag_info
.ori_link
), 1)
1962 AnchorFailure
::RustdocAnchorConflict(res
) => (
1964 "`{}` contains an anchor, but links to {kind}s are already anchored",
1972 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, |diag
, sp
| {
1973 if let Some(mut sp
) = sp
{
1974 if let Some((fragment_offset
, _
)) =
1975 diag_info
.ori_link
.char_indices().filter(|(_
, x
)| *x
== '
#').nth(anchor_idx)
1977 sp
= sp
.with_lo(sp
.lo() + rustc_span
::BytePos(fragment_offset
as _
));
1979 diag
.span_label(sp
, "invalid anchor");
1981 if let AnchorFailure
::RustdocAnchorConflict(Res
::Primitive(_
)) = failure
{
1982 diag
.note("this restriction may be lifted in a future release");
1983 diag
.note("see https://github.com/rust-lang/rust/issues/83083 for more information");
1988 /// Report an error in the link disambiguator.
1989 fn disambiguator_error(
1990 cx
: &DocContext
<'_
>,
1991 mut diag_info
: DiagnosticInfo
<'_
>,
1992 disambiguator_range
: Range
<usize>,
1995 diag_info
.link_range
= disambiguator_range
;
1996 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, msg
, &diag_info
, |_diag
, _sp
| {}
);
1999 /// Report an ambiguity error, where there were multiple possible resolutions.
2001 cx
: &DocContext
<'_
>,
2002 diag_info
: DiagnosticInfo
<'_
>,
2004 candidates
: Vec
<Res
>,
2006 let mut msg
= format
!("`{}` is ", path_str
);
2008 match candidates
.as_slice() {
2009 [first_def
, second_def
] => {
2011 "both {} {} and {} {}",
2012 first_def
.article(),
2014 second_def
.article(),
2019 let mut candidates
= candidates
.iter().peekable();
2020 while let Some(res
) = candidates
.next() {
2021 if candidates
.peek().is_some() {
2022 msg
+= &format
!("{} {}, ", res
.article(), res
.descr());
2024 msg
+= &format
!("and {} {}", res
.article(), res
.descr());
2030 report_diagnostic(cx
.tcx
, BROKEN_INTRA_DOC_LINKS
, &msg
, &diag_info
, |diag
, sp
| {
2031 if let Some(sp
) = sp
{
2032 diag
.span_label(sp
, "ambiguous link");
2034 diag
.note("ambiguous link");
2037 for res
in candidates
{
2038 let disambiguator
= Disambiguator
::from_res(res
);
2039 suggest_disambiguator(
2045 &diag_info
.link_range
,
2051 /// In case of an ambiguity or mismatched disambiguator, suggest the correct
2053 fn suggest_disambiguator(
2054 disambiguator
: Disambiguator
,
2055 diag
: &mut DiagnosticBuilder
<'_
>,
2058 sp
: Option
<rustc_span
::Span
>,
2059 link_range
: &Range
<usize>,
2061 let suggestion
= disambiguator
.suggestion();
2062 let help
= format
!("to link to the {}, {}", disambiguator
.descr(), suggestion
.descr());
2064 if let Some(sp
) = sp
{
2065 let msg
= if dox
.bytes().nth(link_range
.start
) == Some(b'`'
) {
2066 format
!("`{}`", suggestion
.as_help(path_str
))
2068 suggestion
.as_help(path_str
)
2071 diag
.span_suggestion(sp
, &help
, msg
, Applicability
::MaybeIncorrect
);
2073 diag
.help(&format
!("{}: {}", help
, suggestion
.as_help(path_str
)));
2077 /// Report a link from a public item to a private one.
2078 fn privacy_error(cx
: &DocContext
<'_
>, diag_info
: &DiagnosticInfo
<'_
>, path_str
: &str) {
2080 let item_name
= match diag_info
.item
.name
{
2082 sym
= name
.as_str();
2085 None
=> "<unknown>",
2088 format
!("public documentation for `{}` links to private item `{}`", item_name
, path_str
);
2090 report_diagnostic(cx
.tcx
, PRIVATE_INTRA_DOC_LINKS
, &msg
, diag_info
, |diag
, sp
| {
2091 if let Some(sp
) = sp
{
2092 diag
.span_label(sp
, "this item is private");
2095 let note_msg
= if cx
.render_options
.document_private
{
2096 "this link resolves only because you passed `--document-private-items`, but will break without"
2098 "this link will resolve properly if you pass `--document-private-items`"
2100 diag
.note(note_msg
);
2104 /// Given an enum variant's res, return the res of its enum and the associated fragment.
2106 cx
: &DocContext
<'_
>,
2108 extra_fragment
: &Option
<String
>,
2109 ) -> Result
<(Res
, Option
<String
>), ErrorKind
<'
static>> {
2110 use rustc_middle
::ty
::DefIdTree
;
2112 if extra_fragment
.is_some() {
2113 return Err(ErrorKind
::AnchorFailure(AnchorFailure
::RustdocAnchorConflict(res
)));
2116 .parent(res
.def_id())
2118 let parent_def
= Res
::Def(DefKind
::Enum
, parent
);
2119 let variant
= cx
.tcx
.expect_variant_res(res
.as_hir_res().unwrap());
2120 (parent_def
, Some(format
!("variant.{}", variant
.ident
.name
)))
2122 .ok_or_else(|| ResolutionFailure
::NoParentItem
.into())
2125 /// Resolve a primitive type or value.
2126 fn resolve_primitive(path_str
: &str, ns
: Namespace
) -> Option
<Res
> {
2130 use PrimitiveType
::*;
2131 let prim
= match path_str
{
2147 "bool" | "true" | "false" => Bool
,
2148 "str" | "&str" => Str
,
2149 // See #80181 for why these don't have symbols associated.
2154 "pointer" | "*const" | "*mut" => RawPointer
,
2155 "reference" | "&" | "&mut" => Reference
,
2157 "never" | "!" => Never
,
2160 debug
!("resolved primitives {:?}", prim
);
2161 Some(Res
::Primitive(prim
))
2164 fn strip_generics_from_path(path_str
: &str) -> Result
<String
, ResolutionFailure
<'
static>> {
2165 let mut stripped_segments
= vec
![];
2166 let mut path
= path_str
.chars().peekable();
2167 let mut segment
= Vec
::new();
2169 while let Some(chr
) = path
.next() {
2172 if path
.next_if_eq(&'
:'
).is_some() {
2173 let stripped_segment
=
2174 strip_generics_from_path_segment(mem
::take(&mut segment
))?
;
2175 if !stripped_segment
.is_empty() {
2176 stripped_segments
.push(stripped_segment
);
2179 return Err(ResolutionFailure
::MalformedGenerics(
2180 MalformedGenerics
::InvalidPathSeparator
,
2189 return Err(ResolutionFailure
::MalformedGenerics(
2190 MalformedGenerics
::TooManyAngleBrackets
,
2194 return Err(ResolutionFailure
::MalformedGenerics(
2195 MalformedGenerics
::EmptyAngleBrackets
,
2201 while let Some(chr
) = path
.next_if(|c
| *c
!= '
>'
) {
2208 _
=> segment
.push(chr
),
2210 trace
!("raw segment: {:?}", segment
);
2213 if !segment
.is_empty() {
2214 let stripped_segment
= strip_generics_from_path_segment(segment
)?
;
2215 if !stripped_segment
.is_empty() {
2216 stripped_segments
.push(stripped_segment
);
2220 debug
!("path_str: {:?}\nstripped segments: {:?}", path_str
, &stripped_segments
);
2222 let stripped_path
= stripped_segments
.join("::");
2224 if !stripped_path
.is_empty() {
2227 Err(ResolutionFailure
::MalformedGenerics(MalformedGenerics
::MissingType
))
2231 fn strip_generics_from_path_segment(
2233 ) -> Result
<String
, ResolutionFailure
<'
static>> {
2234 let mut stripped_segment
= String
::new();
2235 let mut param_depth
= 0;
2237 let mut latest_generics_chunk
= String
::new();
2242 latest_generics_chunk
.clear();
2243 } else if c
== '
>'
{
2245 if latest_generics_chunk
.contains(" as ") {
2246 // The segment tries to use fully-qualified syntax, which is currently unsupported.
2247 // Give a helpful error message instead of completely ignoring the angle brackets.
2248 return Err(ResolutionFailure
::MalformedGenerics(
2249 MalformedGenerics
::HasFullyQualifiedSyntax
,
2253 if param_depth
== 0 {
2254 stripped_segment
.push(c
);
2256 latest_generics_chunk
.push(c
);
2261 if param_depth
== 0 {
2262 Ok(stripped_segment
)
2264 // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
2265 Err(ResolutionFailure
::MalformedGenerics(MalformedGenerics
::UnbalancedAngleBrackets
))