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