]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_intra_doc_links.rs
Add find_map_relevant_impl
[rust.git] / src / librustdoc / passes / collect_intra_doc_links.rs
1 use rustc_ast as ast;
2 use rustc_data_structures::stable_set::FxHashSet;
3 use rustc_errors::{Applicability, DiagnosticBuilder};
4 use rustc_expand::base::SyntaxExtensionKind;
5 use rustc_hir as hir;
6 use rustc_hir::def::{
7     DefKind,
8     Namespace::{self, *},
9     PerNS, Res,
10 };
11 use rustc_hir::def_id::{CrateNum, DefId};
12 use rustc_middle::ty;
13 use rustc_resolve::ParentScope;
14 use rustc_session::lint::{
15     builtin::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS},
16     Lint,
17 };
18 use rustc_span::hygiene::MacroKind;
19 use rustc_span::symbol::Ident;
20 use rustc_span::symbol::Symbol;
21 use rustc_span::DUMMY_SP;
22 use smallvec::{smallvec, SmallVec};
23
24 use std::borrow::Cow;
25 use std::cell::Cell;
26 use std::ops::Range;
27
28 use crate::clean::*;
29 use crate::core::DocContext;
30 use crate::fold::DocFolder;
31 use crate::html::markdown::markdown_links;
32 use crate::passes::Pass;
33
34 use super::span_of_attrs;
35
36 pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
37     name: "collect-intra-doc-links",
38     run: collect_intra_doc_links,
39     description: "reads a crate's documentation to resolve intra-doc-links",
40 };
41
42 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
43     let mut coll = LinkCollector::new(cx);
44     coll.fold_crate(krate)
45 }
46
47 enum ErrorKind<'a> {
48     Resolve(Box<ResolutionFailure<'a>>),
49     AnchorFailure(AnchorFailure),
50 }
51
52 impl<'a> From<ResolutionFailure<'a>> for ErrorKind<'a> {
53     fn from(err: ResolutionFailure<'a>) -> Self {
54         ErrorKind::Resolve(box err)
55     }
56 }
57
58 #[derive(Debug)]
59 enum ResolutionFailure<'a> {
60     /// This resolved, but with the wrong namespace.
61     /// `Namespace` is the expected namespace (as opposed to the actual).
62     WrongNamespace(Res, Namespace),
63     /// The link failed to resolve. `resolution_failure` should look to see if there's
64     /// a more helpful error that can be given.
65     NotResolved { module_id: DefId, partial_res: Option<Res>, unresolved: Cow<'a, str> },
66     /// should not ever happen
67     NoParentItem,
68     /// used to communicate that this should be ignored, but shouldn't be reported to the user
69     Dummy,
70 }
71
72 impl ResolutionFailure<'a> {
73     // This resolved fully (not just partially) but is erroneous for some other reason
74     fn full_res(&self) -> Option<Res> {
75         match self {
76             Self::WrongNamespace(res, _) => Some(*res),
77             _ => None,
78         }
79     }
80 }
81
82 enum AnchorFailure {
83     MultipleAnchors,
84     RustdocAnchorConflict(Res),
85 }
86
87 struct LinkCollector<'a, 'tcx> {
88     cx: &'a DocContext<'tcx>,
89     // NOTE: this may not necessarily be a module in the current crate
90     mod_ids: Vec<DefId>,
91     /// This is used to store the kind of associated items,
92     /// because `clean` and the disambiguator code expect them to be different.
93     /// See the code for associated items on inherent impls for details.
94     kind_side_channel: Cell<Option<(DefKind, DefId)>>,
95 }
96
97 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
98     fn new(cx: &'a DocContext<'tcx>) -> Self {
99         LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
100     }
101
102     fn variant_field(
103         &self,
104         path_str: &'path str,
105         current_item: &Option<String>,
106         module_id: DefId,
107     ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
108         let cx = self.cx;
109         let no_res = || ResolutionFailure::NotResolved {
110             module_id,
111             partial_res: None,
112             unresolved: path_str.into(),
113         };
114
115         debug!("looking for enum variant {}", path_str);
116         let mut split = path_str.rsplitn(3, "::");
117         let (variant_field_str, variant_field_name) = split
118             .next()
119             .map(|f| (f, Symbol::intern(f)))
120             .expect("fold_item should ensure link is non-empty");
121         let (variant_str, variant_name) =
122             // we're not sure this is a variant at all, so use the full string
123             // If there's no second component, the link looks like `[path]`.
124             // So there's no partial res and we should say the whole link failed to resolve.
125             split.next().map(|f| (f, Symbol::intern(f))).ok_or_else(no_res)?;
126         let path = split
127             .next()
128             .map(|f| {
129                 if f == "self" || f == "Self" {
130                     if let Some(name) = current_item.as_ref() {
131                         return name.clone();
132                     }
133                 }
134                 f.to_owned()
135             })
136             // If there's no third component, we saw `[a::b]` before and it failed to resolve.
137             // So there's no partial res.
138             .ok_or_else(no_res)?;
139         let ty_res = cx
140             .enter_resolver(|resolver| {
141                 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
142             })
143             .map(|(_, res)| res)
144             .unwrap_or(Res::Err);
145         if let Res::Err = ty_res {
146             return Err(no_res().into());
147         }
148         let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
149         match ty_res {
150             Res::Def(DefKind::Enum, did) => {
151                 if cx
152                     .tcx
153                     .inherent_impls(did)
154                     .iter()
155                     .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
156                     .any(|item| item.ident.name == variant_name)
157                 {
158                     // This is just to let `fold_item` know that this shouldn't be considered;
159                     // it's a bug for the error to make it to the user
160                     return Err(ResolutionFailure::Dummy.into());
161                 }
162                 match cx.tcx.type_of(did).kind() {
163                     ty::Adt(def, _) if def.is_enum() => {
164                         if def.all_fields().any(|item| item.ident.name == variant_field_name) {
165                             Ok((
166                                 ty_res,
167                                 Some(format!(
168                                     "variant.{}.field.{}",
169                                     variant_str, variant_field_name
170                                 )),
171                             ))
172                         } else {
173                             Err(ResolutionFailure::NotResolved {
174                                 module_id,
175                                 partial_res: Some(Res::Def(DefKind::Enum, def.did)),
176                                 unresolved: variant_field_str.into(),
177                             }
178                             .into())
179                         }
180                     }
181                     _ => unreachable!(),
182                 }
183             }
184             _ => Err(ResolutionFailure::NotResolved {
185                 module_id,
186                 partial_res: Some(ty_res),
187                 unresolved: variant_str.into(),
188             }
189             .into()),
190         }
191     }
192
193     /// Resolves a string as a macro.
194     fn macro_resolve(
195         &self,
196         path_str: &'a str,
197         module_id: DefId,
198     ) -> Result<Res, ResolutionFailure<'a>> {
199         let cx = self.cx;
200         let path = ast::Path::from_ident(Ident::from_str(path_str));
201         cx.enter_resolver(|resolver| {
202             if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
203                 &path,
204                 None,
205                 &ParentScope::module(resolver.graph_root()),
206                 false,
207                 false,
208             ) {
209                 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
210                     return Ok(res.map_id(|_| panic!("unexpected id")));
211                 }
212             }
213             if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
214                 return Ok(res.map_id(|_| panic!("unexpected id")));
215             }
216             debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
217             if let Ok((_, res)) =
218                 resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
219             {
220                 // don't resolve builtins like `#[derive]`
221                 if let Res::Def(..) = res {
222                     let res = res.map_id(|_| panic!("unexpected node_id"));
223                     return Ok(res);
224                 }
225             }
226             Err(ResolutionFailure::NotResolved {
227                 module_id,
228                 partial_res: None,
229                 unresolved: path_str.into(),
230             })
231         })
232     }
233
234     /// Resolves a string as a path within a particular namespace. Also returns an optional
235     /// URL fragment in the case of variants and methods.
236     fn resolve<'path>(
237         &self,
238         path_str: &'path str,
239         ns: Namespace,
240         current_item: &Option<String>,
241         module_id: DefId,
242         extra_fragment: &Option<String>,
243     ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
244         let cx = self.cx;
245
246         let result = cx.enter_resolver(|resolver| {
247             resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
248         });
249         debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
250         let result = match result {
251             Ok((_, Res::Err)) => Err(()),
252             x => x,
253         };
254
255         if let Ok((_, res)) = result {
256             let res = res.map_id(|_| panic!("unexpected node_id"));
257             // In case this is a trait item, skip the
258             // early return and try looking for the trait.
259             let value = match res {
260                 Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
261                 Res::Def(DefKind::AssocTy, _) => false,
262                 Res::Def(DefKind::Variant, _) => {
263                     return handle_variant(cx, res, extra_fragment);
264                 }
265                 // Not a trait item; just return what we found.
266                 Res::PrimTy(ty) => {
267                     if extra_fragment.is_some() {
268                         return Err(ErrorKind::AnchorFailure(
269                             AnchorFailure::RustdocAnchorConflict(res),
270                         ));
271                     }
272                     return Ok((res, Some(ty.name_str().to_owned())));
273                 }
274                 Res::Def(DefKind::Mod, _) => {
275                     return Ok((res, extra_fragment.clone()));
276                 }
277                 _ => {
278                     return Ok((res, extra_fragment.clone()));
279                 }
280             };
281
282             if value != (ns == ValueNS) {
283                 return Err(ResolutionFailure::WrongNamespace(res, ns).into());
284             }
285         // FIXME: why is this necessary?
286         } else if let Some((path, prim)) = is_primitive(path_str, ns) {
287             if extra_fragment.is_some() {
288                 return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim)));
289             }
290             return Ok((prim, Some(path.to_owned())));
291         }
292
293         // Try looking for methods and associated items.
294         let mut split = path_str.rsplitn(2, "::");
295         // this can be an `unwrap()` because we ensure the link is never empty
296         let (item_str, item_name) = split.next().map(|i| (i, Symbol::intern(i))).unwrap();
297         let path_root = split
298             .next()
299             .map(|f| {
300                 if f == "self" || f == "Self" {
301                     if let Some(name) = current_item.as_ref() {
302                         return name.clone();
303                     }
304                 }
305                 f.to_owned()
306             })
307             // If there's no `::`, it's not an associated item.
308             // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
309             .ok_or_else(|| {
310                 debug!("found no `::`, assumming {} was correctly not in scope", item_name);
311                 ResolutionFailure::NotResolved {
312                     module_id,
313                     partial_res: None,
314                     unresolved: item_str.into(),
315                 }
316             })?;
317
318         if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
319             let impls =
320                 primitive_impl(cx, &path).ok_or_else(|| ResolutionFailure::NotResolved {
321                     module_id,
322                     partial_res: Some(prim),
323                     unresolved: item_str.into(),
324                 })?;
325             for &impl_ in impls {
326                 let link = cx
327                     .tcx
328                     .associated_items(impl_)
329                     .find_by_name_and_namespace(
330                         cx.tcx,
331                         Ident::with_dummy_span(item_name),
332                         ns,
333                         impl_,
334                     )
335                     .map(|item| match item.kind {
336                         ty::AssocKind::Fn => "method",
337                         ty::AssocKind::Const => "associatedconstant",
338                         ty::AssocKind::Type => "associatedtype",
339                     })
340                     .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_str))));
341                 if let Some(link) = link {
342                     return Ok(link);
343                 }
344             }
345             debug!(
346                 "returning primitive error for {}::{} in {} namespace",
347                 path,
348                 item_name,
349                 ns.descr()
350             );
351             return Err(ResolutionFailure::NotResolved {
352                 module_id,
353                 partial_res: Some(prim),
354                 unresolved: item_str.into(),
355             }
356             .into());
357         }
358
359         let ty_res = cx
360             .enter_resolver(|resolver| {
361                 // only types can have associated items
362                 resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
363             })
364             .map(|(_, res)| res);
365         let ty_res = match ty_res {
366             Err(()) | Ok(Res::Err) => {
367                 return if ns == Namespace::ValueNS {
368                     self.variant_field(path_str, current_item, module_id)
369                 } else {
370                     Err(ResolutionFailure::NotResolved {
371                         module_id,
372                         partial_res: None,
373                         unresolved: path_root.into(),
374                     }
375                     .into())
376                 };
377             }
378             Ok(res) => res,
379         };
380         let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
381         let res = match ty_res {
382             Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, did) => {
383                 debug!("looking for associated item named {} for item {:?}", item_name, did);
384                 // Checks if item_name belongs to `impl SomeItem`
385                 let assoc_item = cx
386                     .tcx
387                     .inherent_impls(did)
388                     .iter()
389                     .flat_map(|&imp| {
390                         cx.tcx.associated_items(imp).find_by_name_and_namespace(
391                             cx.tcx,
392                             Ident::with_dummy_span(item_name),
393                             ns,
394                             imp,
395                         )
396                     })
397                     .map(|item| (item.kind, item.def_id))
398                     // There should only ever be one associated item that matches from any inherent impl
399                     .next()
400                     // Check if item_name belongs to `impl SomeTrait for SomeItem`
401                     // This gives precedence to `impl SomeItem`:
402                     // Although having both would be ambiguous, use impl version for compat. sake.
403                     // To handle that properly resolve() would have to support
404                     // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
405                     .or_else(|| {
406                         let kind =
407                             resolve_associated_trait_item(did, module_id, item_name, ns, &self.cx);
408                         debug!("got associated item kind {:?}", kind);
409                         kind
410                     });
411
412                 if let Some((kind, id)) = assoc_item {
413                     let out = match kind {
414                         ty::AssocKind::Fn => "method",
415                         ty::AssocKind::Const => "associatedconstant",
416                         ty::AssocKind::Type => "associatedtype",
417                     };
418                     Some(if extra_fragment.is_some() {
419                         Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
420                     } else {
421                         // HACK(jynelson): `clean` expects the type, not the associated item.
422                         // but the disambiguator logic expects the associated item.
423                         // Store the kind in a side channel so that only the disambiguator logic looks at it.
424                         self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
425                         Ok((ty_res, Some(format!("{}.{}", out, item_str))))
426                     })
427                 } else if ns == Namespace::ValueNS {
428                     debug!("looking for variants or fields named {} for {:?}", item_name, did);
429                     match cx.tcx.type_of(did).kind() {
430                         ty::Adt(def, _) => {
431                             let field = if def.is_enum() {
432                                 def.all_fields().find(|item| item.ident.name == item_name)
433                             } else {
434                                 def.non_enum_variant()
435                                     .fields
436                                     .iter()
437                                     .find(|item| item.ident.name == item_name)
438                             };
439                             field.map(|item| {
440                                 if extra_fragment.is_some() {
441                                     let res = Res::Def(
442                                         if def.is_enum() {
443                                             DefKind::Variant
444                                         } else {
445                                             DefKind::Field
446                                         },
447                                         item.did,
448                                     );
449                                     Err(ErrorKind::AnchorFailure(
450                                         AnchorFailure::RustdocAnchorConflict(res),
451                                     ))
452                                 } else {
453                                     Ok((
454                                         ty_res,
455                                         Some(format!(
456                                             "{}.{}",
457                                             if def.is_enum() { "variant" } else { "structfield" },
458                                             item.ident
459                                         )),
460                                     ))
461                                 }
462                             })
463                         }
464                         _ => None,
465                     }
466                 } else {
467                     // We already know this isn't in ValueNS, so no need to check variant_field
468                     return Err(ResolutionFailure::NotResolved {
469                         module_id,
470                         partial_res: Some(ty_res),
471                         unresolved: item_str.into(),
472                     }
473                     .into());
474                 }
475             }
476             Res::Def(DefKind::Trait, did) => cx
477                 .tcx
478                 .associated_items(did)
479                 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, did)
480                 .map(|item| {
481                     let kind = match item.kind {
482                         ty::AssocKind::Const => "associatedconstant",
483                         ty::AssocKind::Type => "associatedtype",
484                         ty::AssocKind::Fn => {
485                             if item.defaultness.has_value() {
486                                 "method"
487                             } else {
488                                 "tymethod"
489                             }
490                         }
491                     };
492
493                     if extra_fragment.is_some() {
494                         Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
495                     } else {
496                         let res = Res::Def(item.kind.as_def_kind(), item.def_id);
497                         Ok((res, Some(format!("{}.{}", kind, item_str))))
498                     }
499                 }),
500             _ => None,
501         };
502         res.unwrap_or_else(|| {
503             if ns == Namespace::ValueNS {
504                 self.variant_field(path_str, current_item, module_id)
505             } else {
506                 Err(ResolutionFailure::NotResolved {
507                     module_id,
508                     partial_res: Some(ty_res),
509                     unresolved: item_str.into(),
510                 }
511                 .into())
512             }
513         })
514     }
515
516     /// Used for reporting better errors.
517     ///
518     /// Returns whether the link resolved 'fully' in another namespace.
519     /// 'fully' here means that all parts of the link resolved, not just some path segments.
520     /// This returns the `Res` even if it was erroneous for some reason
521     /// (such as having invalid URL fragments or being in the wrong namespace).
522     fn check_full_res(
523         &self,
524         ns: Namespace,
525         path_str: &str,
526         module_id: DefId,
527         current_item: &Option<String>,
528         extra_fragment: &Option<String>,
529     ) -> Option<Res> {
530         let check_full_res_inner = |this: &Self, result: Result<Res, ErrorKind<'_>>| {
531             let res = match result {
532                 Ok(res) => Some(res),
533                 Err(ErrorKind::Resolve(box kind)) => kind.full_res(),
534                 Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => {
535                     Some(res)
536                 }
537                 Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None,
538             };
539             this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
540         };
541         // cannot be used for macro namespace
542         let check_full_res = |this: &Self, ns| {
543             let result = this.resolve(path_str, ns, current_item, module_id, extra_fragment);
544             check_full_res_inner(this, result.map(|(res, _)| res))
545         };
546         let check_full_res_macro = |this: &Self| {
547             let result = this.macro_resolve(path_str, module_id);
548             check_full_res_inner(this, result.map_err(ErrorKind::from))
549         };
550         match ns {
551             Namespace::MacroNS => check_full_res_macro(self),
552             Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns),
553         }
554     }
555 }
556
557 fn resolve_associated_trait_item(
558     did: DefId,
559     module: DefId,
560     item_name: Symbol,
561     ns: Namespace,
562     cx: &DocContext<'_>,
563 ) -> Option<(ty::AssocKind, DefId)> {
564     let ty = cx.tcx.type_of(did);
565     // First consider automatic impls: `impl From<T> for T`
566     let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
567     let mut candidates: Vec<_> = implicit_impls
568         .flat_map(|impl_outer| {
569             match impl_outer.inner {
570                 ImplItem(impl_) => {
571                     debug!("considering auto or blanket impl for trait {:?}", impl_.trait_);
572                     // Give precedence to methods that were overridden
573                     if !impl_.provided_trait_methods.contains(&*item_name.as_str()) {
574                         let mut items = impl_.items.into_iter().filter_map(|assoc| {
575                             if assoc.name.as_deref() != Some(&*item_name.as_str()) {
576                                 return None;
577                             }
578                             let kind = assoc
579                                 .inner
580                                 .as_assoc_kind()
581                                 .expect("inner items for a trait should be associated items");
582                             if kind.namespace() != ns {
583                                 return None;
584                             }
585
586                             trace!("considering associated item {:?}", assoc.inner);
587                             // We have a slight issue: normal methods come from `clean` types,
588                             // but provided methods come directly from `tcx`.
589                             // Fortunately, we don't need the whole method, we just need to know
590                             // what kind of associated item it is.
591                             Some((kind, assoc.def_id))
592                         });
593                         let assoc = items.next();
594                         debug_assert_eq!(items.count(), 0);
595                         assoc
596                     } else {
597                         // These are provided methods or default types:
598                         // ```
599                         // trait T {
600                         //   type A = usize;
601                         //   fn has_default() -> A { 0 }
602                         // }
603                         // ```
604                         let trait_ = impl_.trait_.unwrap().def_id().unwrap();
605                         cx.tcx
606                             .associated_items(trait_)
607                             .find_by_name_and_namespace(
608                                 cx.tcx,
609                                 Ident::with_dummy_span(item_name),
610                                 ns,
611                                 trait_,
612                             )
613                             .map(|assoc| (assoc.kind, assoc.def_id))
614                     }
615                 }
616                 _ => panic!("get_impls returned something that wasn't an impl"),
617             }
618         })
619         .collect();
620
621     // Next consider explicit impls: `impl MyTrait for MyType`
622     // Give precedence to inherent impls.
623     if candidates.is_empty() {
624         let traits = traits_implemented_by(cx, did, module);
625         debug!("considering traits {:?}", traits);
626         candidates.extend(traits.iter().filter_map(|&trait_| {
627             cx.tcx
628                 .associated_items(trait_)
629                 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
630                 .map(|assoc| (assoc.kind, assoc.def_id))
631         }));
632     }
633     // FIXME: warn about ambiguity
634     debug!("the candidates were {:?}", candidates);
635     candidates.pop()
636 }
637
638 /// Given a type, return all traits in scope in `module` implemented by that type.
639 ///
640 /// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
641 /// So it is not stable to serialize cross-crate.
642 fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet<DefId> {
643     let mut cache = cx.module_trait_cache.borrow_mut();
644     let in_scope_traits = cache.entry(module).or_insert_with(|| {
645         cx.enter_resolver(|resolver| {
646             resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect()
647         })
648     });
649
650     let ty = cx.tcx.type_of(type_);
651     let iter = in_scope_traits.iter().flat_map(|&trait_| {
652         trace!("considering explicit impl for trait {:?}", trait_);
653
654         // Look at each trait implementation to see if it's an impl for `did`
655         cx.tcx.find_map_relevant_impl(trait_, ty, |impl_| {
656             let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
657             // Check if these are the same type.
658             let impl_type = trait_ref.self_ty();
659             trace!(
660                 "comparing type {} with kind {:?} against type {:?}",
661                 impl_type,
662                 impl_type.kind(),
663                 type_
664             );
665             // Fast path: if this is a primitive simple `==` will work
666             let saw_impl = impl_type == ty
667                 || match impl_type.kind() {
668                     // Check if these are the same def_id
669                     ty::Adt(def, _) => {
670                         debug!("adt def_id: {:?}", def.did);
671                         def.did == type_
672                     }
673                     ty::Foreign(def_id) => *def_id == type_,
674                     _ => false,
675                 };
676
677             if saw_impl { Some(trait_) } else { None }
678         })
679     });
680     iter.collect()
681 }
682
683 /// Check for resolve collisions between a trait and its derive
684 ///
685 /// These are common and we should just resolve to the trait in that case
686 fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_>>>) -> bool {
687     if let PerNS {
688         type_ns: Ok((Res::Def(DefKind::Trait, _), _)),
689         macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
690         ..
691     } = *ns
692     {
693         true
694     } else {
695         false
696     }
697 }
698
699 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
700     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
701         use rustc_middle::ty::DefIdTree;
702
703         let parent_node = if item.is_fake() {
704             // FIXME: is this correct?
705             None
706         // If we're documenting the crate root itself, it has no parent. Use the root instead.
707         } else if item.def_id.is_top_level_module() {
708             Some(item.def_id)
709         } else {
710             let mut current = item.def_id;
711             // The immediate parent might not always be a module.
712             // Find the first parent which is.
713             loop {
714                 if let Some(parent) = self.cx.tcx.parent(current) {
715                     if self.cx.tcx.def_kind(parent) == DefKind::Mod {
716                         break Some(parent);
717                     }
718                     current = parent;
719                 } else {
720                     debug!(
721                         "{:?} has no parent (kind={:?}, original was {:?})",
722                         current,
723                         self.cx.tcx.def_kind(current),
724                         item.def_id
725                     );
726                     break None;
727                 }
728             }
729         };
730
731         if parent_node.is_some() {
732             trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
733         }
734
735         let current_item = match item.inner {
736             ModuleItem(..) => {
737                 if item.attrs.inner_docs {
738                     if item.def_id.is_top_level_module() { item.name.clone() } else { None }
739                 } else {
740                     match parent_node.or(self.mod_ids.last().copied()) {
741                         Some(parent) if !parent.is_top_level_module() => {
742                             // FIXME: can we pull the parent module's name from elsewhere?
743                             Some(self.cx.tcx.item_name(parent).to_string())
744                         }
745                         _ => None,
746                     }
747                 }
748             }
749             ImplItem(Impl { ref for_, .. }) => {
750                 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
751             }
752             // we don't display docs on `extern crate` items anyway, so don't process them.
753             ExternCrateItem(..) => {
754                 debug!("ignoring extern crate item {:?}", item.def_id);
755                 return self.fold_item_recur(item);
756             }
757             ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
758             MacroItem(..) => None,
759             _ => item.name.clone(),
760         };
761
762         if item.is_mod() && item.attrs.inner_docs {
763             self.mod_ids.push(item.def_id);
764         }
765
766         // find item's parent to resolve `Self` in item's docs below
767         let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| {
768             let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
769             let item_parent = self.cx.tcx.hir().find(parent_hir);
770             match item_parent {
771                 Some(hir::Node::Item(hir::Item {
772                     kind:
773                         hir::ItemKind::Impl {
774                             self_ty:
775                                 hir::Ty {
776                                     kind:
777                                         hir::TyKind::Path(hir::QPath::Resolved(
778                                             _,
779                                             hir::Path { segments, .. },
780                                         )),
781                                     ..
782                                 },
783                             ..
784                         },
785                     ..
786                 })) => segments.first().map(|seg| seg.ident.to_string()),
787                 Some(hir::Node::Item(hir::Item {
788                     ident, kind: hir::ItemKind::Enum(..), ..
789                 }))
790                 | Some(hir::Node::Item(hir::Item {
791                     ident, kind: hir::ItemKind::Struct(..), ..
792                 }))
793                 | Some(hir::Node::Item(hir::Item {
794                     ident, kind: hir::ItemKind::Union(..), ..
795                 }))
796                 | Some(hir::Node::Item(hir::Item {
797                     ident, kind: hir::ItemKind::Trait(..), ..
798                 })) => Some(ident.to_string()),
799                 _ => None,
800             }
801         });
802
803         // We want to resolve in the lexical scope of the documentation.
804         // In the presence of re-exports, this is not the same as the module of the item.
805         // Rather than merging all documentation into one, resolve it one attribute at a time
806         // so we know which module it came from.
807         let mut attrs = item.attrs.doc_strings.iter().peekable();
808         while let Some(attr) = attrs.next() {
809             // `collapse_docs` does not have the behavior we want:
810             // we want `///` and `#[doc]` to count as the same attribute,
811             // but currently it will treat them as separate.
812             // As a workaround, combine all attributes with the same parent module into the same attribute.
813             let mut combined_docs = attr.doc.clone();
814             loop {
815                 match attrs.peek() {
816                     Some(next) if next.parent_module == attr.parent_module => {
817                         combined_docs.push('\n');
818                         combined_docs.push_str(&attrs.next().unwrap().doc);
819                     }
820                     _ => break,
821                 }
822             }
823             debug!("combined_docs={}", combined_docs);
824
825             let (krate, parent_node) = if let Some(id) = attr.parent_module {
826                 trace!("docs {:?} came from {:?}", attr.doc, id);
827                 (id.krate, Some(id))
828             } else {
829                 trace!("no parent found for {:?}", attr.doc);
830                 (item.def_id.krate, parent_node)
831             };
832             // NOTE: if there are links that start in one crate and end in another, this will not resolve them.
833             // This is a degenerate case and it's not supported by rustdoc.
834             // FIXME: this will break links that start in `#[doc = ...]` and end as a sugared doc. Should this be supported?
835             for (ori_link, link_range) in markdown_links(&combined_docs) {
836                 let link = self.resolve_link(
837                     &item,
838                     &combined_docs,
839                     &current_item,
840                     parent_node,
841                     &parent_name,
842                     krate,
843                     ori_link,
844                     link_range,
845                 );
846                 if let Some(link) = link {
847                     item.attrs.links.push(link);
848                 }
849             }
850         }
851
852         if item.is_mod() && !item.attrs.inner_docs {
853             self.mod_ids.push(item.def_id);
854         }
855
856         if item.is_mod() {
857             let ret = self.fold_item_recur(item);
858
859             self.mod_ids.pop();
860
861             ret
862         } else {
863             self.fold_item_recur(item)
864         }
865     }
866 }
867
868 impl LinkCollector<'_, '_> {
869     fn resolve_link(
870         &self,
871         item: &Item,
872         dox: &str,
873         current_item: &Option<String>,
874         parent_node: Option<DefId>,
875         parent_name: &Option<String>,
876         krate: CrateNum,
877         ori_link: String,
878         link_range: Option<Range<usize>>,
879     ) -> Option<ItemLink> {
880         trace!("considering link '{}'", ori_link);
881
882         // Bail early for real links.
883         if ori_link.contains('/') {
884             return None;
885         }
886
887         // [] is mostly likely not supposed to be a link
888         if ori_link.is_empty() {
889             return None;
890         }
891
892         let cx = self.cx;
893         let link = ori_link.replace("`", "");
894         let parts = link.split('#').collect::<Vec<_>>();
895         let (link, extra_fragment) = if parts.len() > 2 {
896             anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
897             return None;
898         } else if parts.len() == 2 {
899             if parts[0].trim().is_empty() {
900                 // This is an anchor to an element of the current page, nothing to do in here!
901                 return None;
902             }
903             (parts[0], Some(parts[1].to_owned()))
904         } else {
905             (parts[0], None)
906         };
907         let resolved_self;
908         let link_text;
909         let mut path_str;
910         let disambiguator;
911         let (mut res, mut fragment) = {
912             path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
913                 disambiguator = Some(d);
914                 path
915             } else {
916                 disambiguator = None;
917                 &link
918             }
919             .trim();
920
921             if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
922                 return None;
923             }
924
925             // We stripped `()` and `!` when parsing the disambiguator.
926             // Add them back to be displayed, but not prefix disambiguators.
927             link_text = disambiguator
928                 .map(|d| d.display_for(path_str))
929                 .unwrap_or_else(|| path_str.to_owned());
930
931             // In order to correctly resolve intra-doc-links we need to
932             // pick a base AST node to work from.  If the documentation for
933             // this module came from an inner comment (//!) then we anchor
934             // our name resolution *inside* the module.  If, on the other
935             // hand it was an outer comment (///) then we anchor the name
936             // resolution in the parent module on the basis that the names
937             // used are more likely to be intended to be parent names.  For
938             // this, we set base_node to None for inner comments since
939             // we've already pushed this node onto the resolution stack but
940             // for outer comments we explicitly try and resolve against the
941             // parent_node first.
942             let base_node = if item.is_mod() && item.attrs.inner_docs {
943                 self.mod_ids.last().copied()
944             } else {
945                 parent_node
946             };
947
948             let mut module_id = if let Some(id) = base_node {
949                 id
950             } else {
951                 debug!("attempting to resolve item without parent module: {}", path_str);
952                 let err_kind = ResolutionFailure::NoParentItem.into();
953                 resolution_failure(
954                     self,
955                     &item,
956                     path_str,
957                     disambiguator,
958                     dox,
959                     link_range,
960                     smallvec![err_kind],
961                 );
962                 return None;
963             };
964
965             // replace `Self` with suitable item's parent name
966             if path_str.starts_with("Self::") {
967                 if let Some(ref name) = parent_name {
968                     resolved_self = format!("{}::{}", name, &path_str[6..]);
969                     path_str = &resolved_self;
970                 }
971             } else if path_str.starts_with("crate::") {
972                 use rustc_span::def_id::CRATE_DEF_INDEX;
973
974                 // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented.
975                 // But rustdoc wants it to mean the crate this item was originally present in.
976                 // To work around this, remove it and resolve relative to the crate root instead.
977                 // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous
978                 // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root.
979                 resolved_self = format!("self::{}", &path_str["crate::".len()..]);
980                 path_str = &resolved_self;
981                 module_id = DefId { krate, index: CRATE_DEF_INDEX };
982             }
983
984             match self.resolve_with_disambiguator(
985                 disambiguator,
986                 item,
987                 dox,
988                 path_str,
989                 current_item,
990                 module_id,
991                 extra_fragment,
992                 &ori_link,
993                 link_range.clone(),
994             ) {
995                 Some(x) => x,
996                 None => return None,
997             }
998         };
999
1000         // Check for a primitive which might conflict with a module
1001         // Report the ambiguity and require that the user specify which one they meant.
1002         // FIXME: could there ever be a primitive not in the type namespace?
1003         if matches!(
1004             disambiguator,
1005             None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
1006         ) && !matches!(res, Res::PrimTy(_))
1007         {
1008             if let Some((path, prim)) = is_primitive(path_str, TypeNS) {
1009                 // `prim@char`
1010                 if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1011                     if fragment.is_some() {
1012                         anchor_failure(
1013                             cx,
1014                             &item,
1015                             path_str,
1016                             dox,
1017                             link_range,
1018                             AnchorFailure::RustdocAnchorConflict(prim),
1019                         );
1020                         return None;
1021                     }
1022                     res = prim;
1023                     fragment = Some(path.to_owned());
1024                 } else {
1025                     // `[char]` when a `char` module is in scope
1026                     let candidates = vec![res, prim];
1027                     ambiguity_error(cx, &item, path_str, dox, link_range, candidates);
1028                     return None;
1029                 }
1030             }
1031         }
1032
1033         let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
1034             // The resolved item did not match the disambiguator; give a better error than 'not found'
1035             let msg = format!("incompatible link kind for `{}`", path_str);
1036             let callback = |diag: &mut DiagnosticBuilder<'_>, sp| {
1037                 let note = format!(
1038                     "this link resolved to {} {}, which is not {} {}",
1039                     resolved.article(),
1040                     resolved.descr(),
1041                     specified.article(),
1042                     specified.descr()
1043                 );
1044                 diag.note(&note);
1045                 suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
1046             };
1047             report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, &item, dox, &link_range, callback);
1048         };
1049         if let Res::PrimTy(..) = res {
1050             match disambiguator {
1051                 Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
1052                     Some(ItemLink { link: ori_link, link_text, did: None, fragment })
1053                 }
1054                 Some(other) => {
1055                     report_mismatch(other, Disambiguator::Primitive);
1056                     None
1057                 }
1058             }
1059         } else {
1060             debug!("intra-doc link to {} resolved to {:?}", path_str, res);
1061
1062             // Disallow e.g. linking to enums with `struct@`
1063             if let Res::Def(kind, _) = res {
1064                 debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
1065                 match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
1066                     | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
1067                     // NOTE: this allows 'method' to mean both normal functions and associated functions
1068                     // This can't cause ambiguity because both are in the same namespace.
1069                     | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
1070                     // These are namespaces; allow anything in the namespace to match
1071                     | (_, Some(Disambiguator::Namespace(_)))
1072                     // If no disambiguator given, allow anything
1073                     | (_, None)
1074                     // All of these are valid, so do nothing
1075                     => {}
1076                     (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
1077                     (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
1078                         report_mismatch(specified, Disambiguator::Kind(kind));
1079                         return None;
1080                     }
1081                 }
1082             }
1083
1084             // item can be non-local e.g. when using #[doc(primitive = "pointer")]
1085             if let Some((src_id, dst_id)) = res
1086                 .opt_def_id()
1087                 .and_then(|def_id| def_id.as_local())
1088                 .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
1089             {
1090                 use rustc_hir::def_id::LOCAL_CRATE;
1091
1092                 let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
1093                 let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
1094
1095                 if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
1096                     && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
1097                 {
1098                     privacy_error(cx, &item, &path_str, dox, link_range);
1099                 }
1100             }
1101             let id = register_res(cx, res);
1102             Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment })
1103         }
1104     }
1105
1106     fn resolve_with_disambiguator(
1107         &self,
1108         disambiguator: Option<Disambiguator>,
1109         item: &Item,
1110         dox: &str,
1111         path_str: &str,
1112         current_item: &Option<String>,
1113         base_node: DefId,
1114         extra_fragment: Option<String>,
1115         ori_link: &str,
1116         link_range: Option<Range<usize>>,
1117     ) -> Option<(Res, Option<String>)> {
1118         match disambiguator.map(Disambiguator::ns) {
1119             Some(ns @ (ValueNS | TypeNS)) => {
1120                 match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment) {
1121                     Ok(res) => Some(res),
1122                     Err(ErrorKind::Resolve(box mut kind)) => {
1123                         // We only looked in one namespace. Try to give a better error if possible.
1124                         if kind.full_res().is_none() {
1125                             let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
1126                             // FIXME: really it should be `resolution_failure` that does this, not `resolve_with_disambiguator`
1127                             // See https://github.com/rust-lang/rust/pull/76955#discussion_r493953382 for a good approach
1128                             for &new_ns in &[other_ns, MacroNS] {
1129                                 if let Some(res) = self.check_full_res(
1130                                     new_ns,
1131                                     path_str,
1132                                     base_node,
1133                                     &current_item,
1134                                     &extra_fragment,
1135                                 ) {
1136                                     kind = ResolutionFailure::WrongNamespace(res, ns);
1137                                     break;
1138                                 }
1139                             }
1140                         }
1141                         resolution_failure(
1142                             self,
1143                             &item,
1144                             path_str,
1145                             disambiguator,
1146                             dox,
1147                             link_range,
1148                             smallvec![kind],
1149                         );
1150                         // This could just be a normal link or a broken link
1151                         // we could potentially check if something is
1152                         // "intra-doc-link-like" and warn in that case.
1153                         return None;
1154                     }
1155                     Err(ErrorKind::AnchorFailure(msg)) => {
1156                         anchor_failure(self.cx, &item, &ori_link, dox, link_range, msg);
1157                         return None;
1158                     }
1159                 }
1160             }
1161             None => {
1162                 // Try everything!
1163                 let mut candidates = PerNS {
1164                     macro_ns: self
1165                         .macro_resolve(path_str, base_node)
1166                         .map(|res| (res, extra_fragment.clone())),
1167                     type_ns: match self.resolve(
1168                         path_str,
1169                         TypeNS,
1170                         &current_item,
1171                         base_node,
1172                         &extra_fragment,
1173                     ) {
1174                         Ok(res) => {
1175                             debug!("got res in TypeNS: {:?}", res);
1176                             Ok(res)
1177                         }
1178                         Err(ErrorKind::AnchorFailure(msg)) => {
1179                             anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1180                             return None;
1181                         }
1182                         Err(ErrorKind::Resolve(box kind)) => Err(kind),
1183                     },
1184                     value_ns: match self.resolve(
1185                         path_str,
1186                         ValueNS,
1187                         &current_item,
1188                         base_node,
1189                         &extra_fragment,
1190                     ) {
1191                         Ok(res) => Ok(res),
1192                         Err(ErrorKind::AnchorFailure(msg)) => {
1193                             anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1194                             return None;
1195                         }
1196                         Err(ErrorKind::Resolve(box kind)) => Err(kind),
1197                     }
1198                     .and_then(|(res, fragment)| {
1199                         // Constructors are picked up in the type namespace.
1200                         match res {
1201                             Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
1202                                 Err(ResolutionFailure::WrongNamespace(res, TypeNS))
1203                             }
1204                             _ => match (fragment, extra_fragment) {
1205                                 (Some(fragment), Some(_)) => {
1206                                     // Shouldn't happen but who knows?
1207                                     Ok((res, Some(fragment)))
1208                                 }
1209                                 (fragment, None) | (None, fragment) => Ok((res, fragment)),
1210                             },
1211                         }
1212                     }),
1213                 };
1214
1215                 let len = candidates.iter().filter(|res| res.is_ok()).count();
1216
1217                 if len == 0 {
1218                     resolution_failure(
1219                         self,
1220                         &item,
1221                         path_str,
1222                         disambiguator,
1223                         dox,
1224                         link_range,
1225                         candidates.into_iter().filter_map(|res| res.err()).collect(),
1226                     );
1227                     // this could just be a normal link
1228                     return None;
1229                 }
1230
1231                 if len == 1 {
1232                     Some(candidates.into_iter().filter_map(|res| res.ok()).next().unwrap())
1233                 } else if len == 2 && is_derive_trait_collision(&candidates) {
1234                     Some(candidates.type_ns.unwrap())
1235                 } else {
1236                     if is_derive_trait_collision(&candidates) {
1237                         candidates.macro_ns = Err(ResolutionFailure::Dummy);
1238                     }
1239                     // If we're reporting an ambiguity, don't mention the namespaces that failed
1240                     let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
1241                     ambiguity_error(
1242                         self.cx,
1243                         &item,
1244                         path_str,
1245                         dox,
1246                         link_range,
1247                         candidates.present_items().collect(),
1248                     );
1249                     return None;
1250                 }
1251             }
1252             Some(MacroNS) => {
1253                 match self.macro_resolve(path_str, base_node) {
1254                     Ok(res) => Some((res, extra_fragment)),
1255                     Err(mut kind) => {
1256                         // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
1257                         for &ns in &[TypeNS, ValueNS] {
1258                             if let Some(res) = self.check_full_res(
1259                                 ns,
1260                                 path_str,
1261                                 base_node,
1262                                 &current_item,
1263                                 &extra_fragment,
1264                             ) {
1265                                 kind = ResolutionFailure::WrongNamespace(res, MacroNS);
1266                                 break;
1267                             }
1268                         }
1269                         resolution_failure(
1270                             self,
1271                             &item,
1272                             path_str,
1273                             disambiguator,
1274                             dox,
1275                             link_range,
1276                             smallvec![kind],
1277                         );
1278                         return None;
1279                     }
1280                 }
1281             }
1282         }
1283     }
1284 }
1285
1286 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
1287 enum Disambiguator {
1288     Primitive,
1289     Kind(DefKind),
1290     Namespace(Namespace),
1291 }
1292
1293 impl Disambiguator {
1294     /// The text that should be displayed when the path is rendered as HTML.
1295     ///
1296     /// NOTE: `path` is not the original link given by the user, but a name suitable for passing to `resolve`.
1297     fn display_for(&self, path: &str) -> String {
1298         match self {
1299             // FIXME: this will have different output if the user had `m!()` originally.
1300             Self::Kind(DefKind::Macro(MacroKind::Bang)) => format!("{}!", path),
1301             Self::Kind(DefKind::Fn) => format!("{}()", path),
1302             _ => path.to_owned(),
1303         }
1304     }
1305
1306     /// (disambiguator, path_str)
1307     fn from_str(link: &str) -> Result<(Self, &str), ()> {
1308         use Disambiguator::{Kind, Namespace as NS, Primitive};
1309
1310         let find_suffix = || {
1311             let suffixes = [
1312                 ("!()", DefKind::Macro(MacroKind::Bang)),
1313                 ("()", DefKind::Fn),
1314                 ("!", DefKind::Macro(MacroKind::Bang)),
1315             ];
1316             for &(suffix, kind) in &suffixes {
1317                 if link.ends_with(suffix) {
1318                     return Ok((Kind(kind), link.trim_end_matches(suffix)));
1319                 }
1320             }
1321             Err(())
1322         };
1323
1324         if let Some(idx) = link.find('@') {
1325             let (prefix, rest) = link.split_at(idx);
1326             let d = match prefix {
1327                 "struct" => Kind(DefKind::Struct),
1328                 "enum" => Kind(DefKind::Enum),
1329                 "trait" => Kind(DefKind::Trait),
1330                 "union" => Kind(DefKind::Union),
1331                 "module" | "mod" => Kind(DefKind::Mod),
1332                 "const" | "constant" => Kind(DefKind::Const),
1333                 "static" => Kind(DefKind::Static),
1334                 "function" | "fn" | "method" => Kind(DefKind::Fn),
1335                 "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
1336                 "type" => NS(Namespace::TypeNS),
1337                 "value" => NS(Namespace::ValueNS),
1338                 "macro" => NS(Namespace::MacroNS),
1339                 "prim" | "primitive" => Primitive,
1340                 _ => return find_suffix(),
1341             };
1342             Ok((d, &rest[1..]))
1343         } else {
1344             find_suffix()
1345         }
1346     }
1347
1348     /// WARNING: panics on `Res::Err`
1349     fn from_res(res: Res) -> Self {
1350         match res {
1351             Res::Def(kind, _) => Disambiguator::Kind(kind),
1352             Res::PrimTy(_) => Disambiguator::Primitive,
1353             _ => Disambiguator::Namespace(res.ns().expect("can't call `from_res` on Res::err")),
1354         }
1355     }
1356
1357     fn suggestion(self) -> Suggestion {
1358         let kind = match self {
1359             Disambiguator::Primitive => return Suggestion::Prefix("prim"),
1360             Disambiguator::Kind(kind) => kind,
1361             Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"),
1362         };
1363         if kind == DefKind::Macro(MacroKind::Bang) {
1364             return Suggestion::Macro;
1365         } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
1366             return Suggestion::Function;
1367         }
1368
1369         let prefix = match kind {
1370             DefKind::Struct => "struct",
1371             DefKind::Enum => "enum",
1372             DefKind::Trait => "trait",
1373             DefKind::Union => "union",
1374             DefKind::Mod => "mod",
1375             DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
1376                 "const"
1377             }
1378             DefKind::Static => "static",
1379             DefKind::Macro(MacroKind::Derive) => "derive",
1380             // Now handle things that don't have a specific disambiguator
1381             _ => match kind
1382                 .ns()
1383                 .expect("tried to calculate a disambiguator for a def without a namespace?")
1384             {
1385                 Namespace::TypeNS => "type",
1386                 Namespace::ValueNS => "value",
1387                 Namespace::MacroNS => "macro",
1388             },
1389         };
1390
1391         Suggestion::Prefix(prefix)
1392     }
1393
1394     fn ns(self) -> Namespace {
1395         match self {
1396             Self::Namespace(n) => n,
1397             Self::Kind(k) => {
1398                 k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
1399             }
1400             Self::Primitive => TypeNS,
1401         }
1402     }
1403
1404     fn article(self) -> &'static str {
1405         match self {
1406             Self::Namespace(_) => panic!("article() doesn't make sense for namespaces"),
1407             Self::Kind(k) => k.article(),
1408             Self::Primitive => "a",
1409         }
1410     }
1411
1412     fn descr(self) -> &'static str {
1413         match self {
1414             Self::Namespace(n) => n.descr(),
1415             // HACK(jynelson): by looking at the source I saw the DefId we pass
1416             // for `expected.descr()` doesn't matter, since it's not a crate
1417             Self::Kind(k) => k.descr(DefId::local(hir::def_id::DefIndex::from_usize(0))),
1418             Self::Primitive => "builtin type",
1419         }
1420     }
1421 }
1422
1423 enum Suggestion {
1424     Prefix(&'static str),
1425     Function,
1426     Macro,
1427 }
1428
1429 impl Suggestion {
1430     fn descr(&self) -> Cow<'static, str> {
1431         match self {
1432             Self::Prefix(x) => format!("prefix with `{}@`", x).into(),
1433             Self::Function => "add parentheses".into(),
1434             Self::Macro => "add an exclamation mark".into(),
1435         }
1436     }
1437
1438     fn as_help(&self, path_str: &str) -> String {
1439         // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1440         match self {
1441             Self::Prefix(prefix) => format!("{}@{}", prefix, path_str),
1442             Self::Function => format!("{}()", path_str),
1443             Self::Macro => format!("{}!", path_str),
1444         }
1445     }
1446 }
1447
1448 /// Reports a diagnostic for an intra-doc link.
1449 ///
1450 /// If no link range is provided, or the source span of the link cannot be determined, the span of
1451 /// the entire documentation block is used for the lint. If a range is provided but the span
1452 /// calculation fails, a note is added to the diagnostic pointing to the link in the markdown.
1453 ///
1454 /// The `decorate` callback is invoked in all cases to allow further customization of the
1455 /// diagnostic before emission. If the span of the link was able to be determined, the second
1456 /// parameter of the callback will contain it, and the primary span of the diagnostic will be set
1457 /// to it.
1458 fn report_diagnostic(
1459     cx: &DocContext<'_>,
1460     lint: &'static Lint,
1461     msg: &str,
1462     item: &Item,
1463     dox: &str,
1464     link_range: &Option<Range<usize>>,
1465     decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
1466 ) {
1467     let hir_id = match cx.as_local_hir_id(item.def_id) {
1468         Some(hir_id) => hir_id,
1469         None => {
1470             // If non-local, no need to check anything.
1471             info!("ignoring warning from parent crate: {}", msg);
1472             return;
1473         }
1474     };
1475
1476     let attrs = &item.attrs;
1477     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
1478
1479     cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
1480         let mut diag = lint.build(msg);
1481
1482         let span = link_range
1483             .as_ref()
1484             .and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
1485
1486         if let Some(link_range) = link_range {
1487             if let Some(sp) = span {
1488                 diag.set_span(sp);
1489             } else {
1490                 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1491                 //                       ^     ~~~~
1492                 //                       |     link_range
1493                 //                       last_new_line_offset
1494                 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1495                 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1496
1497                 // Print the line containing the `link_range` and manually mark it with '^'s.
1498                 diag.note(&format!(
1499                     "the link appears in this line:\n\n{line}\n\
1500                      {indicator: <before$}{indicator:^<found$}",
1501                     line = line,
1502                     indicator = "",
1503                     before = link_range.start - last_new_line_offset,
1504                     found = link_range.len(),
1505                 ));
1506             }
1507         }
1508
1509         decorate(&mut diag, span);
1510
1511         diag.emit();
1512     });
1513 }
1514
1515 fn resolution_failure(
1516     collector: &LinkCollector<'_, '_>,
1517     item: &Item,
1518     path_str: &str,
1519     disambiguator: Option<Disambiguator>,
1520     dox: &str,
1521     link_range: Option<Range<usize>>,
1522     kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
1523 ) {
1524     report_diagnostic(
1525         collector.cx,
1526         BROKEN_INTRA_DOC_LINKS,
1527         &format!("unresolved link to `{}`", path_str),
1528         item,
1529         dox,
1530         &link_range,
1531         |diag, sp| {
1532             let item = |res: Res| {
1533                 format!(
1534                     "the {} `{}`",
1535                     res.descr(),
1536                     collector.cx.tcx.item_name(res.def_id()).to_string()
1537                 )
1538             };
1539             let assoc_item_not_allowed = |res: Res| {
1540                 let def_id = res.def_id();
1541                 let name = collector.cx.tcx.item_name(def_id);
1542                 format!(
1543                     "`{}` is {} {}, not a module or type, and cannot have associated items",
1544                     name,
1545                     res.article(),
1546                     res.descr()
1547                 )
1548             };
1549             // ignore duplicates
1550             let mut variants_seen = SmallVec::<[_; 3]>::new();
1551             for mut failure in kinds {
1552                 let variant = std::mem::discriminant(&failure);
1553                 if variants_seen.contains(&variant) {
1554                     continue;
1555                 }
1556                 variants_seen.push(variant);
1557
1558                 if let ResolutionFailure::NotResolved { module_id, partial_res, unresolved } =
1559                     &mut failure
1560                 {
1561                     use DefKind::*;
1562
1563                     let module_id = *module_id;
1564                     // FIXME(jynelson): this might conflict with my `Self` fix in #76467
1565                     // FIXME: maybe use itertools `collect_tuple` instead?
1566                     fn split(path: &str) -> Option<(&str, &str)> {
1567                         let mut splitter = path.rsplitn(2, "::");
1568                         splitter.next().and_then(|right| splitter.next().map(|left| (left, right)))
1569                     }
1570
1571                     // Check if _any_ parent of the path gets resolved.
1572                     // If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
1573                     let mut name = path_str;
1574                     'outer: loop {
1575                         let (start, end) = if let Some(x) = split(name) {
1576                             x
1577                         } else {
1578                             // avoid bug that marked [Quux::Z] as missing Z, not Quux
1579                             if partial_res.is_none() {
1580                                 *unresolved = name.into();
1581                             }
1582                             break;
1583                         };
1584                         name = start;
1585                         for &ns in &[TypeNS, ValueNS, MacroNS] {
1586                             if let Some(res) =
1587                                 collector.check_full_res(ns, &start, module_id, &None, &None)
1588                             {
1589                                 debug!("found partial_res={:?}", res);
1590                                 *partial_res = Some(res);
1591                                 *unresolved = end.into();
1592                                 break 'outer;
1593                             }
1594                         }
1595                         *unresolved = end.into();
1596                     }
1597
1598                     let last_found_module = match *partial_res {
1599                         Some(Res::Def(DefKind::Mod, id)) => Some(id),
1600                         None => Some(module_id),
1601                         _ => None,
1602                     };
1603                     // See if this was a module: `[path]` or `[std::io::nope]`
1604                     if let Some(module) = last_found_module {
1605                         let note = if partial_res.is_some() {
1606                             // Part of the link resolved; e.g. `std::io::nonexistent`
1607                             let module_name = collector.cx.tcx.item_name(module);
1608                             format!("no item named `{}` in module `{}`", unresolved, module_name)
1609                         } else {
1610                             // None of the link resolved; e.g. `Notimported`
1611                             format!("no item named `{}` in scope", unresolved)
1612                         };
1613                         if let Some(span) = sp {
1614                             diag.span_label(span, &note);
1615                         } else {
1616                             diag.note(&note);
1617                         }
1618
1619                         // If the link has `::` in it, assume it was meant to be an intra-doc link.
1620                         // Otherwise, the `[]` might be unrelated.
1621                         // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links
1622                         if !path_str.contains("::") {
1623                             diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1624                         }
1625
1626                         continue;
1627                     }
1628
1629                     // Otherwise, it must be an associated item or variant
1630                     let res = partial_res.expect("None case was handled by `last_found_module`");
1631                     let diagnostic_name;
1632                     let (kind, name) = match res {
1633                         Res::Def(kind, def_id) => {
1634                             diagnostic_name = collector.cx.tcx.item_name(def_id).as_str();
1635                             (Some(kind), &*diagnostic_name)
1636                         }
1637                         Res::PrimTy(ty) => (None, ty.name_str()),
1638                         _ => unreachable!("only ADTs and primitives are in scope at module level"),
1639                     };
1640                     let path_description = if let Some(kind) = kind {
1641                         match kind {
1642                             Mod | ForeignMod => "inner item",
1643                             Struct => "field or associated item",
1644                             Enum | Union => "variant or associated item",
1645                             Variant
1646                             | Field
1647                             | Closure
1648                             | Generator
1649                             | AssocTy
1650                             | AssocConst
1651                             | AssocFn
1652                             | Fn
1653                             | Macro(_)
1654                             | Const
1655                             | ConstParam
1656                             | ExternCrate
1657                             | Use
1658                             | LifetimeParam
1659                             | Ctor(_, _)
1660                             | AnonConst => {
1661                                 let note = assoc_item_not_allowed(res);
1662                                 if let Some(span) = sp {
1663                                     diag.span_label(span, &note);
1664                                 } else {
1665                                     diag.note(&note);
1666                                 }
1667                                 return;
1668                             }
1669                             Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
1670                             | Static => "associated item",
1671                             Impl | GlobalAsm => unreachable!("not a path"),
1672                         }
1673                     } else {
1674                         "associated item"
1675                     };
1676                     let note = format!(
1677                         "the {} `{}` has no {} named `{}`",
1678                         res.descr(),
1679                         name,
1680                         disambiguator.map_or(path_description, |d| d.descr()),
1681                         unresolved,
1682                     );
1683                     if let Some(span) = sp {
1684                         diag.span_label(span, &note);
1685                     } else {
1686                         diag.note(&note);
1687                     }
1688
1689                     continue;
1690                 }
1691                 let note = match failure {
1692                     ResolutionFailure::NotResolved { .. } => unreachable!("handled above"),
1693                     ResolutionFailure::Dummy => continue,
1694                     ResolutionFailure::WrongNamespace(res, expected_ns) => {
1695                         if let Res::Def(kind, _) = res {
1696                             let disambiguator = Disambiguator::Kind(kind);
1697                             suggest_disambiguator(
1698                                 disambiguator,
1699                                 diag,
1700                                 path_str,
1701                                 dox,
1702                                 sp,
1703                                 &link_range,
1704                             )
1705                         }
1706
1707                         format!(
1708                             "this link resolves to {}, which is not in the {} namespace",
1709                             item(res),
1710                             expected_ns.descr()
1711                         )
1712                     }
1713                     ResolutionFailure::NoParentItem => {
1714                         diag.level = rustc_errors::Level::Bug;
1715                         "all intra doc links should have a parent item".to_owned()
1716                     }
1717                 };
1718                 if let Some(span) = sp {
1719                     diag.span_label(span, &note);
1720                 } else {
1721                     diag.note(&note);
1722                 }
1723             }
1724         },
1725     );
1726 }
1727
1728 fn anchor_failure(
1729     cx: &DocContext<'_>,
1730     item: &Item,
1731     path_str: &str,
1732     dox: &str,
1733     link_range: Option<Range<usize>>,
1734     failure: AnchorFailure,
1735 ) {
1736     let msg = match failure {
1737         AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str),
1738         AnchorFailure::RustdocAnchorConflict(res) => format!(
1739             "`{}` contains an anchor, but links to {kind}s are already anchored",
1740             path_str,
1741             kind = res.descr(),
1742         ),
1743     };
1744
1745     report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
1746         if let Some(sp) = sp {
1747             diag.span_label(sp, "contains invalid anchor");
1748         }
1749     });
1750 }
1751
1752 fn ambiguity_error(
1753     cx: &DocContext<'_>,
1754     item: &Item,
1755     path_str: &str,
1756     dox: &str,
1757     link_range: Option<Range<usize>>,
1758     candidates: Vec<Res>,
1759 ) {
1760     let mut msg = format!("`{}` is ", path_str);
1761
1762     match candidates.as_slice() {
1763         [first_def, second_def] => {
1764             msg += &format!(
1765                 "both {} {} and {} {}",
1766                 first_def.article(),
1767                 first_def.descr(),
1768                 second_def.article(),
1769                 second_def.descr(),
1770             );
1771         }
1772         _ => {
1773             let mut candidates = candidates.iter().peekable();
1774             while let Some(res) = candidates.next() {
1775                 if candidates.peek().is_some() {
1776                     msg += &format!("{} {}, ", res.article(), res.descr());
1777                 } else {
1778                     msg += &format!("and {} {}", res.article(), res.descr());
1779                 }
1780             }
1781         }
1782     }
1783
1784     report_diagnostic(cx, BROKEN_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
1785         if let Some(sp) = sp {
1786             diag.span_label(sp, "ambiguous link");
1787         } else {
1788             diag.note("ambiguous link");
1789         }
1790
1791         for res in candidates {
1792             let disambiguator = Disambiguator::from_res(res);
1793             suggest_disambiguator(disambiguator, diag, path_str, dox, sp, &link_range);
1794         }
1795     });
1796 }
1797
1798 fn suggest_disambiguator(
1799     disambiguator: Disambiguator,
1800     diag: &mut DiagnosticBuilder<'_>,
1801     path_str: &str,
1802     dox: &str,
1803     sp: Option<rustc_span::Span>,
1804     link_range: &Option<Range<usize>>,
1805 ) {
1806     let suggestion = disambiguator.suggestion();
1807     let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
1808
1809     if let Some(sp) = sp {
1810         let link_range = link_range.as_ref().expect("must have a link range if we have a span");
1811         let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
1812             format!("`{}`", suggestion.as_help(path_str))
1813         } else {
1814             suggestion.as_help(path_str)
1815         };
1816
1817         diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect);
1818     } else {
1819         diag.help(&format!("{}: {}", help, suggestion.as_help(path_str)));
1820     }
1821 }
1822
1823 fn privacy_error(
1824     cx: &DocContext<'_>,
1825     item: &Item,
1826     path_str: &str,
1827     dox: &str,
1828     link_range: Option<Range<usize>>,
1829 ) {
1830     let item_name = item.name.as_deref().unwrap_or("<unknown>");
1831     let msg =
1832         format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
1833
1834     report_diagnostic(cx, PRIVATE_INTRA_DOC_LINKS, &msg, item, dox, &link_range, |diag, sp| {
1835         if let Some(sp) = sp {
1836             diag.span_label(sp, "this item is private");
1837         }
1838
1839         let note_msg = if cx.render_options.document_private {
1840             "this link resolves only because you passed `--document-private-items`, but will break without"
1841         } else {
1842             "this link will resolve properly if you pass `--document-private-items`"
1843         };
1844         diag.note(note_msg);
1845     });
1846 }
1847
1848 /// Given an enum variant's res, return the res of its enum and the associated fragment.
1849 fn handle_variant(
1850     cx: &DocContext<'_>,
1851     res: Res,
1852     extra_fragment: &Option<String>,
1853 ) -> Result<(Res, Option<String>), ErrorKind<'static>> {
1854     use rustc_middle::ty::DefIdTree;
1855
1856     if extra_fragment.is_some() {
1857         return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res)));
1858     }
1859     let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
1860         parent
1861     } else {
1862         return Err(ResolutionFailure::NoParentItem.into());
1863     };
1864     let parent_def = Res::Def(DefKind::Enum, parent);
1865     let variant = cx.tcx.expect_variant_res(res);
1866     Ok((parent_def, Some(format!("variant.{}", variant.ident.name))))
1867 }
1868
1869 const PRIMITIVES: &[(&str, Res)] = &[
1870     ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))),
1871     ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))),
1872     ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))),
1873     ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))),
1874     ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))),
1875     ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))),
1876     ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))),
1877     ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))),
1878     ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))),
1879     ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))),
1880     ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))),
1881     ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))),
1882     ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))),
1883     ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))),
1884     ("str", Res::PrimTy(hir::PrimTy::Str)),
1885     ("bool", Res::PrimTy(hir::PrimTy::Bool)),
1886     ("true", Res::PrimTy(hir::PrimTy::Bool)),
1887     ("false", Res::PrimTy(hir::PrimTy::Bool)),
1888     ("char", Res::PrimTy(hir::PrimTy::Char)),
1889 ];
1890
1891 fn is_primitive(path_str: &str, ns: Namespace) -> Option<(&'static str, Res)> {
1892     if ns == TypeNS {
1893         PRIMITIVES
1894             .iter()
1895             .filter(|x| x.0 == path_str)
1896             .copied()
1897             .map(|x| if x.0 == "true" || x.0 == "false" { ("bool", x.1) } else { x })
1898             .next()
1899     } else {
1900         None
1901     }
1902 }
1903
1904 fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<&'static SmallVec<[DefId; 4]>> {
1905     Some(PrimitiveType::from_symbol(Symbol::intern(path_str))?.impls(cx.tcx))
1906 }