]> git.proxmox.com Git - rustc.git/blob - src/librustdoc/passes/collect_intra_doc_links.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / src / librustdoc / passes / collect_intra_doc_links.rs
1 //! This module implements [RFC 1946]: Intra-rustdoc-links
2 //!
3 //! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
4
5 use rustc_ast as ast;
6 use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
7 use rustc_errors::{Applicability, DiagnosticBuilder};
8 use rustc_expand::base::SyntaxExtensionKind;
9 use rustc_hir as hir;
10 use rustc_hir::def::{
11 DefKind,
12 Namespace::{self, *},
13 PerNS,
14 };
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};
24
25 use pulldown_cmark::LinkType;
26
27 use std::borrow::Cow;
28 use std::cell::Cell;
29 use std::convert::{TryFrom, TryInto};
30 use std::mem;
31 use std::ops::Range;
32
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;
39
40 mod early;
41 crate use early::IntraLinkCrateLoader;
42
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",
47 };
48
49 fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
50 LinkCollector {
51 cx,
52 mod_ids: Vec::new(),
53 kind_side_channel: Cell::new(None),
54 visited_links: FxHashMap::default(),
55 }
56 .fold_crate(krate)
57 }
58
59 /// Top-level errors emitted by this pass.
60 enum ErrorKind<'a> {
61 Resolve(Box<ResolutionFailure<'a>>),
62 AnchorFailure(AnchorFailure),
63 }
64
65 impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
66 fn from(err: ResolutionFailure<'a>) -> Self {
67 ErrorKind::Resolve(box err)
68 }
69 }
70
71 #[derive(Copy, Clone, Debug, Hash)]
72 enum Res {
73 Def(DefKind, DefId),
74 Primitive(PrimitiveType),
75 }
76
77 type ResolveRes = rustc_hir::def::Res<rustc_ast::NodeId>;
78
79 impl Res {
80 fn descr(self) -> &'static str {
81 match self {
82 Res::Def(kind, id) => ResolveRes::Def(kind, id).descr(),
83 Res::Primitive(_) => "builtin type",
84 }
85 }
86
87 fn article(self) -> &'static str {
88 match self {
89 Res::Def(kind, id) => ResolveRes::Def(kind, id).article(),
90 Res::Primitive(_) => "a",
91 }
92 }
93
94 fn name(self, tcx: TyCtxt<'_>) -> String {
95 match self {
96 Res::Def(_, id) => tcx.item_name(id).to_string(),
97 Res::Primitive(prim) => prim.as_str().to_string(),
98 }
99 }
100
101 fn def_id(self) -> DefId {
102 self.opt_def_id().expect("called def_id() on a primitive")
103 }
104
105 fn opt_def_id(self) -> Option<DefId> {
106 match self {
107 Res::Def(_, id) => Some(id),
108 Res::Primitive(_) => None,
109 }
110 }
111
112 fn as_hir_res(self) -> Option<rustc_hir::def::Res> {
113 match self {
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,
117 }
118 }
119 }
120
121 impl TryFrom<ResolveRes> for Res {
122 type Error = ();
123
124 fn try_from(res: ResolveRes) -> Result<Self, ()> {
125 use rustc_hir::def::Res::*;
126 match res {
127 Def(kind, id) => Ok(Res::Def(kind, id)),
128 PrimTy(prim) => Ok(Res::Primitive(PrimitiveType::from_hir(prim))),
129 // e.g. `#[derive]`
130 NonMacroAttr(..) | Err => Result::Err(()),
131 other => bug!("unrecognized res {:?}", other),
132 }
133 }
134 }
135
136 /// A link failed to resolve.
137 #[derive(Debug)]
138 enum ResolutionFailure<'a> {
139 /// This resolved, but with the wrong namespace.
140 WrongNamespace {
141 /// What the link resolved to.
142 res: Res,
143 /// The expected namespace for the resolution, determined from the link's disambiguator.
144 ///
145 /// E.g., for `[fn@Result]` this is [`Namespace::ValueNS`],
146 /// even though `Result`'s actual namespace is [`Namespace::TypeNS`].
147 expected_ns: Namespace,
148 },
149 /// The link failed to resolve. [`resolution_failure`] should look to see if there's
150 /// a more helpful error that can be given.
151 NotResolved {
152 /// The scope the link was resolved in.
153 module_id: DefId,
154 /// If part of the link resolved, this has the `Res`.
155 ///
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.
159 ///
160 /// In `[std::io::Error::x]`, `x` would be unresolved.
161 unresolved: Cow<'a, str>,
162 },
163 /// This happens when rustdoc can't determine the parent scope for an item.
164 /// It is always a bug in rustdoc.
165 NoParentItem,
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.
169 ///
170 /// This happens when there is no disambiguator and one of the namespaces
171 /// failed to resolve.
172 Dummy,
173 }
174
175 #[derive(Debug)]
176 enum MalformedGenerics {
177 /// This link has unbalanced angle brackets.
178 ///
179 /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
180 UnbalancedAngleBrackets,
181 /// The generics are not attached to a type.
182 ///
183 /// For example, `<T>` should trigger this.
184 ///
185 /// This is detected by checking if the path is empty after the generics are stripped.
186 MissingType,
187 /// The link uses fully-qualified syntax, which is currently unsupported.
188 ///
189 /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
190 ///
191 /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
192 /// angle brackets.
193 HasFullyQualifiedSyntax,
194 /// The link has an invalid path separator.
195 ///
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
198 /// called.
199 ///
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)).
203 ///
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.
207 ///
208 /// For example, `Vec<<T>>` should trigger this.
209 TooManyAngleBrackets,
210 /// The link has empty angle brackets.
211 ///
212 /// For example, `Vec<>` should trigger this.
213 EmptyAngleBrackets,
214 }
215
216 impl ResolutionFailure<'a> {
217 /// This resolved fully (not just partially) but is erroneous for some other reason
218 ///
219 /// Returns the full resolution of the link, if present.
220 fn full_res(&self) -> Option<Res> {
221 match self {
222 Self::WrongNamespace { res, expected_ns: _ } => Some(*res),
223 _ => None,
224 }
225 }
226 }
227
228 enum AnchorFailure {
229 /// User error: `[std#x#y]` is not valid
230 MultipleAnchors,
231 /// The anchor provided by the user conflicts with Rustdoc's generated anchor.
232 ///
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.
238 ///
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),
245 }
246
247 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
248 struct ResolutionInfo {
249 module_id: DefId,
250 dis: Option<Disambiguator>,
251 path_str: String,
252 extra_fragment: Option<String>,
253 }
254
255 #[derive(Clone)]
256 struct DiagnosticInfo<'a> {
257 item: &'a Item,
258 dox: &'a str,
259 ori_link: &'a str,
260 link_range: Range<usize>,
261 }
262
263 #[derive(Clone, Debug, Hash)]
264 struct CachedLink {
265 pub res: (Res, Option<String>),
266 pub side_channel: Option<(DefKind, DefId)>,
267 }
268
269 struct LinkCollector<'a, 'tcx> {
270 cx: &'a mut DocContext<'tcx>,
271 /// A stack of modules used to decide what scope to resolve in.
272 ///
273 /// The last module will be used if the parent scope of the current item is
274 /// unknown.
275 mod_ids: Vec<DefId>,
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>>,
283 }
284
285 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
286 /// Given a full link, parse it as an [enum struct variant].
287 ///
288 /// In particular, this will return an error whenever there aren't three
289 /// full path segments left in the link.
290 ///
291 /// [enum struct variant]: hir::VariantData::Struct
292 fn variant_field(
293 &self,
294 path_str: &'path str,
295 module_id: DefId,
296 ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
297 let tcx = self.cx.tcx;
298 let no_res = || ResolutionFailure::NotResolved {
299 module_id,
300 partial_res: None,
301 unresolved: path_str.into(),
302 };
303
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
307 .next()
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)?;
315 let path = split
316 .next()
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)?;
321 let ty_res = self
322 .cx
323 .enter_resolver(|resolver| {
324 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
325 })
326 .and_then(|(_, res)| res.try_into())
327 .map_err(|()| no_res())?;
328
329 match ty_res {
330 Res::Def(DefKind::Enum, did) => {
331 if tcx
332 .inherent_impls(did)
333 .iter()
334 .flat_map(|imp| tcx.associated_items(*imp).in_definition_order())
335 .any(|item| item.ident.name == variant_name)
336 {
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());
340 }
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) {
344 Ok((
345 ty_res,
346 Some(format!(
347 "variant.{}.field.{}",
348 variant_str, variant_field_name
349 )),
350 ))
351 } else {
352 Err(ResolutionFailure::NotResolved {
353 module_id,
354 partial_res: Some(Res::Def(DefKind::Enum, def.did)),
355 unresolved: variant_field_str.into(),
356 }
357 .into())
358 }
359 }
360 _ => unreachable!(),
361 }
362 }
363 _ => Err(ResolutionFailure::NotResolved {
364 module_id,
365 partial_res: Some(ty_res),
366 unresolved: variant_str.into(),
367 }
368 .into()),
369 }
370 }
371
372 /// Given a primitive type, try to resolve an associated item.
373 fn resolve_primitive_associated_item(
374 &self,
375 prim_ty: PrimitiveType,
376 ns: Namespace,
377 item_name: Symbol,
378 ) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
379 let tcx = self.cx.tcx;
380
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_)
384 .map(|item| {
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",
390 };
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)))
393 })
394 })
395 }
396
397 /// Resolves a string as a macro.
398 ///
399 /// FIXME(jynelson): Can this be unified with `resolve()`?
400 fn resolve_macro(
401 &self,
402 path_str: &'a str,
403 module_id: DefId,
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(
409 &path,
410 None,
411 &ParentScope::module(resolver.graph_root(), resolver),
412 false,
413 false,
414 ) {
415 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
416 return Ok(res.try_into().unwrap());
417 }
418 }
419 if let Some(&res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
420 return Ok(res.try_into().unwrap());
421 }
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)
425 {
426 // don't resolve builtins like `#[derive]`
427 if let Ok(res) = res.try_into() {
428 return Ok(res);
429 }
430 }
431 Err(ResolutionFailure::NotResolved {
432 module_id,
433 partial_res: None,
434 unresolved: path_str.into(),
435 })
436 })
437 }
438
439 /// Convenience wrapper around `resolve_str_path_error`.
440 ///
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| {
446 resolver
447 .resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
448 .and_then(|(_, res)| res.try_into())
449 });
450 debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
451 match result {
452 // resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
453 // manually as bool
454 Err(()) => resolve_primitive(path_str, ns),
455 Ok(res) => Some(res),
456 }
457 }
458
459 /// Resolves a string as a path within a particular namespace. Returns an
460 /// optional URL fragment in the case of variants and methods.
461 fn resolve<'path>(
462 &mut self,
463 path_str: &'path str,
464 ns: Namespace,
465 module_id: DefId,
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) {
469 match res {
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);
476 }
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),
482 ));
483 }
484 return Ok((res, Some(ty.as_str().to_owned())));
485 }
486 _ => return Ok((res, extra_fragment.clone())),
487 }
488 }
489
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
497 .next()
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.
501 .ok_or_else(|| {
502 debug!("found no `::`, assumming {} was correctly not in scope", item_name);
503 ResolutionFailure::NotResolved {
504 module_id,
505 partial_res: None,
506 unresolved: item_str.into(),
507 }
508 })?;
509
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
513 // primitives.
514 resolve_primitive(&path_root, TypeNS)
515 .or_else(|| self.resolve_path(&path_root, TypeNS, module_id))
516 .and_then(|ty_res| {
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)))
522 } else {
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)));
528 }
529 Ok((res, Some(fragment)))
530 };
531 Some(result)
532 })
533 .unwrap_or_else(|| {
534 if ns == Namespace::ValueNS {
535 self.variant_field(path_str, module_id)
536 } else {
537 Err(ResolutionFailure::NotResolved {
538 module_id,
539 partial_res: None,
540 unresolved: path_root.into(),
541 }
542 .into())
543 }
544 })
545 }
546
547 /// Returns:
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(
552 &mut self,
553 root_res: Res,
554 item_name: Symbol,
555 ns: Namespace,
556 module_id: DefId,
557 ) -> Option<(Res, String, Option<(DefKind, DefId)>)> {
558 let tcx = self.cx.tcx;
559
560 match root_res {
561 Res::Primitive(prim) => self.resolve_primitive_associated_item(prim, ns, item_name),
562 Res::Def(
563 DefKind::Struct
564 | DefKind::Union
565 | DefKind::Enum
566 | DefKind::TyAlias
567 | DefKind::ForeignTy,
568 did,
569 ) => {
570 debug!("looking for associated item named {} for item {:?}", item_name, did);
571 // Checks if item_name belongs to `impl SomeItem`
572 let assoc_item = tcx
573 .inherent_impls(did)
574 .iter()
575 .flat_map(|&imp| {
576 tcx.associated_items(imp).find_by_name_and_namespace(
577 tcx,
578 Ident::with_dummy_span(item_name),
579 ns,
580 imp,
581 )
582 })
583 .map(|item| (item.kind, item.def_id))
584 // There should only ever be one associated item that matches from any inherent impl
585 .next()
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)
591 .or_else(|| {
592 let kind =
593 resolve_associated_trait_item(did, module_id, item_name, ns, self.cx);
594 debug!("got associated item kind {:?}", kind);
595 kind
596 });
597
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",
603 };
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.
607 return Some((
608 root_res,
609 format!("{}.{}", out, item_name),
610 Some((kind.as_def_kind(), id)),
611 ));
612 }
613
614 if ns != Namespace::ValueNS {
615 return None;
616 }
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,
623 _ => return None,
624 };
625 let field = if def.is_enum() {
626 def.all_fields().find(|item| item.ident.name == item_name)
627 } else {
628 def.non_enum_variant().fields.iter().find(|item| item.ident.name == item_name)
629 }?;
630 let kind = if def.is_enum() { DefKind::Variant } else { DefKind::Field };
631 Some((
632 root_res,
633 format!(
634 "{}.{}",
635 if def.is_enum() { "variant" } else { "structfield" },
636 field.ident
637 ),
638 Some((kind, field.did)),
639 ))
640 }
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)
644 .map(|item| {
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() {
650 "method"
651 } else {
652 "tymethod"
653 }
654 }
655 };
656
657 let res = Res::Def(item.kind.as_def_kind(), item.def_id);
658 (res, format!("{}.{}", kind, item_name), None)
659 }),
660 _ => None,
661 }
662 }
663
664 /// Used for reporting better errors.
665 ///
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).
670 fn check_full_res(
671 &mut self,
672 ns: Namespace,
673 path_str: &str,
674 module_id: DefId,
675 extra_fragment: &Option<String>,
676 ) -> Option<Res> {
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)
682 }
683 };
684
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,
690 };
691 self.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
692 }
693 }
694
695 /// Look to see if a resolved item has an associated item named `item_name`.
696 ///
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(
701 did: DefId,
702 module: DefId,
703 item_name: Symbol,
704 ns: Namespace,
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.
710
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_| {
716 cx.tcx
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))
720 });
721 // FIXME(#74563): warn about ambiguity
722 debug!("the candidates were {:?}", candidates.clone().collect::<Vec<_>>());
723 candidates.next()
724 }
725
726 /// Given a type, return all traits in scope in `module` implemented by that type.
727 ///
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);
735 resolver
736 .traits_in_scope(None, parent_scope, SyntaxContext::root(), None)
737 .into_iter()
738 .map(|candidate| candidate.def_id)
739 .collect()
740 })
741 });
742
743 let tcx = cx.tcx;
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_);
747
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();
753 trace!(
754 "comparing type {} with kind {:?} against type {:?}",
755 impl_type,
756 impl_type.kind(),
757 type_
758 );
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
763 ty::Adt(def, _) => {
764 debug!("adt def_id: {:?}", def.did);
765 def.did == type_
766 }
767 ty::Foreign(def_id) => *def_id == type_,
768 _ => false,
769 };
770
771 if saw_impl { Some(trait_) } else { None }
772 })
773 });
774 iter.collect()
775 }
776
777 /// Check for resolve collisions between a trait and its derive.
778 ///
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 {
781 matches!(
782 *ns,
783 PerNS {
784 type_ns: Ok((Res::Def(DefKind::Trait, _), _)),
785 macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
786 ..
787 }
788 )
789 }
790
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;
794
795 let parent_node = if item.is_fake() {
796 None
797 } else {
798 find_nearest_parent_module(self.cx.tcx, item.def_id)
799 };
800
801 if parent_node.is_some() {
802 trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
803 }
804
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() {
808 None
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)
811 && matches!(
812 self.cx.tcx.def_kind(self.cx.tcx.parent(item.def_id).unwrap()),
813 DefKind::Variant
814 ))
815 {
816 self.cx.tcx.parent(item.def_id).and_then(|item_id| self.cx.tcx.parent(item_id))
817 } else if matches!(
818 self.cx.tcx.def_kind(item.def_id),
819 DefKind::AssocConst
820 | DefKind::AssocFn
821 | DefKind::AssocTy
822 | DefKind::Variant
823 | DefKind::Field
824 ) {
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 })
833 } else {
834 Some(item.def_id)
835 };
836
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()),
845 _ => None,
846 };
847 debug!("using type_of(): {:?}", name);
848 name
849 } else {
850 let name = self.cx.tcx.opt_item_name(self_id).map(|sym| sym.to_string());
851 debug!("using item_name(): {:?}", name);
852 name
853 }
854 });
855
856 let inner_docs = item.inner_docs(self.cx.tcx);
857
858 if item.is_mod() && inner_docs {
859 self.mod_ids.push(item.def_id);
860 }
861
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);
868
869 let (krate, parent_node) = if let Some(id) = parent_module {
870 (id.krate, Some(id))
871 } else {
872 (item.def_id.krate, parent_node)
873 };
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);
880 }
881 }
882 }
883
884 Some(if item.is_mod() {
885 if !inner_docs {
886 self.mod_ids.push(item.def_id);
887 }
888
889 let ret = self.fold_item_recur(item);
890 self.mod_ids.pop();
891 ret
892 } else {
893 self.fold_item_recur(item)
894 })
895 }
896 }
897
898 enum PreprocessingError<'a> {
899 Anchor(AnchorFailure),
900 Disambiguator(Range<usize>, String),
901 Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>),
902 }
903
904 impl From<AnchorFailure> for PreprocessingError<'_> {
905 fn from(err: AnchorFailure) -> Self {
906 Self::Anchor(err)
907 }
908 }
909
910 struct PreprocessingInfo {
911 path_str: String,
912 disambiguator: Option<Disambiguator>,
913 extra_fragment: Option<String>,
914 link_text: String,
915 }
916
917 /// Returns:
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
921 ///
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() {
928 return None;
929 }
930
931 // Bail early for real links.
932 if ori_link.link.contains('/') {
933 return None;
934 }
935
936 let stripped = ori_link.link.replace("`", "");
937 let mut parts = stripped.split('#');
938
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!
942 return None;
943 }
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()));
948 }
949
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)));
961 } else {
962 return None;
963 }
964 }
965 };
966
967 if should_ignore_link(path_str) {
968 return None;
969 }
970
971 // We stripped `()` and `!` when parsing the disambiguator.
972 // Add them back to be displayed, but not prefix disambiguators.
973 let link_text =
974 disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned());
975
976 // Strip generics from the path.
977 let path_str = if path_str.contains(['<', '>'].as_slice()) {
978 match strip_generics_from_path(&path_str) {
979 Ok(path) => path,
980 Err(err_kind) => {
981 debug!("link has malformed generics: {}", path_str);
982 return Some(Err(PreprocessingError::Resolution(
983 err_kind,
984 path_str.to_owned(),
985 disambiguator,
986 )));
987 }
988 }
989 } else {
990 path_str.to_owned()
991 };
992
993 // Sanity check to make sure we don't have any angle brackets after stripping generics.
994 assert!(!path_str.contains(['<', '>'].as_slice()));
995
996 // The link is not an intra-doc link if it still contains spaces after stripping generics.
997 if path_str.contains(' ') {
998 return None;
999 }
1000
1001 Some(Ok(PreprocessingInfo {
1002 path_str,
1003 disambiguator,
1004 extra_fragment: extra_fragment.map(String::from),
1005 link_text,
1006 }))
1007 }
1008
1009 impl LinkCollector<'_, '_> {
1010 /// This is the entry point for resolving an intra-doc link.
1011 ///
1012 /// FIXME(jynelson): this is way too many arguments
1013 fn resolve_link(
1014 &mut self,
1015 item: &Item,
1016 dox: &str,
1017 self_name: &Option<String>,
1018 parent_node: Option<DefId>,
1019 krate: CrateNum,
1020 ori_link: MarkdownLink,
1021 ) -> Option<ItemLink> {
1022 trace!("considering link '{}'", ori_link.link);
1023
1024 let diag_info = DiagnosticInfo {
1025 item,
1026 dox,
1027 ori_link: &ori_link.link,
1028 link_range: ori_link.range.clone(),
1029 };
1030
1031 let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
1032 match preprocess_link(&ori_link)? {
1033 Ok(x) => x,
1034 Err(err) => {
1035 match err {
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)
1039 }
1040 PreprocessingError::Resolution(err, path_str, disambiguator) => {
1041 resolution_failure(
1042 self,
1043 diag_info,
1044 &path_str,
1045 disambiguator,
1046 smallvec![err],
1047 );
1048 }
1049 }
1050 return None;
1051 }
1052 };
1053 let mut path_str = &*path_str;
1054
1055 let inner_docs = item.inner_docs(self.cx.tcx);
1056
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.
1068 let base_node =
1069 if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
1070
1071 let mut module_id = if let Some(id) = base_node {
1072 id
1073 } else {
1074 // This is a bug.
1075 debug!("attempting to resolve item without parent module: {}", path_str);
1076 resolution_failure(
1077 self,
1078 diag_info,
1079 path_str,
1080 disambiguator,
1081 smallvec![ResolutionFailure::NoParentItem],
1082 );
1083 return None;
1084 };
1085
1086 let resolved_self;
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 {
1092 if is_lone_self {
1093 path_str = name;
1094 } else {
1095 resolved_self = format!("{}::{}", name, &path_str[6..]);
1096 path_str = &resolved_self;
1097 }
1098 }
1099 } else if path_str.starts_with("crate::") || is_lone_crate {
1100 use rustc_span::def_id::CRATE_DEF_INDEX;
1101
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.
1108 if is_lone_crate {
1109 path_str = "self";
1110 } else {
1111 resolved_self = format!("self::{}", &path_str["crate::".len()..]);
1112 path_str = &resolved_self;
1113 }
1114 module_id = DefId { krate, index: CRATE_DEF_INDEX };
1115 }
1116
1117 let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
1118 ResolutionInfo {
1119 module_id,
1120 dis: disambiguator,
1121 path_str: path_str.to_owned(),
1122 extra_fragment: extra_fragment.map(String::from),
1123 },
1124 diag_info.clone(), // this struct should really be Copy, but Range is not :(
1125 matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
1126 )?;
1127
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?
1131 if matches!(
1132 disambiguator,
1133 None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
1134 ) && !matches!(res, Res::Primitive(_))
1135 {
1136 if let Some(prim) = resolve_primitive(path_str, TypeNS) {
1137 // `prim@char`
1138 if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1139 if fragment.is_some() {
1140 anchor_failure(
1141 self.cx,
1142 diag_info,
1143 AnchorFailure::RustdocAnchorConflict(prim),
1144 );
1145 return None;
1146 }
1147 res = prim;
1148 fragment = Some(prim.name(self.cx.tcx));
1149 } else {
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);
1153 return None;
1154 }
1155 }
1156 }
1157
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| {
1162 let note = format!(
1163 "this link resolved to {} {}, which is not {} {}",
1164 resolved.article(),
1165 resolved.descr(),
1166 specified.article(),
1167 specified.descr()
1168 );
1169 diag.note(&note);
1170 suggest_disambiguator(resolved, diag, path_str, dox, sp, &ori_link.range);
1171 };
1172 report_diagnostic(self.cx.tcx, BROKEN_INTRA_DOC_LINKS, &msg, &diag_info, callback);
1173 };
1174
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);
1178
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
1189 | (_, None)
1190 // All of these are valid, so do nothing
1191 => {}
1192 (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
1193 (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
1194 report_mismatch(specified, Disambiguator::Kind(kind));
1195 return None;
1196 }
1197 }
1198
1199 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
1200 if let Some((src_id, dst_id)) = id
1201 .as_local()
1202 .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
1203 {
1204 use rustc_hir::def_id::LOCAL_CRATE;
1205
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);
1208
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)
1211 {
1212 privacy_error(self.cx, &diag_info, &path_str);
1213 }
1214 }
1215
1216 Some(())
1217 };
1218
1219 match res {
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.
1230 verify(kind, id)?;
1231
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
1237 {
1238 let span = super::source_span_for_markdown_range(
1239 self.cx.tcx,
1240 dox,
1241 &ori_link.range,
1242 &item.attrs,
1243 )
1244 .unwrap_or_else(|| item.attr_span(self.cx.tcx));
1245
1246 rustc_session::parse::feature_err(
1247 &self.cx.tcx.sess.parse_sess,
1248 sym::intra_doc_pointers,
1249 span,
1250 "linking to associated items of raw pointers is experimental",
1251 )
1252 .note("rustdoc does not allow disambiguating between `*const` and `*mut`, and pointers are unstable until it does")
1253 .emit();
1254 }
1255 } else {
1256 match disambiguator {
1257 Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {}
1258 Some(other) => {
1259 report_mismatch(other, Disambiguator::Primitive);
1260 return None;
1261 }
1262 }
1263 }
1264
1265 Some(ItemLink { link: ori_link.link, link_text, did: None, fragment })
1266 }
1267 Res::Def(kind, id) => {
1268 verify(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 })
1271 }
1272 }
1273 }
1274
1275 fn resolve_with_disambiguator_cached(
1276 &mut self,
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) {
1283 match cached {
1284 Some(cached) => {
1285 self.kind_side_channel.set(cached.side_channel.clone());
1286 return Some(cached.res.clone());
1287 }
1288 None if cache_resolution_failure => return None,
1289 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.
1293 }
1294 }
1295 }
1296
1297 let res = self.resolve_with_disambiguator(&key, diag);
1298
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(
1303 key,
1304 Some(CachedLink {
1305 res: res.clone(),
1306 side_channel: self.kind_side_channel.clone().into_inner(),
1307 }),
1308 );
1309
1310 Some(res)
1311 } else {
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);
1316 }
1317
1318 None
1319 }
1320 }
1321
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(
1325 &mut self,
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;
1333
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] {
1345 if let Some(res) =
1346 self.check_full_res(new_ns, path_str, base_node, extra_fragment)
1347 {
1348 kind = ResolutionFailure::WrongNamespace { res, expected_ns };
1349 break;
1350 }
1351 }
1352 }
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.
1357 None
1358 }
1359 Err(ErrorKind::AnchorFailure(msg)) => {
1360 anchor_failure(self.cx, diag, msg);
1361 None
1362 }
1363 }
1364 }
1365 None => {
1366 // Try everything!
1367 let mut candidates = PerNS {
1368 macro_ns: self
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) {
1372 Ok(res) => {
1373 debug!("got res in TypeNS: {:?}", res);
1374 Ok(res)
1375 }
1376 Err(ErrorKind::AnchorFailure(msg)) => {
1377 anchor_failure(self.cx, diag, msg);
1378 return None;
1379 }
1380 Err(ErrorKind::Resolve(box kind)) => Err(kind),
1381 },
1382 value_ns: match self.resolve(path_str, ValueNS, base_node, extra_fragment) {
1383 Ok(res) => Ok(res),
1384 Err(ErrorKind::AnchorFailure(msg)) => {
1385 anchor_failure(self.cx, diag, msg);
1386 return None;
1387 }
1388 Err(ErrorKind::Resolve(box kind)) => Err(kind),
1389 }
1390 .and_then(|(res, fragment)| {
1391 // Constructors are picked up in the type namespace.
1392 match res {
1393 Res::Def(DefKind::Ctor(..), _) => {
1394 Err(ResolutionFailure::WrongNamespace { res, expected_ns: TypeNS })
1395 }
1396 _ => {
1397 match (fragment, extra_fragment.clone()) {
1398 (Some(fragment), Some(_)) => {
1399 // Shouldn't happen but who knows?
1400 Ok((res, Some(fragment)))
1401 }
1402 (fragment, None) | (None, fragment) => Ok((res, fragment)),
1403 }
1404 }
1405 }
1406 }),
1407 };
1408
1409 let len = candidates.iter().filter(|res| res.is_ok()).count();
1410
1411 if len == 0 {
1412 resolution_failure(
1413 self,
1414 diag,
1415 path_str,
1416 disambiguator,
1417 candidates.into_iter().filter_map(|res| res.err()).collect(),
1418 );
1419 // this could just be a normal link
1420 return None;
1421 }
1422
1423 if len == 1 {
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())
1427 } else {
1428 if is_derive_trait_collision(&candidates) {
1429 candidates.macro_ns = Err(ResolutionFailure::Dummy);
1430 }
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());
1434 None
1435 }
1436 }
1437 Some(MacroNS) => {
1438 match self.resolve_macro(path_str, base_node) {
1439 Ok(res) => Some((res, extra_fragment.clone())),
1440 Err(mut kind) => {
1441 // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible.
1442 for &ns in &[TypeNS, ValueNS] {
1443 if let Some(res) =
1444 self.check_full_res(ns, path_str, base_node, extra_fragment)
1445 {
1446 kind =
1447 ResolutionFailure::WrongNamespace { res, expected_ns: MacroNS };
1448 break;
1449 }
1450 }
1451 resolution_failure(self, diag, path_str, disambiguator, smallvec![kind]);
1452 None
1453 }
1454 }
1455 }
1456 }
1457 }
1458 }
1459
1460 /// Get the section of a link between the backticks,
1461 /// or the whole link if there aren't any backticks.
1462 ///
1463 /// For example:
1464 ///
1465 /// ```text
1466 /// [`Foo`]
1467 /// ^^^
1468 /// ```
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
1472 .link
1473 .bytes()
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)
1479 }
1480
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.
1484 ///
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)))
1489 }
1490
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)))
1495 }
1496
1497 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1498 /// Disambiguators for a link.
1499 enum Disambiguator {
1500 /// `prim@`
1501 ///
1502 /// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
1503 Primitive,
1504 /// `struct@` or `f()`
1505 Kind(DefKind),
1506 /// `type@`
1507 Namespace(Namespace),
1508 }
1509
1510 impl Disambiguator {
1511 /// The text that should be displayed when the path is rendered as HTML.
1512 ///
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 {
1515 match self {
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(),
1520 }
1521 }
1522
1523 /// Given a link, parse and return `(disambiguator, path_str)`.
1524 ///
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};
1530
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)),
1548 };
1549 Ok(Some((d, &rest[1..])))
1550 } else {
1551 let suffixes = [
1552 ("!()", DefKind::Macro(MacroKind::Bang)),
1553 ("()", DefKind::Fn),
1554 ("!", DefKind::Macro(MacroKind::Bang)),
1555 ];
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)));
1561 }
1562 }
1563 }
1564 Ok(None)
1565 }
1566 }
1567
1568 fn from_res(res: Res) -> Self {
1569 match res {
1570 Res::Def(kind, _) => Disambiguator::Kind(kind),
1571 Res::Primitive(_) => Disambiguator::Primitive,
1572 }
1573 }
1574
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"),
1581 };
1582 if kind == DefKind::Macro(MacroKind::Bang) {
1583 return Suggestion::Macro;
1584 } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
1585 return Suggestion::Function;
1586 }
1587
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 => {
1595 "const"
1596 }
1597 DefKind::Static => "static",
1598 DefKind::Macro(MacroKind::Derive) => "derive",
1599 // Now handle things that don't have a specific disambiguator
1600 _ => match kind
1601 .ns()
1602 .expect("tried to calculate a disambiguator for a def without a namespace?")
1603 {
1604 Namespace::TypeNS => "type",
1605 Namespace::ValueNS => "value",
1606 Namespace::MacroNS => "macro",
1607 },
1608 };
1609
1610 Suggestion::Prefix(prefix)
1611 }
1612
1613 fn ns(self) -> Namespace {
1614 match self {
1615 Self::Namespace(n) => n,
1616 Self::Kind(k) => {
1617 k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
1618 }
1619 Self::Primitive => TypeNS,
1620 }
1621 }
1622
1623 fn article(self) -> &'static str {
1624 match self {
1625 Self::Namespace(_) => panic!("article() doesn't make sense for namespaces"),
1626 Self::Kind(k) => k.article(),
1627 Self::Primitive => "a",
1628 }
1629 }
1630
1631 fn descr(self) -> &'static str {
1632 match self {
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",
1638 }
1639 }
1640 }
1641
1642 /// A suggestion to show in a diagnostic.
1643 enum Suggestion {
1644 /// `struct@`
1645 Prefix(&'static str),
1646 /// `f()`
1647 Function,
1648 /// `m!`
1649 Macro,
1650 }
1651
1652 impl Suggestion {
1653 fn descr(&self) -> Cow<'static, str> {
1654 match self {
1655 Self::Prefix(x) => format!("prefix with `{}@`", x).into(),
1656 Self::Function => "add parentheses".into(),
1657 Self::Macro => "add an exclamation mark".into(),
1658 }
1659 }
1660
1661 fn as_help(&self, path_str: &str) -> String {
1662 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1663 match self {
1664 Self::Prefix(prefix) => format!("{}@{}", prefix, path_str),
1665 Self::Function => format!("{}()", path_str),
1666 Self::Macro => format!("{}!", path_str),
1667 }
1668 }
1669 }
1670
1671 /// Reports a diagnostic for an intra-doc link.
1672 ///
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.
1676 ///
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
1680 /// to it.
1681 fn report_diagnostic(
1682 tcx: TyCtxt<'_>,
1683 lint: &'static Lint,
1684 msg: &str,
1685 DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>,
1686 decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
1687 ) {
1688 let hir_id = match DocContext::as_local_hir_id(tcx, item.def_id) {
1689 Some(hir_id) => hir_id,
1690 None => {
1691 // If non-local, no need to check anything.
1692 info!("ignoring warning from parent crate: {}", msg);
1693 return;
1694 }
1695 };
1696
1697 let sp = item.attr_span(tcx);
1698
1699 tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
1700 let mut diag = lint.build(msg);
1701
1702 let span = super::source_span_for_markdown_range(tcx, dox, link_range, &item.attrs);
1703
1704 if let Some(sp) = span {
1705 diag.set_span(sp);
1706 } else {
1707 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1708 // ^ ~~~~
1709 // | link_range
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("");
1713
1714 // Print the line containing the `link_range` and manually mark it with '^'s.
1715 diag.note(&format!(
1716 "the link appears in this line:\n\n{line}\n\
1717 {indicator: <before$}{indicator:^<found$}",
1718 line = line,
1719 indicator = "",
1720 before = link_range.start - last_new_line_offset,
1721 found = link_range.len(),
1722 ));
1723 }
1724
1725 decorate(&mut diag, span);
1726
1727 diag.emit();
1728 });
1729 }
1730
1731 /// Reports a link that failed to resolve.
1732 ///
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<'_>,
1739 path_str: &str,
1740 disambiguator: Option<Disambiguator>,
1741 kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
1742 ) {
1743 let tcx = collector.cx.tcx;
1744 report_diagnostic(
1745 tcx,
1746 BROKEN_INTRA_DOC_LINKS,
1747 &format!("unresolved link to `{}`", path_str),
1748 &diag_info,
1749 |diag, sp| {
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);
1753 format!(
1754 "`{}` is {} {}, not a module or type, and cannot have associated items",
1755 name,
1756 res.article(),
1757 res.descr()
1758 )
1759 };
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) {
1765 continue;
1766 }
1767 variants_seen.push(variant);
1768
1769 if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
1770 &mut failure
1771 {
1772 use DefKind::*;
1773
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)))
1780 }
1781
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;
1785 'outer: loop {
1786 let (start, end) = if let Some(x) = split(name) {
1787 x
1788 } else {
1789 // avoid bug that marked [Quux::Z] as missing Z, not Quux
1790 if partial_res.is_none() {
1791 *unresolved = name.into();
1792 }
1793 break;
1794 };
1795 name = start;
1796 for &ns in &[TypeNS, ValueNS, MacroNS] {
1797 if let Some(res) =
1798 collector.check_full_res(ns, &start, module_id, &None)
1799 {
1800 debug!("found partial_res={:?}", res);
1801 *partial_res = Some(res);
1802 *unresolved = end.into();
1803 break 'outer;
1804 }
1805 }
1806 *unresolved = end.into();
1807 }
1808
1809 let last_found_module = match *partial_res {
1810 Some(Res::Def(DefKind::Mod, id)) => Some(id),
1811 None => Some(module_id),
1812 _ => None,
1813 };
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)
1820 } else {
1821 // None of the link resolved; e.g. `Notimported`
1822 format!("no item named `{}` in scope", unresolved)
1823 };
1824 if let Some(span) = sp {
1825 diag.span_label(span, &note);
1826 } else {
1827 diag.note(&note);
1828 }
1829
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 `\]`"#);
1835 }
1836
1837 continue;
1838 }
1839
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,
1846 };
1847 let path_description = if let Some(kind) = kind {
1848 match kind {
1849 Mod | ForeignMod => "inner item",
1850 Struct => "field or associated item",
1851 Enum | Union => "variant or associated item",
1852 Variant
1853 | Field
1854 | Closure
1855 | Generator
1856 | AssocTy
1857 | AssocConst
1858 | AssocFn
1859 | Fn
1860 | Macro(_)
1861 | Const
1862 | ConstParam
1863 | ExternCrate
1864 | Use
1865 | LifetimeParam
1866 | Ctor(_, _)
1867 | AnonConst => {
1868 let note = assoc_item_not_allowed(res);
1869 if let Some(span) = sp {
1870 diag.span_label(span, &note);
1871 } else {
1872 diag.note(&note);
1873 }
1874 return;
1875 }
1876 Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1877 | Static => "associated item",
1878 Impl | GlobalAsm => unreachable!("not a path"),
1879 }
1880 } else {
1881 "associated item"
1882 };
1883 let note = format!(
1884 "the {} `{}` has no {} named `{}`",
1885 res.descr(),
1886 name,
1887 disambiguator.map_or(path_description, |d| d.descr()),
1888 unresolved,
1889 );
1890 if let Some(span) = sp {
1891 diag.span_label(span, &note);
1892 } else {
1893 diag.note(&note);
1894 }
1895
1896 continue;
1897 }
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(
1905 disambiguator,
1906 diag,
1907 path_str,
1908 diag_info.dox,
1909 sp,
1910 &diag_info.link_range,
1911 )
1912 }
1913
1914 format!(
1915 "this link resolves to {}, which is not in the {} namespace",
1916 item(res),
1917 expected_ns.descr()
1918 )
1919 }
1920 ResolutionFailure::NoParentItem => {
1921 diag.level = rustc_errors::Level::Bug;
1922 "all intra-doc links should have a parent item".to_owned()
1923 }
1924 ResolutionFailure::MalformedGenerics(variant) => match variant {
1925 MalformedGenerics::UnbalancedAngleBrackets => {
1926 String::from("unbalanced angle brackets")
1927 }
1928 MalformedGenerics::MissingType => {
1929 String::from("missing type for generic parameters")
1930 }
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")
1934 }
1935 MalformedGenerics::InvalidPathSeparator => {
1936 String::from("has invalid path separator")
1937 }
1938 MalformedGenerics::TooManyAngleBrackets => {
1939 String::from("too many angle brackets")
1940 }
1941 MalformedGenerics::EmptyAngleBrackets => {
1942 String::from("empty angle brackets")
1943 }
1944 },
1945 };
1946 if let Some(span) = sp {
1947 diag.span_label(span, &note);
1948 } else {
1949 diag.note(&note);
1950 }
1951 }
1952 },
1953 );
1954 }
1955
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)
1961 }
1962 AnchorFailure::RustdocAnchorConflict(res) => (
1963 format!(
1964 "`{}` contains an anchor, but links to {kind}s are already anchored",
1965 diag_info.ori_link,
1966 kind = res.descr(),
1967 ),
1968 0,
1969 ),
1970 };
1971
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)
1976 {
1977 sp = sp.with_lo(sp.lo() + rustc_span::BytePos(fragment_offset as _));
1978 }
1979 diag.span_label(sp, "invalid anchor");
1980 }
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");
1984 }
1985 });
1986 }
1987
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>,
1993 msg: &str,
1994 ) {
1995 diag_info.link_range = disambiguator_range;
1996 report_diagnostic(cx.tcx, BROKEN_INTRA_DOC_LINKS, msg, &diag_info, |_diag, _sp| {});
1997 }
1998
1999 /// Report an ambiguity error, where there were multiple possible resolutions.
2000 fn ambiguity_error(
2001 cx: &DocContext<'_>,
2002 diag_info: DiagnosticInfo<'_>,
2003 path_str: &str,
2004 candidates: Vec<Res>,
2005 ) {
2006 let mut msg = format!("`{}` is ", path_str);
2007
2008 match candidates.as_slice() {
2009 [first_def, second_def] => {
2010 msg += &format!(
2011 "both {} {} and {} {}",
2012 first_def.article(),
2013 first_def.descr(),
2014 second_def.article(),
2015 second_def.descr(),
2016 );
2017 }
2018 _ => {
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());
2023 } else {
2024 msg += &format!("and {} {}", res.article(), res.descr());
2025 }
2026 }
2027 }
2028 }
2029
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");
2033 } else {
2034 diag.note("ambiguous link");
2035 }
2036
2037 for res in candidates {
2038 let disambiguator = Disambiguator::from_res(res);
2039 suggest_disambiguator(
2040 disambiguator,
2041 diag,
2042 path_str,
2043 diag_info.dox,
2044 sp,
2045 &diag_info.link_range,
2046 );
2047 }
2048 });
2049 }
2050
2051 /// In case of an ambiguity or mismatched disambiguator, suggest the correct
2052 /// disambiguator.
2053 fn suggest_disambiguator(
2054 disambiguator: Disambiguator,
2055 diag: &mut DiagnosticBuilder<'_>,
2056 path_str: &str,
2057 dox: &str,
2058 sp: Option<rustc_span::Span>,
2059 link_range: &Range<usize>,
2060 ) {
2061 let suggestion = disambiguator.suggestion();
2062 let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
2063
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))
2067 } else {
2068 suggestion.as_help(path_str)
2069 };
2070
2071 diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect);
2072 } else {
2073 diag.help(&format!("{}: {}", help, suggestion.as_help(path_str)));
2074 }
2075 }
2076
2077 /// Report a link from a public item to a private one.
2078 fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: &str) {
2079 let sym;
2080 let item_name = match diag_info.item.name {
2081 Some(name) => {
2082 sym = name.as_str();
2083 &*sym
2084 }
2085 None => "<unknown>",
2086 };
2087 let msg =
2088 format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
2089
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");
2093 }
2094
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"
2097 } else {
2098 "this link will resolve properly if you pass `--document-private-items`"
2099 };
2100 diag.note(note_msg);
2101 });
2102 }
2103
2104 /// Given an enum variant's res, return the res of its enum and the associated fragment.
2105 fn handle_variant(
2106 cx: &DocContext<'_>,
2107 res: Res,
2108 extra_fragment: &Option<String>,
2109 ) -> Result<(Res, Option<String>), ErrorKind<'static>> {
2110 use rustc_middle::ty::DefIdTree;
2111
2112 if extra_fragment.is_some() {
2113 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res)));
2114 }
2115 cx.tcx
2116 .parent(res.def_id())
2117 .map(|parent| {
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)))
2121 })
2122 .ok_or_else(|| ResolutionFailure::NoParentItem.into())
2123 }
2124
2125 /// Resolve a primitive type or value.
2126 fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
2127 if ns != TypeNS {
2128 return None;
2129 }
2130 use PrimitiveType::*;
2131 let prim = match path_str {
2132 "isize" => Isize,
2133 "i8" => I8,
2134 "i16" => I16,
2135 "i32" => I32,
2136 "i64" => I64,
2137 "i128" => I128,
2138 "usize" => Usize,
2139 "u8" => U8,
2140 "u16" => U16,
2141 "u32" => U32,
2142 "u64" => U64,
2143 "u128" => U128,
2144 "f32" => F32,
2145 "f64" => F64,
2146 "char" => Char,
2147 "bool" | "true" | "false" => Bool,
2148 "str" | "&str" => Str,
2149 // See #80181 for why these don't have symbols associated.
2150 "slice" => Slice,
2151 "array" => Array,
2152 "tuple" => Tuple,
2153 "unit" => Unit,
2154 "pointer" | "*const" | "*mut" => RawPointer,
2155 "reference" | "&" | "&mut" => Reference,
2156 "fn" => Fn,
2157 "never" | "!" => Never,
2158 _ => return None,
2159 };
2160 debug!("resolved primitives {:?}", prim);
2161 Some(Res::Primitive(prim))
2162 }
2163
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();
2168
2169 while let Some(chr) = path.next() {
2170 match chr {
2171 ':' => {
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);
2177 }
2178 } else {
2179 return Err(ResolutionFailure::MalformedGenerics(
2180 MalformedGenerics::InvalidPathSeparator,
2181 ));
2182 }
2183 }
2184 '<' => {
2185 segment.push(chr);
2186
2187 match path.next() {
2188 Some('<') => {
2189 return Err(ResolutionFailure::MalformedGenerics(
2190 MalformedGenerics::TooManyAngleBrackets,
2191 ));
2192 }
2193 Some('>') => {
2194 return Err(ResolutionFailure::MalformedGenerics(
2195 MalformedGenerics::EmptyAngleBrackets,
2196 ));
2197 }
2198 Some(chr) => {
2199 segment.push(chr);
2200
2201 while let Some(chr) = path.next_if(|c| *c != '>') {
2202 segment.push(chr);
2203 }
2204 }
2205 None => break,
2206 }
2207 }
2208 _ => segment.push(chr),
2209 }
2210 trace!("raw segment: {:?}", segment);
2211 }
2212
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);
2217 }
2218 }
2219
2220 debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments);
2221
2222 let stripped_path = stripped_segments.join("::");
2223
2224 if !stripped_path.is_empty() {
2225 Ok(stripped_path)
2226 } else {
2227 Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::MissingType))
2228 }
2229 }
2230
2231 fn strip_generics_from_path_segment(
2232 segment: Vec<char>,
2233 ) -> Result<String, ResolutionFailure<'static>> {
2234 let mut stripped_segment = String::new();
2235 let mut param_depth = 0;
2236
2237 let mut latest_generics_chunk = String::new();
2238
2239 for c in segment {
2240 if c == '<' {
2241 param_depth += 1;
2242 latest_generics_chunk.clear();
2243 } else if c == '>' {
2244 param_depth -= 1;
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,
2250 ));
2251 }
2252 } else {
2253 if param_depth == 0 {
2254 stripped_segment.push(c);
2255 } else {
2256 latest_generics_chunk.push(c);
2257 }
2258 }
2259 }
2260
2261 if param_depth == 0 {
2262 Ok(stripped_segment)
2263 } else {
2264 // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
2265 Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::UnbalancedAngleBrackets))
2266 }
2267 }