]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_intra_doc_links.rs
Rollup merge of #75485 - RalfJung:pin, r=nagisa
[rust.git] / src / librustdoc / passes / collect_intra_doc_links.rs
1 use rustc_ast::ast;
2 use rustc_errors::{Applicability, DiagnosticBuilder};
3 use rustc_expand::base::SyntaxExtensionKind;
4 use rustc_feature::UnstableFeatures;
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::DefId;
12 use rustc_middle::ty;
13 use rustc_resolve::ParentScope;
14 use rustc_session::lint;
15 use rustc_span::hygiene::MacroKind;
16 use rustc_span::symbol::Ident;
17 use rustc_span::symbol::Symbol;
18 use rustc_span::DUMMY_SP;
19
20 use std::cell::Cell;
21 use std::ops::Range;
22
23 use crate::clean::*;
24 use crate::core::DocContext;
25 use crate::fold::DocFolder;
26 use crate::html::markdown::markdown_links;
27 use crate::passes::Pass;
28
29 use super::span_of_attrs;
30
31 pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
32     name: "collect-intra-doc-links",
33     run: collect_intra_doc_links,
34     description: "reads a crate's documentation to resolve intra-doc-links",
35 };
36
37 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
38     if !UnstableFeatures::from_environment().is_nightly_build() {
39         krate
40     } else {
41         let mut coll = LinkCollector::new(cx);
42
43         coll.fold_crate(krate)
44     }
45 }
46
47 enum ErrorKind {
48     ResolutionFailure,
49     AnchorFailure(AnchorFailure),
50 }
51
52 enum AnchorFailure {
53     MultipleAnchors,
54     Primitive,
55     Variant,
56     AssocConstant,
57     AssocType,
58     Field,
59     Method,
60 }
61
62 struct LinkCollector<'a, 'tcx> {
63     cx: &'a DocContext<'tcx>,
64     // NOTE: this may not necessarily be a module in the current crate
65     mod_ids: Vec<DefId>,
66     /// This is used to store the kind of associated items,
67     /// because `clean` and the disambiguator code expect them to be different.
68     /// See the code for associated items on inherent impls for details.
69     kind_side_channel: Cell<Option<DefKind>>,
70 }
71
72 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
73     fn new(cx: &'a DocContext<'tcx>) -> Self {
74         LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
75     }
76
77     fn variant_field(
78         &self,
79         path_str: &str,
80         current_item: &Option<String>,
81         module_id: DefId,
82     ) -> Result<(Res, Option<String>), ErrorKind> {
83         let cx = self.cx;
84
85         let mut split = path_str.rsplitn(3, "::");
86         let variant_field_name =
87             split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
88         let variant_name =
89             split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
90         let path = split
91             .next()
92             .map(|f| {
93                 if f == "self" || f == "Self" {
94                     if let Some(name) = current_item.as_ref() {
95                         return name.clone();
96                     }
97                 }
98                 f.to_owned()
99             })
100             .ok_or(ErrorKind::ResolutionFailure)?;
101         let (_, ty_res) = cx
102             .enter_resolver(|resolver| {
103                 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
104             })
105             .map_err(|_| ErrorKind::ResolutionFailure)?;
106         if let Res::Err = ty_res {
107             return Err(ErrorKind::ResolutionFailure);
108         }
109         let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
110         match ty_res {
111             Res::Def(DefKind::Enum, did) => {
112                 if cx
113                     .tcx
114                     .inherent_impls(did)
115                     .iter()
116                     .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
117                     .any(|item| item.ident.name == variant_name)
118                 {
119                     return Err(ErrorKind::ResolutionFailure);
120                 }
121                 match cx.tcx.type_of(did).kind {
122                     ty::Adt(def, _) if def.is_enum() => {
123                         if def.all_fields().any(|item| item.ident.name == variant_field_name) {
124                             Ok((
125                                 ty_res,
126                                 Some(format!(
127                                     "variant.{}.field.{}",
128                                     variant_name, variant_field_name
129                                 )),
130                             ))
131                         } else {
132                             Err(ErrorKind::ResolutionFailure)
133                         }
134                     }
135                     _ => Err(ErrorKind::ResolutionFailure),
136                 }
137             }
138             _ => Err(ErrorKind::ResolutionFailure),
139         }
140     }
141
142     /// Resolves a string as a macro.
143     fn macro_resolve(&self, path_str: &str, parent_id: Option<DefId>) -> Option<Res> {
144         let cx = self.cx;
145         let path = ast::Path::from_ident(Ident::from_str(path_str));
146         cx.enter_resolver(|resolver| {
147             if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
148                 &path,
149                 None,
150                 &ParentScope::module(resolver.graph_root()),
151                 false,
152                 false,
153             ) {
154                 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
155                     return Some(res.map_id(|_| panic!("unexpected id")));
156                 }
157             }
158             if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
159                 return Some(res.map_id(|_| panic!("unexpected id")));
160             }
161             if let Some(module_id) = parent_id {
162                 if let Ok((_, res)) =
163                     resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
164                 {
165                     // don't resolve builtins like `#[derive]`
166                     if let Res::Def(..) = res {
167                         let res = res.map_id(|_| panic!("unexpected node_id"));
168                         return Some(res);
169                     }
170                 }
171             } else {
172                 debug!("attempting to resolve item without parent module: {}", path_str);
173             }
174             None
175         })
176     }
177     /// Resolves a string as a path within a particular namespace. Also returns an optional
178     /// URL fragment in the case of variants and methods.
179     fn resolve(
180         &self,
181         path_str: &str,
182         disambiguator: Option<Disambiguator>,
183         ns: Namespace,
184         current_item: &Option<String>,
185         parent_id: Option<DefId>,
186         extra_fragment: &Option<String>,
187         item_opt: Option<&Item>,
188     ) -> Result<(Res, Option<String>), ErrorKind> {
189         let cx = self.cx;
190
191         // In case we're in a module, try to resolve the relative path.
192         if let Some(module_id) = parent_id {
193             let result = cx.enter_resolver(|resolver| {
194                 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
195             });
196             debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
197             let result = match result {
198                 Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
199                 _ => result.map_err(|_| ErrorKind::ResolutionFailure),
200             };
201
202             if let Ok((_, res)) = result {
203                 let res = res.map_id(|_| panic!("unexpected node_id"));
204                 // In case this is a trait item, skip the
205                 // early return and try looking for the trait.
206                 let value = match res {
207                     Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
208                     Res::Def(DefKind::AssocTy, _) => false,
209                     Res::Def(DefKind::Variant, _) => {
210                         return handle_variant(cx, res, extra_fragment);
211                     }
212                     // Not a trait item; just return what we found.
213                     Res::PrimTy(..) => {
214                         if extra_fragment.is_some() {
215                             return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
216                         }
217                         return Ok((res, Some(path_str.to_owned())));
218                     }
219                     Res::Def(DefKind::Mod, _) => {
220                         // This resolved to a module, but we want primitive types to take precedence instead.
221                         if matches!(
222                             disambiguator,
223                             None | Some(Disambiguator::Namespace(Namespace::TypeNS))
224                         ) {
225                             if let Some(prim) = is_primitive(path_str, ns) {
226                                 if extra_fragment.is_some() {
227                                     return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
228                                 }
229                                 return Ok((prim, Some(path_str.to_owned())));
230                             }
231                         }
232                         return Ok((res, extra_fragment.clone()));
233                     }
234                     _ => {
235                         return Ok((res, extra_fragment.clone()));
236                     }
237                 };
238
239                 if value != (ns == ValueNS) {
240                     return Err(ErrorKind::ResolutionFailure);
241                 }
242             } else if let Some(prim) = is_primitive(path_str, ns) {
243                 if extra_fragment.is_some() {
244                     return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
245                 }
246                 return Ok((prim, Some(path_str.to_owned())));
247             } else {
248                 // If resolution failed, it may still be a method
249                 // because methods are not handled by the resolver
250                 // If so, bail when we're not looking for a value.
251                 if ns != ValueNS {
252                     return Err(ErrorKind::ResolutionFailure);
253                 }
254             }
255
256             // Try looking for methods and associated items.
257             let mut split = path_str.rsplitn(2, "::");
258             let item_name =
259                 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
260             let path = split
261                 .next()
262                 .map(|f| {
263                     if f == "self" || f == "Self" {
264                         if let Some(name) = current_item.as_ref() {
265                             return name.clone();
266                         }
267                     }
268                     f.to_owned()
269                 })
270                 .ok_or(ErrorKind::ResolutionFailure)?;
271
272             if let Some(prim) = is_primitive(&path, TypeNS) {
273                 let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?;
274                 return cx
275                     .tcx
276                     .associated_items(did)
277                     .filter_by_name_unhygienic(item_name)
278                     .next()
279                     .and_then(|item| match item.kind {
280                         ty::AssocKind::Fn => Some("method"),
281                         _ => None,
282                     })
283                     .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))))
284                     .ok_or(ErrorKind::ResolutionFailure);
285             }
286
287             let (_, ty_res) = cx
288                 .enter_resolver(|resolver| {
289                     resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
290                 })
291                 .map_err(|_| ErrorKind::ResolutionFailure)?;
292             if let Res::Err = ty_res {
293                 return self.variant_field(path_str, current_item, module_id);
294             }
295             let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
296             match ty_res {
297                 Res::Def(
298                     DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias,
299                     did,
300                 ) => {
301                     // Checks if item_name belongs to `impl SomeItem`
302                     let impl_item = cx
303                         .tcx
304                         .inherent_impls(did)
305                         .iter()
306                         .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order())
307                         .find(|item| item.ident.name == item_name);
308                     let trait_item = item_opt
309                         .and_then(|item| self.cx.as_local_hir_id(item.def_id))
310                         .and_then(|item_hir| {
311                             // Checks if item_name belongs to `impl SomeTrait for SomeItem`
312                             let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
313                             let item_parent = self.cx.tcx.hir().find(parent_hir);
314                             match item_parent {
315                                 Some(hir::Node::Item(hir::Item {
316                                     kind: hir::ItemKind::Impl { of_trait: Some(_), self_ty, .. },
317                                     ..
318                                 })) => cx
319                                     .tcx
320                                     .associated_item_def_ids(self_ty.hir_id.owner)
321                                     .iter()
322                                     .map(|child| {
323                                         let associated_item = cx.tcx.associated_item(*child);
324                                         associated_item
325                                     })
326                                     .find(|child| child.ident.name == item_name),
327                                 _ => None,
328                             }
329                         });
330                     let item = match (impl_item, trait_item) {
331                         (Some(from_impl), Some(_)) => {
332                             // Although it's ambiguous, return impl version for compat. sake.
333                             // To handle that properly resolve() would have to support
334                             // something like
335                             // [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
336                             Some(from_impl)
337                         }
338                         (None, Some(from_trait)) => Some(from_trait),
339                         (Some(from_impl), None) => Some(from_impl),
340                         _ => None,
341                     };
342
343                     if let Some(item) = item {
344                         let out = match item.kind {
345                             ty::AssocKind::Fn if ns == ValueNS => "method",
346                             ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
347                             ty::AssocKind::Type if ns == ValueNS => "associatedtype",
348                             _ => return self.variant_field(path_str, current_item, module_id),
349                         };
350                         if extra_fragment.is_some() {
351                             Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Fn {
352                                 AnchorFailure::Method
353                             } else {
354                                 AnchorFailure::AssocConstant
355                             }))
356                         } else {
357                             // HACK(jynelson): `clean` expects the type, not the associated item.
358                             // but the disambiguator logic expects the associated item.
359                             // Store the kind in a side channel so that only the disambiguator logic looks at it.
360                             self.kind_side_channel.replace(Some(item.kind.as_def_kind()));
361                             Ok((ty_res, Some(format!("{}.{}", out, item_name))))
362                         }
363                     } else {
364                         match cx.tcx.type_of(did).kind {
365                             ty::Adt(def, _) => {
366                                 if let Some(item) = if def.is_enum() {
367                                     def.all_fields().find(|item| item.ident.name == item_name)
368                                 } else {
369                                     def.non_enum_variant()
370                                         .fields
371                                         .iter()
372                                         .find(|item| item.ident.name == item_name)
373                                 } {
374                                     if extra_fragment.is_some() {
375                                         Err(ErrorKind::AnchorFailure(if def.is_enum() {
376                                             AnchorFailure::Variant
377                                         } else {
378                                             AnchorFailure::Field
379                                         }))
380                                     } else {
381                                         Ok((
382                                             ty_res,
383                                             Some(format!(
384                                                 "{}.{}",
385                                                 if def.is_enum() {
386                                                     "variant"
387                                                 } else {
388                                                     "structfield"
389                                                 },
390                                                 item.ident
391                                             )),
392                                         ))
393                                     }
394                                 } else {
395                                     self.variant_field(path_str, current_item, module_id)
396                                 }
397                             }
398                             _ => self.variant_field(path_str, current_item, module_id),
399                         }
400                     }
401                 }
402                 Res::Def(DefKind::Trait, did) => {
403                     let item = cx
404                         .tcx
405                         .associated_item_def_ids(did)
406                         .iter()
407                         .map(|item| cx.tcx.associated_item(*item))
408                         .find(|item| item.ident.name == item_name);
409                     if let Some(item) = item {
410                         let kind =
411                             match item.kind {
412                                 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
413                                 ty::AssocKind::Type if ns == TypeNS => "associatedtype",
414                                 ty::AssocKind::Fn if ns == ValueNS => {
415                                     if item.defaultness.has_value() { "method" } else { "tymethod" }
416                                 }
417                                 _ => return self.variant_field(path_str, current_item, module_id),
418                             };
419
420                         if extra_fragment.is_some() {
421                             Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const {
422                                 AnchorFailure::AssocConstant
423                             } else if item.kind == ty::AssocKind::Type {
424                                 AnchorFailure::AssocType
425                             } else {
426                                 AnchorFailure::Method
427                             }))
428                         } else {
429                             let res = Res::Def(item.kind.as_def_kind(), item.def_id);
430                             Ok((res, Some(format!("{}.{}", kind, item_name))))
431                         }
432                     } else {
433                         self.variant_field(path_str, current_item, module_id)
434                     }
435                 }
436                 _ => self.variant_field(path_str, current_item, module_id),
437             }
438         } else {
439             debug!("attempting to resolve item without parent module: {}", path_str);
440             Err(ErrorKind::ResolutionFailure)
441         }
442     }
443 }
444
445 /// Check for resolve collisions between a trait and its derive
446 ///
447 /// These are common and we should just resolve to the trait in that case
448 fn is_derive_trait_collision<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
449     if let PerNS {
450         type_ns: Some((Res::Def(DefKind::Trait, _), _)),
451         macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)),
452         ..
453     } = *ns
454     {
455         true
456     } else {
457         false
458     }
459 }
460
461 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
462     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
463         use rustc_middle::ty::DefIdTree;
464
465         let parent_node = if item.is_fake() {
466             // FIXME: is this correct?
467             None
468         } else {
469             let mut current = item.def_id;
470             // The immediate parent might not always be a module.
471             // Find the first parent which is.
472             loop {
473                 if let Some(parent) = self.cx.tcx.parent(current) {
474                     if self.cx.tcx.def_kind(parent) == DefKind::Mod {
475                         break Some(parent);
476                     }
477                     current = parent;
478                 } else {
479                     break None;
480                 }
481             }
482         };
483
484         if parent_node.is_some() {
485             trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
486         }
487
488         let current_item = match item.inner {
489             ModuleItem(..) => {
490                 if item.attrs.inner_docs {
491                     if item.def_id.is_top_level_module() { item.name.clone() } else { None }
492                 } else {
493                     match parent_node.or(self.mod_ids.last().copied()) {
494                         Some(parent) if !parent.is_top_level_module() => {
495                             // FIXME: can we pull the parent module's name from elsewhere?
496                             Some(self.cx.tcx.item_name(parent).to_string())
497                         }
498                         _ => None,
499                     }
500                 }
501             }
502             ImplItem(Impl { ref for_, .. }) => {
503                 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
504             }
505             // we don't display docs on `extern crate` items anyway, so don't process them.
506             ExternCrateItem(..) => {
507                 debug!("ignoring extern crate item {:?}", item.def_id);
508                 return self.fold_item_recur(item);
509             }
510             ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
511             MacroItem(..) => None,
512             _ => item.name.clone(),
513         };
514
515         if item.is_mod() && item.attrs.inner_docs {
516             self.mod_ids.push(item.def_id);
517         }
518
519         let cx = self.cx;
520         let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
521         trace!("got documentation '{}'", dox);
522
523         // find item's parent to resolve `Self` in item's docs below
524         let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| {
525             let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir);
526             let item_parent = self.cx.tcx.hir().find(parent_hir);
527             match item_parent {
528                 Some(hir::Node::Item(hir::Item {
529                     kind:
530                         hir::ItemKind::Impl {
531                             self_ty:
532                                 hir::Ty {
533                                     kind:
534                                         hir::TyKind::Path(hir::QPath::Resolved(
535                                             _,
536                                             hir::Path { segments, .. },
537                                         )),
538                                     ..
539                                 },
540                             ..
541                         },
542                     ..
543                 })) => segments.first().map(|seg| seg.ident.to_string()),
544                 Some(hir::Node::Item(hir::Item {
545                     ident, kind: hir::ItemKind::Enum(..), ..
546                 }))
547                 | Some(hir::Node::Item(hir::Item {
548                     ident, kind: hir::ItemKind::Struct(..), ..
549                 }))
550                 | Some(hir::Node::Item(hir::Item {
551                     ident, kind: hir::ItemKind::Union(..), ..
552                 }))
553                 | Some(hir::Node::Item(hir::Item {
554                     ident, kind: hir::ItemKind::Trait(..), ..
555                 })) => Some(ident.to_string()),
556                 _ => None,
557             }
558         });
559
560         for (ori_link, link_range) in markdown_links(&dox) {
561             trace!("considering link '{}'", ori_link);
562
563             // Bail early for real links.
564             if ori_link.contains('/') {
565                 continue;
566             }
567
568             // [] is mostly likely not supposed to be a link
569             if ori_link.is_empty() {
570                 continue;
571             }
572
573             let link = ori_link.replace("`", "");
574             let parts = link.split('#').collect::<Vec<_>>();
575             let (link, extra_fragment) = if parts.len() > 2 {
576                 anchor_failure(cx, &item, &link, &dox, link_range, AnchorFailure::MultipleAnchors);
577                 continue;
578             } else if parts.len() == 2 {
579                 if parts[0].trim().is_empty() {
580                     // This is an anchor to an element of the current page, nothing to do in here!
581                     continue;
582                 }
583                 (parts[0].to_owned(), Some(parts[1].to_owned()))
584             } else {
585                 (parts[0].to_owned(), None)
586             };
587             let resolved_self;
588             let mut path_str;
589             let disambiguator;
590             let (res, fragment) = {
591                 path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
592                     disambiguator = Some(d);
593                     path
594                 } else {
595                     disambiguator = None;
596                     &link
597                 }
598                 .trim();
599
600                 if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
601                     continue;
602                 }
603
604                 // In order to correctly resolve intra-doc-links we need to
605                 // pick a base AST node to work from.  If the documentation for
606                 // this module came from an inner comment (//!) then we anchor
607                 // our name resolution *inside* the module.  If, on the other
608                 // hand it was an outer comment (///) then we anchor the name
609                 // resolution in the parent module on the basis that the names
610                 // used are more likely to be intended to be parent names.  For
611                 // this, we set base_node to None for inner comments since
612                 // we've already pushed this node onto the resolution stack but
613                 // for outer comments we explicitly try and resolve against the
614                 // parent_node first.
615                 let base_node = if item.is_mod() && item.attrs.inner_docs {
616                     self.mod_ids.last().copied()
617                 } else {
618                     parent_node
619                 };
620
621                 // replace `Self` with suitable item's parent name
622                 if path_str.starts_with("Self::") {
623                     if let Some(ref name) = parent_name {
624                         resolved_self = format!("{}::{}", name, &path_str[6..]);
625                         path_str = &resolved_self;
626                     }
627                 }
628
629                 match disambiguator.map(Disambiguator::ns) {
630                     Some(ns @ ValueNS) => {
631                         match self.resolve(
632                             path_str,
633                             disambiguator,
634                             ns,
635                             &current_item,
636                             base_node,
637                             &extra_fragment,
638                             Some(&item),
639                         ) {
640                             Ok(res) => res,
641                             Err(ErrorKind::ResolutionFailure) => {
642                                 resolution_failure(cx, &item, path_str, &dox, link_range);
643                                 // This could just be a normal link or a broken link
644                                 // we could potentially check if something is
645                                 // "intra-doc-link-like" and warn in that case.
646                                 continue;
647                             }
648                             Err(ErrorKind::AnchorFailure(msg)) => {
649                                 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
650                                 continue;
651                             }
652                         }
653                     }
654                     Some(ns @ TypeNS) => {
655                         match self.resolve(
656                             path_str,
657                             disambiguator,
658                             ns,
659                             &current_item,
660                             base_node,
661                             &extra_fragment,
662                             Some(&item),
663                         ) {
664                             Ok(res) => res,
665                             Err(ErrorKind::ResolutionFailure) => {
666                                 resolution_failure(cx, &item, path_str, &dox, link_range);
667                                 // This could just be a normal link.
668                                 continue;
669                             }
670                             Err(ErrorKind::AnchorFailure(msg)) => {
671                                 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
672                                 continue;
673                             }
674                         }
675                     }
676                     None => {
677                         // Try everything!
678                         let mut candidates = PerNS {
679                             macro_ns: self
680                                 .macro_resolve(path_str, base_node)
681                                 .map(|res| (res, extra_fragment.clone())),
682                             type_ns: match self.resolve(
683                                 path_str,
684                                 disambiguator,
685                                 TypeNS,
686                                 &current_item,
687                                 base_node,
688                                 &extra_fragment,
689                                 Some(&item),
690                             ) {
691                                 Err(ErrorKind::AnchorFailure(msg)) => {
692                                     anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
693                                     continue;
694                                 }
695                                 x => x.ok(),
696                             },
697                             value_ns: match self.resolve(
698                                 path_str,
699                                 disambiguator,
700                                 ValueNS,
701                                 &current_item,
702                                 base_node,
703                                 &extra_fragment,
704                                 Some(&item),
705                             ) {
706                                 Err(ErrorKind::AnchorFailure(msg)) => {
707                                     anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
708                                     continue;
709                                 }
710                                 x => x.ok(),
711                             }
712                             .and_then(|(res, fragment)| {
713                                 // Constructors are picked up in the type namespace.
714                                 match res {
715                                     Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
716                                     _ => match (fragment, extra_fragment) {
717                                         (Some(fragment), Some(_)) => {
718                                             // Shouldn't happen but who knows?
719                                             Some((res, Some(fragment)))
720                                         }
721                                         (fragment, None) | (None, fragment) => {
722                                             Some((res, fragment))
723                                         }
724                                     },
725                                 }
726                             }),
727                         };
728
729                         if candidates.is_empty() {
730                             resolution_failure(cx, &item, path_str, &dox, link_range);
731                             // this could just be a normal link
732                             continue;
733                         }
734
735                         let len = candidates.clone().present_items().count();
736
737                         if len == 1 {
738                             candidates.present_items().next().unwrap()
739                         } else if len == 2 && is_derive_trait_collision(&candidates) {
740                             candidates.type_ns.unwrap()
741                         } else {
742                             if is_derive_trait_collision(&candidates) {
743                                 candidates.macro_ns = None;
744                             }
745                             ambiguity_error(
746                                 cx,
747                                 &item,
748                                 path_str,
749                                 &dox,
750                                 link_range,
751                                 candidates.map(|candidate| candidate.map(|(res, _)| res)),
752                             );
753                             continue;
754                         }
755                     }
756                     Some(MacroNS) => {
757                         if let Some(res) = self.macro_resolve(path_str, base_node) {
758                             (res, extra_fragment)
759                         } else {
760                             resolution_failure(cx, &item, path_str, &dox, link_range);
761                             continue;
762                         }
763                     }
764                 }
765             };
766
767             if let Res::PrimTy(_) = res {
768                 item.attrs.links.push((ori_link, None, fragment));
769             } else {
770                 debug!("intra-doc link to {} resolved to {:?}", path_str, res);
771
772                 // Disallow e.g. linking to enums with `struct@`
773                 if let Res::Def(kind, id) = res {
774                     debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
775                     match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
776                         | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
777                         // NOTE: this allows 'method' to mean both normal functions and associated functions
778                         // This can't cause ambiguity because both are in the same namespace.
779                         | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
780                         // These are namespaces; allow anything in the namespace to match
781                         | (_, Some(Disambiguator::Namespace(_)))
782                         // If no disambiguator given, allow anything
783                         | (_, None)
784                         // All of these are valid, so do nothing
785                         => {}
786                         (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
787                         (_, Some(Disambiguator::Kind(expected))) => {
788                             // The resolved item did not match the disambiguator; give a better error than 'not found'
789                             let msg = format!("incompatible link kind for `{}`", path_str);
790                             report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
791                                 // HACK(jynelson): by looking at the source I saw the DefId we pass
792                                 // for `expected.descr()` doesn't matter, since it's not a crate
793                                 let note = format!("this link resolved to {} {}, which is not {} {}", kind.article(), kind.descr(id), expected.article(), expected.descr(id));
794                                 let suggestion = Disambiguator::display_for(kind, path_str);
795                                 let help_msg = format!("to link to the {}, use its disambiguator", kind.descr(id));
796                                 diag.note(&note);
797                                 if let Some(sp) = sp {
798                                     diag.span_suggestion(sp, &help_msg, suggestion, Applicability::MaybeIncorrect);
799                                 } else {
800                                     diag.help(&format!("{}: {}", help_msg, suggestion));
801                                 }
802                             });
803                             continue;
804                         }
805                     }
806                 }
807
808                 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
809                 if let Some((src_id, dst_id)) = res
810                     .opt_def_id()
811                     .and_then(|def_id| def_id.as_local())
812                     .and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
813                 {
814                     use rustc_hir::def_id::LOCAL_CRATE;
815
816                     let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
817                     let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
818
819                     if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
820                         && !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
821                     {
822                         privacy_error(cx, &item, &path_str, &dox, link_range);
823                         continue;
824                     }
825                 }
826                 let id = register_res(cx, res);
827                 item.attrs.links.push((ori_link, Some(id), fragment));
828             }
829         }
830
831         if item.is_mod() && !item.attrs.inner_docs {
832             self.mod_ids.push(item.def_id);
833         }
834
835         if item.is_mod() {
836             let ret = self.fold_item_recur(item);
837
838             self.mod_ids.pop();
839
840             ret
841         } else {
842             self.fold_item_recur(item)
843         }
844     }
845
846     // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
847     // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
848     // will never resolve properly
849     fn fold_crate(&mut self, mut c: Crate) -> Crate {
850         c.module = c.module.take().and_then(|module| self.fold_item(module));
851
852         c
853     }
854 }
855
856 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
857 enum Disambiguator {
858     Kind(DefKind),
859     Namespace(Namespace),
860 }
861
862 impl Disambiguator {
863     /// (disambiguator, path_str)
864     fn from_str(link: &str) -> Result<(Self, &str), ()> {
865         use Disambiguator::{Kind, Namespace as NS};
866
867         let find_suffix = || {
868             let suffixes = [
869                 ("!()", DefKind::Macro(MacroKind::Bang)),
870                 ("()", DefKind::Fn),
871                 ("!", DefKind::Macro(MacroKind::Bang)),
872             ];
873             for &(suffix, kind) in &suffixes {
874                 if link.ends_with(suffix) {
875                     return Ok((Kind(kind), link.trim_end_matches(suffix)));
876                 }
877             }
878             Err(())
879         };
880
881         if let Some(idx) = link.find('@') {
882             let (prefix, rest) = link.split_at(idx);
883             let d = match prefix {
884                 "struct" => Kind(DefKind::Struct),
885                 "enum" => Kind(DefKind::Enum),
886                 "trait" => Kind(DefKind::Trait),
887                 "union" => Kind(DefKind::Union),
888                 "module" | "mod" => Kind(DefKind::Mod),
889                 "const" | "constant" => Kind(DefKind::Const),
890                 "static" => Kind(DefKind::Static),
891                 "function" | "fn" | "method" => Kind(DefKind::Fn),
892                 "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
893                 "type" => NS(Namespace::TypeNS),
894                 "value" => NS(Namespace::ValueNS),
895                 "macro" => NS(Namespace::MacroNS),
896                 _ => return find_suffix(),
897             };
898             Ok((d, &rest[1..]))
899         } else {
900             find_suffix()
901         }
902     }
903
904     fn display_for(kind: DefKind, path_str: &str) -> String {
905         if kind == DefKind::Macro(MacroKind::Bang) {
906             return format!("{}!", path_str);
907         } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
908             return format!("{}()", path_str);
909         }
910         let prefix = match kind {
911             DefKind::Struct => "struct",
912             DefKind::Enum => "enum",
913             DefKind::Trait => "trait",
914             DefKind::Union => "union",
915             DefKind::Mod => "mod",
916             DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
917                 "const"
918             }
919             DefKind::Static => "static",
920             DefKind::Macro(MacroKind::Derive) => "derive",
921             // Now handle things that don't have a specific disambiguator
922             _ => match kind
923                 .ns()
924                 .expect("tried to calculate a disambiguator for a def without a namespace?")
925             {
926                 Namespace::TypeNS => "type",
927                 Namespace::ValueNS => "value",
928                 Namespace::MacroNS => "macro",
929             },
930         };
931         format!("{}@{}", prefix, path_str)
932     }
933
934     fn ns(self) -> Namespace {
935         match self {
936             Self::Namespace(n) => n,
937             Self::Kind(k) => {
938                 k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
939             }
940         }
941     }
942 }
943
944 /// Reports a diagnostic for an intra-doc link.
945 ///
946 /// If no link range is provided, or the source span of the link cannot be determined, the span of
947 /// the entire documentation block is used for the lint. If a range is provided but the span
948 /// calculation fails, a note is added to the diagnostic pointing to the link in the markdown.
949 ///
950 /// The `decorate` callback is invoked in all cases to allow further customization of the
951 /// diagnostic before emission. If the span of the link was able to be determined, the second
952 /// parameter of the callback will contain it, and the primary span of the diagnostic will be set
953 /// to it.
954 fn report_diagnostic(
955     cx: &DocContext<'_>,
956     msg: &str,
957     item: &Item,
958     dox: &str,
959     link_range: Option<Range<usize>>,
960     decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
961 ) {
962     let hir_id = match cx.as_local_hir_id(item.def_id) {
963         Some(hir_id) => hir_id,
964         None => {
965             // If non-local, no need to check anything.
966             info!("ignoring warning from parent crate: {}", msg);
967             return;
968         }
969     };
970
971     let attrs = &item.attrs;
972     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
973
974     cx.tcx.struct_span_lint_hir(lint::builtin::BROKEN_INTRA_DOC_LINKS, hir_id, sp, |lint| {
975         let mut diag = lint.build(msg);
976
977         let span = link_range
978             .as_ref()
979             .and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
980
981         if let Some(link_range) = link_range {
982             if let Some(sp) = span {
983                 diag.set_span(sp);
984             } else {
985                 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
986                 //                       ^     ~~~~
987                 //                       |     link_range
988                 //                       last_new_line_offset
989                 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
990                 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
991
992                 // Print the line containing the `link_range` and manually mark it with '^'s.
993                 diag.note(&format!(
994                     "the link appears in this line:\n\n{line}\n\
995                          {indicator: <before$}{indicator:^<found$}",
996                     line = line,
997                     indicator = "",
998                     before = link_range.start - last_new_line_offset,
999                     found = link_range.len(),
1000                 ));
1001             }
1002         }
1003
1004         decorate(&mut diag, span);
1005
1006         diag.emit();
1007     });
1008 }
1009
1010 fn resolution_failure(
1011     cx: &DocContext<'_>,
1012     item: &Item,
1013     path_str: &str,
1014     dox: &str,
1015     link_range: Option<Range<usize>>,
1016 ) {
1017     report_diagnostic(
1018         cx,
1019         &format!("unresolved link to `{}`", path_str),
1020         item,
1021         dox,
1022         link_range,
1023         |diag, sp| {
1024             if let Some(sp) = sp {
1025                 diag.span_label(sp, "unresolved link");
1026             }
1027
1028             diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#);
1029         },
1030     );
1031 }
1032
1033 fn anchor_failure(
1034     cx: &DocContext<'_>,
1035     item: &Item,
1036     path_str: &str,
1037     dox: &str,
1038     link_range: Option<Range<usize>>,
1039     failure: AnchorFailure,
1040 ) {
1041     let msg = match failure {
1042         AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str),
1043         AnchorFailure::Primitive
1044         | AnchorFailure::Variant
1045         | AnchorFailure::AssocConstant
1046         | AnchorFailure::AssocType
1047         | AnchorFailure::Field
1048         | AnchorFailure::Method => {
1049             let kind = match failure {
1050                 AnchorFailure::Primitive => "primitive type",
1051                 AnchorFailure::Variant => "enum variant",
1052                 AnchorFailure::AssocConstant => "associated constant",
1053                 AnchorFailure::AssocType => "associated type",
1054                 AnchorFailure::Field => "struct field",
1055                 AnchorFailure::Method => "method",
1056                 AnchorFailure::MultipleAnchors => unreachable!("should be handled already"),
1057             };
1058
1059             format!(
1060                 "`{}` contains an anchor, but links to {kind}s are already anchored",
1061                 path_str,
1062                 kind = kind
1063             )
1064         }
1065     };
1066
1067     report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| {
1068         if let Some(sp) = sp {
1069             diag.span_label(sp, "contains invalid anchor");
1070         }
1071     });
1072 }
1073
1074 fn ambiguity_error(
1075     cx: &DocContext<'_>,
1076     item: &Item,
1077     path_str: &str,
1078     dox: &str,
1079     link_range: Option<Range<usize>>,
1080     candidates: PerNS<Option<Res>>,
1081 ) {
1082     let mut msg = format!("`{}` is ", path_str);
1083
1084     let candidates = [TypeNS, ValueNS, MacroNS]
1085         .iter()
1086         .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
1087         .collect::<Vec<_>>();
1088     match candidates.as_slice() {
1089         [(first_def, _), (second_def, _)] => {
1090             msg += &format!(
1091                 "both {} {} and {} {}",
1092                 first_def.article(),
1093                 first_def.descr(),
1094                 second_def.article(),
1095                 second_def.descr(),
1096             );
1097         }
1098         _ => {
1099             let mut candidates = candidates.iter().peekable();
1100             while let Some((res, _)) = candidates.next() {
1101                 if candidates.peek().is_some() {
1102                     msg += &format!("{} {}, ", res.article(), res.descr());
1103                 } else {
1104                     msg += &format!("and {} {}", res.article(), res.descr());
1105                 }
1106             }
1107         }
1108     }
1109
1110     report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| {
1111         if let Some(sp) = sp {
1112             diag.span_label(sp, "ambiguous link");
1113
1114             let link_range = link_range.expect("must have a link range if we have a span");
1115
1116             for (res, ns) in candidates {
1117                 let (action, mut suggestion) = match res {
1118                     Res::Def(DefKind::AssocFn | DefKind::Fn, _) => {
1119                         ("add parentheses", format!("{}()", path_str))
1120                     }
1121                     Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
1122                         ("add an exclamation mark", format!("{}!", path_str))
1123                     }
1124                     _ => {
1125                         let type_ = match (res, ns) {
1126                             (Res::Def(DefKind::Const, _), _) => "const",
1127                             (Res::Def(DefKind::Static, _), _) => "static",
1128                             (Res::Def(DefKind::Struct, _), _) => "struct",
1129                             (Res::Def(DefKind::Enum, _), _) => "enum",
1130                             (Res::Def(DefKind::Union, _), _) => "union",
1131                             (Res::Def(DefKind::Trait, _), _) => "trait",
1132                             (Res::Def(DefKind::Mod, _), _) => "module",
1133                             (_, TypeNS) => "type",
1134                             (_, ValueNS) => "value",
1135                             (Res::Def(DefKind::Macro(MacroKind::Derive), _), MacroNS) => "derive",
1136                             (_, MacroNS) => "macro",
1137                         };
1138
1139                         // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
1140                         ("prefix with the item type", format!("{}@{}", type_, path_str))
1141                     }
1142                 };
1143
1144                 if dox.bytes().nth(link_range.start) == Some(b'`') {
1145                     suggestion = format!("`{}`", suggestion);
1146                 }
1147
1148                 // FIXME: Create a version of this suggestion for when we don't have the span.
1149                 diag.span_suggestion(
1150                     sp,
1151                     &format!("to link to the {}, {}", res.descr(), action),
1152                     suggestion,
1153                     Applicability::MaybeIncorrect,
1154                 );
1155             }
1156         }
1157     });
1158 }
1159
1160 fn privacy_error(
1161     cx: &DocContext<'_>,
1162     item: &Item,
1163     path_str: &str,
1164     dox: &str,
1165     link_range: Option<Range<usize>>,
1166 ) {
1167     let item_name = item.name.as_deref().unwrap_or("<unknown>");
1168     let msg =
1169         format!("public documentation for `{}` links to private item `{}`", item_name, path_str);
1170
1171     report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| {
1172         if let Some(sp) = sp {
1173             diag.span_label(sp, "this item is private");
1174         }
1175
1176         let note_msg = if cx.render_options.document_private {
1177             "this link resolves only because you passed `--document-private-items`, but will break without"
1178         } else {
1179             "this link will resolve properly if you pass `--document-private-items`"
1180         };
1181         diag.note(note_msg);
1182     });
1183 }
1184
1185 /// Given an enum variant's res, return the res of its enum and the associated fragment.
1186 fn handle_variant(
1187     cx: &DocContext<'_>,
1188     res: Res,
1189     extra_fragment: &Option<String>,
1190 ) -> Result<(Res, Option<String>), ErrorKind> {
1191     use rustc_middle::ty::DefIdTree;
1192
1193     if extra_fragment.is_some() {
1194         return Err(ErrorKind::AnchorFailure(AnchorFailure::Variant));
1195     }
1196     let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
1197         parent
1198     } else {
1199         return Err(ErrorKind::ResolutionFailure);
1200     };
1201     let parent_def = Res::Def(DefKind::Enum, parent);
1202     let variant = cx.tcx.expect_variant_res(res);
1203     Ok((parent_def, Some(format!("variant.{}", variant.ident.name))))
1204 }
1205
1206 const PRIMITIVES: &[(&str, Res)] = &[
1207     ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U8))),
1208     ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U16))),
1209     ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U32))),
1210     ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U64))),
1211     ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::U128))),
1212     ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::ast::UintTy::Usize))),
1213     ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I8))),
1214     ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I16))),
1215     ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I32))),
1216     ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I64))),
1217     ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::I128))),
1218     ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::ast::IntTy::Isize))),
1219     ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F32))),
1220     ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::ast::FloatTy::F64))),
1221     ("str", Res::PrimTy(hir::PrimTy::Str)),
1222     ("bool", Res::PrimTy(hir::PrimTy::Bool)),
1223     ("char", Res::PrimTy(hir::PrimTy::Char)),
1224 ];
1225
1226 fn is_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
1227     if ns == TypeNS { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) } else { None }
1228 }
1229
1230 fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<DefId> {
1231     let tcx = cx.tcx;
1232     match path_str {
1233         "u8" => tcx.lang_items().u8_impl(),
1234         "u16" => tcx.lang_items().u16_impl(),
1235         "u32" => tcx.lang_items().u32_impl(),
1236         "u64" => tcx.lang_items().u64_impl(),
1237         "u128" => tcx.lang_items().u128_impl(),
1238         "usize" => tcx.lang_items().usize_impl(),
1239         "i8" => tcx.lang_items().i8_impl(),
1240         "i16" => tcx.lang_items().i16_impl(),
1241         "i32" => tcx.lang_items().i32_impl(),
1242         "i64" => tcx.lang_items().i64_impl(),
1243         "i128" => tcx.lang_items().i128_impl(),
1244         "isize" => tcx.lang_items().isize_impl(),
1245         "f32" => tcx.lang_items().f32_impl(),
1246         "f64" => tcx.lang_items().f64_impl(),
1247         "str" => tcx.lang_items().str_impl(),
1248         "bool" => tcx.lang_items().bool_impl(),
1249         "char" => tcx.lang_items().char_impl(),
1250         _ => None,
1251     }
1252 }