]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_intra_doc_links.rs
Rollup merge of #65857 - kinnison:kinnison/issue-55364, r=Manisheart,GuillaumeGomez
[rust.git] / src / librustdoc / passes / collect_intra_doc_links.rs
1 use errors::Applicability;
2 use rustc::hir::def::{Res, DefKind, Namespace::{self, *}, PerNS};
3 use rustc::hir::def_id::DefId;
4 use rustc::hir;
5 use rustc::lint as lint;
6 use rustc::ty;
7 use rustc_resolve::ParentScope;
8 use syntax;
9 use syntax::ast::{self, Ident};
10 use syntax_expand::base::SyntaxExtensionKind;
11 use syntax::feature_gate::UnstableFeatures;
12 use syntax::symbol::Symbol;
13 use syntax_pos::DUMMY_SP;
14
15 use std::ops::Range;
16
17 use crate::core::DocContext;
18 use crate::fold::DocFolder;
19 use crate::html::markdown::markdown_links;
20 use crate::clean::*;
21 use crate::passes::{look_for_tests, Pass};
22
23 use super::span_of_attrs;
24
25 pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
26     name: "collect-intra-doc-links",
27     pass: collect_intra_doc_links,
28     description: "reads a crate's documentation to resolve intra-doc-links",
29 };
30
31 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
32     if !UnstableFeatures::from_environment().is_nightly_build() {
33         krate
34     } else {
35         let mut coll = LinkCollector::new(cx);
36
37         coll.fold_crate(krate)
38     }
39 }
40
41 struct LinkCollector<'a, 'tcx> {
42     cx: &'a DocContext<'tcx>,
43     mod_ids: Vec<hir::HirId>,
44 }
45
46 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
47     fn new(cx: &'a DocContext<'tcx>) -> Self {
48         LinkCollector {
49             cx,
50             mod_ids: Vec::new(),
51         }
52     }
53
54     /// Resolves a string as a path within a particular namespace. Also returns an optional
55     /// URL fragment in the case of variants and methods.
56     fn resolve(&self,
57                path_str: &str,
58                ns: Namespace,
59                current_item: &Option<String>,
60                parent_id: Option<hir::HirId>)
61         -> Result<(Res, Option<String>), ()>
62     {
63         let cx = self.cx;
64
65         // In case we're in a module, try to resolve the relative path.
66         if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
67             let module_id = cx.tcx.hir().hir_to_node_id(module_id);
68             let result = cx.enter_resolver(|resolver| {
69                 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
70             });
71             let result = match result {
72                 Ok((_, Res::Err)) => Err(()),
73                 _ => result,
74             };
75
76             if let Ok((_, res)) = result {
77                 let res = res.map_id(|_| panic!("unexpected node_id"));
78                 // In case this is a trait item, skip the
79                 // early return and try looking for the trait.
80                 let value = match res {
81                     Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true,
82                     Res::Def(DefKind::AssocTy, _) => false,
83                     Res::Def(DefKind::Variant, _) => return handle_variant(cx, res),
84                     // Not a trait item; just return what we found.
85                     Res::PrimTy(..) => return Ok((res, Some(path_str.to_owned()))),
86                     _ => return Ok((res, None))
87                 };
88
89                 if value != (ns == ValueNS) {
90                     return Err(())
91                 }
92             } else if let Some(prim) = is_primitive(path_str, ns) {
93                 return Ok((prim, Some(path_str.to_owned())))
94             } else {
95                 // If resolution failed, it may still be a method
96                 // because methods are not handled by the resolver
97                 // If so, bail when we're not looking for a value.
98                 if ns != ValueNS {
99                     return Err(())
100                 }
101             }
102
103             // Try looking for methods and associated items.
104             let mut split = path_str.rsplitn(2, "::");
105             let item_name = if let Some(first) = split.next() {
106                 Symbol::intern(first)
107             } else {
108                 return Err(())
109             };
110
111             let mut path = if let Some(second) = split.next() {
112                 second.to_owned()
113             } else {
114                 return Err(())
115             };
116
117             if path == "self" || path == "Self" {
118                 if let Some(name) = current_item.as_ref() {
119                     path = name.clone();
120                 }
121             }
122             if let Some(prim) = is_primitive(&path, TypeNS) {
123                 let did = primitive_impl(cx, &path).ok_or(())?;
124                 return cx.tcx.associated_items(did)
125                     .find(|item| item.ident.name == item_name)
126                     .and_then(|item| match item.kind {
127                         ty::AssocKind::Method => Some("method"),
128                         _ => None,
129                     })
130                     .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))))
131                     .ok_or(());
132             }
133
134             let (_, ty_res) = cx.enter_resolver(|resolver| {
135                 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
136             })?;
137             if let Res::Err = ty_res {
138                 return Err(());
139             }
140             let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
141             match ty_res {
142                 Res::Def(DefKind::Struct, did)
143                 | Res::Def(DefKind::Union, did)
144                 | Res::Def(DefKind::Enum, did)
145                 | Res::Def(DefKind::TyAlias, did) => {
146                     let item = cx.tcx.inherent_impls(did)
147                                      .iter()
148                                      .flat_map(|imp| cx.tcx.associated_items(*imp))
149                                      .find(|item| item.ident.name == item_name);
150                     if let Some(item) = item {
151                         let out = match item.kind {
152                             ty::AssocKind::Method if ns == ValueNS => "method",
153                             ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
154                             _ => return Err(())
155                         };
156                         Ok((ty_res, Some(format!("{}.{}", out, item_name))))
157                     } else {
158                         match cx.tcx.type_of(did).kind {
159                             ty::Adt(def, _) => {
160                                 if let Some(item) = if def.is_enum() {
161                                     def.all_fields().find(|item| item.ident.name == item_name)
162                                 } else {
163                                     def.non_enum_variant()
164                                        .fields
165                                        .iter()
166                                        .find(|item| item.ident.name == item_name)
167                                 } {
168                                     Ok((ty_res,
169                                         Some(format!("{}.{}",
170                                                      if def.is_enum() {
171                                                          "variant"
172                                                      } else {
173                                                          "structfield"
174                                                      },
175                                                      item.ident))))
176                                 } else {
177                                     Err(())
178                                 }
179                             }
180                             _ => Err(()),
181                         }
182                     }
183                 }
184                 Res::Def(DefKind::Trait, did) => {
185                     let item = cx.tcx.associated_item_def_ids(did).iter()
186                                  .map(|item| cx.tcx.associated_item(*item))
187                                  .find(|item| item.ident.name == item_name);
188                     if let Some(item) = item {
189                         let kind = match item.kind {
190                             ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
191                             ty::AssocKind::Type if ns == TypeNS => "associatedtype",
192                             ty::AssocKind::Method if ns == ValueNS => {
193                                 if item.defaultness.has_value() {
194                                     "method"
195                                 } else {
196                                     "tymethod"
197                                 }
198                             }
199                             _ => return Err(())
200                         };
201
202                         Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
203                     } else {
204                         Err(())
205                     }
206                 }
207                 _ => Err(())
208             }
209         } else {
210             debug!("attempting to resolve item without parent module: {}", path_str);
211             Err(())
212         }
213     }
214 }
215
216 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
217     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
218         let item_hir_id = if item.is_mod() {
219             if let Some(id) = self.cx.tcx.hir().as_local_hir_id(item.def_id) {
220                 Some(id)
221             } else {
222                 debug!("attempting to fold on a non-local item: {:?}", item);
223                 return self.fold_item_recur(item);
224             }
225         } else {
226             None
227         };
228
229         // FIXME: get the resolver to work with non-local resolve scopes.
230         let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| {
231             // FIXME: this fails hard for impls in non-module scope, but is necessary for the
232             // current `resolve()` implementation.
233             match self.cx.tcx.hir().get_module_parent_node(hir_id) {
234                 id if id != hir_id => Some(id),
235                 _ => None,
236             }
237         });
238
239         if parent_node.is_some() {
240             debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
241         }
242
243         let current_item = match item.inner {
244             ModuleItem(..) => {
245                 if item.attrs.inner_docs {
246                     if item_hir_id.unwrap() != hir::CRATE_HIR_ID {
247                         item.name.clone()
248                     } else {
249                         None
250                     }
251                 } else {
252                     match parent_node.or(self.mod_ids.last().cloned()) {
253                         Some(parent) if parent != hir::CRATE_HIR_ID => {
254                             // FIXME: can we pull the parent module's name from elsewhere?
255                             Some(self.cx.tcx.hir().name(parent).to_string())
256                         }
257                         _ => None,
258                     }
259                 }
260             }
261             ImplItem(Impl { ref for_, .. }) => {
262                 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
263             }
264             // we don't display docs on `extern crate` items anyway, so don't process them.
265             ExternCrateItem(..) => return self.fold_item_recur(item),
266             ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
267             MacroItem(..) => None,
268             _ => item.name.clone(),
269         };
270
271         if item.is_mod() && item.attrs.inner_docs {
272             self.mod_ids.push(item_hir_id.unwrap());
273         }
274
275         let cx = self.cx;
276         let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
277
278         look_for_tests(&cx, &dox, &item, true);
279
280         for (ori_link, link_range) in markdown_links(&dox) {
281             // Bail early for real links.
282             if ori_link.contains('/') {
283                 continue;
284             }
285
286             // [] is mostly likely not supposed to be a link
287             if ori_link.is_empty() {
288                 continue;
289             }
290
291             let link = ori_link.replace("`", "");
292             let (res, fragment) = {
293                 let mut kind = None;
294                 let path_str = if let Some(prefix) =
295                     ["struct@", "enum@", "type@",
296                      "trait@", "union@"].iter()
297                                       .find(|p| link.starts_with(**p)) {
298                     kind = Some(TypeNS);
299                     link.trim_start_matches(prefix)
300                 } else if let Some(prefix) =
301                     ["const@", "static@",
302                      "value@", "function@", "mod@",
303                      "fn@", "module@", "method@"]
304                         .iter().find(|p| link.starts_with(**p)) {
305                     kind = Some(ValueNS);
306                     link.trim_start_matches(prefix)
307                 } else if link.ends_with("()") {
308                     kind = Some(ValueNS);
309                     link.trim_end_matches("()")
310                 } else if link.starts_with("macro@") {
311                     kind = Some(MacroNS);
312                     link.trim_start_matches("macro@")
313                 } else if link.ends_with('!') {
314                     kind = Some(MacroNS);
315                     link.trim_end_matches('!')
316                 } else {
317                     &link[..]
318                 }.trim();
319
320                 if path_str.contains(|ch: char| !(ch.is_alphanumeric() ||
321                                                   ch == ':' || ch == '_')) {
322                     continue;
323                 }
324
325                 // In order to correctly resolve intra-doc-links we need to
326                 // pick a base AST node to work from.  If the documentation for
327                 // this module came from an inner comment (//!) then we anchor
328                 // our name resolution *inside* the module.  If, on the other
329                 // hand it was an outer comment (///) then we anchor the name
330                 // resolution in the parent module on the basis that the names
331                 // used are more likely to be intended to be parent names.  For
332                 // this, we set base_node to None for inner comments since
333                 // we've already pushed this node onto the resolution stack but
334                 // for outer comments we explicitly try and resolve against the
335                 // parent_node first.
336                 let base_node = if item.is_mod() && item.attrs.inner_docs {
337                     None
338                 } else {
339                     parent_node
340                 };
341
342                 match kind {
343                     Some(ns @ ValueNS) => {
344                         if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node) {
345                             res
346                         } else {
347                             resolution_failure(cx, &item, path_str, &dox, link_range);
348                             // This could just be a normal link or a broken link
349                             // we could potentially check if something is
350                             // "intra-doc-link-like" and warn in that case.
351                             continue;
352                         }
353                     }
354                     Some(ns @ TypeNS) => {
355                         if let Ok(res) = self.resolve(path_str, ns, &current_item, base_node) {
356                             res
357                         } else {
358                             resolution_failure(cx, &item, path_str, &dox, link_range);
359                             // This could just be a normal link.
360                             continue;
361                         }
362                     }
363                     None => {
364                         // Try everything!
365                         let candidates = PerNS {
366                             macro_ns: macro_resolve(cx, path_str).map(|res| (res, None)),
367                             type_ns: self
368                                 .resolve(path_str, TypeNS, &current_item, base_node)
369                                 .ok(),
370                             value_ns: self
371                                 .resolve(path_str, ValueNS, &current_item, base_node)
372                                 .ok()
373                                 .and_then(|(res, fragment)| {
374                                     // Constructors are picked up in the type namespace.
375                                     match res {
376                                         Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
377                                         _ => Some((res, fragment))
378                                     }
379                                 }),
380                         };
381
382                         if candidates.is_empty() {
383                             resolution_failure(cx, &item, path_str, &dox, link_range);
384                             // this could just be a normal link
385                             continue;
386                         }
387
388                         let is_unambiguous = candidates.clone().present_items().count() == 1;
389                         if is_unambiguous {
390                             candidates.present_items().next().unwrap()
391                         } else {
392                             ambiguity_error(
393                                 cx,
394                                 &item,
395                                 path_str,
396                                 &dox,
397                                 link_range,
398                                 candidates.map(|candidate| candidate.map(|(res, _)| res)),
399                             );
400                             continue;
401                         }
402                     }
403                     Some(MacroNS) => {
404                         if let Some(res) = macro_resolve(cx, path_str) {
405                             (res, None)
406                         } else {
407                             resolution_failure(cx, &item, path_str, &dox, link_range);
408                             continue
409                         }
410                     }
411                 }
412             };
413
414             if let Res::PrimTy(_) = res {
415                 item.attrs.links.push((ori_link, None, fragment));
416             } else {
417                 let id = register_res(cx, res);
418                 item.attrs.links.push((ori_link, Some(id), fragment));
419             }
420         }
421
422         if item.is_mod() && !item.attrs.inner_docs {
423             self.mod_ids.push(item_hir_id.unwrap());
424         }
425
426         if item.is_mod() {
427             let ret = self.fold_item_recur(item);
428
429             self.mod_ids.pop();
430
431             ret
432         } else {
433             self.fold_item_recur(item)
434         }
435     }
436
437     // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
438     // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
439     // will never resolve properly
440     fn fold_crate(&mut self, mut c: Crate) -> Crate {
441         c.module = c.module.take().and_then(|module| self.fold_item(module));
442
443         c
444     }
445 }
446
447 /// Resolves a string as a macro.
448 fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option<Res> {
449     let path = ast::Path::from_ident(Ident::from_str(path_str));
450     cx.enter_resolver(|resolver| {
451         if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
452             &path, None, &ParentScope::module(resolver.graph_root()), false, false
453         ) {
454             if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
455                 return Some(res.map_id(|_| panic!("unexpected id")));
456             }
457         }
458         if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
459             return Some(res.map_id(|_| panic!("unexpected id")));
460         }
461         None
462     })
463 }
464
465 /// Reports a resolution failure diagnostic.
466 ///
467 /// If we cannot find the exact source span of the resolution failure, we use the span of the
468 /// documentation attributes themselves. This is a little heavy-handed, so we display the markdown
469 /// line containing the failure as a note as well.
470 fn resolution_failure(
471     cx: &DocContext<'_>,
472     item: &Item,
473     path_str: &str,
474     dox: &str,
475     link_range: Option<Range<usize>>,
476 ) {
477     let hir_id = match cx.as_local_hir_id(item.def_id) {
478         Some(hir_id) => hir_id,
479         None => {
480             // If non-local, no need to check anything.
481             return;
482         }
483     };
484     let attrs = &item.attrs;
485     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
486
487     let mut diag = cx.tcx.struct_span_lint_hir(
488         lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
489         hir_id,
490         sp,
491         &format!("`[{}]` cannot be resolved, ignoring it...", path_str),
492     );
493     if let Some(link_range) = link_range {
494         if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
495             diag.set_span(sp);
496             diag.span_label(sp, "cannot be resolved, ignoring");
497         } else {
498             // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
499             //                       ^     ~~~~
500             //                       |     link_range
501             //                       last_new_line_offset
502             let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
503             let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
504
505             // Print the line containing the `link_range` and manually mark it with '^'s.
506             diag.note(&format!(
507                 "the link appears in this line:\n\n{line}\n\
508                  {indicator: <before$}{indicator:^<found$}",
509                 line=line,
510                 indicator="",
511                 before=link_range.start - last_new_line_offset,
512                 found=link_range.len(),
513             ));
514         }
515     };
516     diag.help("to escape `[` and `]` characters, just add '\\' before them like \
517                `\\[` or `\\]`");
518     diag.emit();
519 }
520
521 fn ambiguity_error(
522     cx: &DocContext<'_>,
523     item: &Item,
524     path_str: &str,
525     dox: &str,
526     link_range: Option<Range<usize>>,
527     candidates: PerNS<Option<Res>>,
528 ) {
529     let hir_id = match cx.as_local_hir_id(item.def_id) {
530         Some(hir_id) => hir_id,
531         None => {
532             // If non-local, no need to check anything.
533             return;
534         }
535     };
536     let attrs = &item.attrs;
537     let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
538
539     let mut msg = format!("`{}` is ", path_str);
540
541     let candidates = [TypeNS, ValueNS, MacroNS].iter().filter_map(|&ns| {
542         candidates[ns].map(|res| (res, ns))
543     }).collect::<Vec<_>>();
544     match candidates.as_slice() {
545         [(first_def, _), (second_def, _)] => {
546             msg += &format!(
547                 "both {} {} and {} {}",
548                 first_def.article(),
549                 first_def.descr(),
550                 second_def.article(),
551                 second_def.descr(),
552             );
553         }
554         _ => {
555             let mut candidates = candidates.iter().peekable();
556             while let Some((res, _)) = candidates.next() {
557                 if candidates.peek().is_some() {
558                     msg += &format!("{} {}, ", res.article(), res.descr());
559                 } else {
560                     msg += &format!("and {} {}", res.article(), res.descr());
561                 }
562             }
563         }
564     }
565
566     let mut diag = cx.tcx.struct_span_lint_hir(
567         lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
568         hir_id,
569         sp,
570         &msg,
571     );
572
573     if let Some(link_range) = link_range {
574         if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
575             diag.set_span(sp);
576             diag.span_label(sp, "ambiguous link");
577
578             for (res, ns) in candidates {
579                 let (action, mut suggestion) = match res {
580                     Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
581                         ("add parentheses", format!("{}()", path_str))
582                     }
583                     Res::Def(DefKind::Macro(..), _) => {
584                         ("add an exclamation mark", format!("{}!", path_str))
585                     }
586                     _ => {
587                         let type_ = match (res, ns) {
588                             (Res::Def(DefKind::Const, _), _) => "const",
589                             (Res::Def(DefKind::Static, _), _) => "static",
590                             (Res::Def(DefKind::Struct, _), _) => "struct",
591                             (Res::Def(DefKind::Enum, _), _) => "enum",
592                             (Res::Def(DefKind::Union, _), _) => "union",
593                             (Res::Def(DefKind::Trait, _), _) => "trait",
594                             (Res::Def(DefKind::Mod, _), _) => "module",
595                             (_, TypeNS) => "type",
596                             (_, ValueNS) => "value",
597                             (_, MacroNS) => "macro",
598                         };
599
600                         // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
601                         ("prefix with the item type", format!("{}@{}", type_, path_str))
602                     }
603                 };
604
605                 if dox.bytes().nth(link_range.start) == Some(b'`') {
606                     suggestion = format!("`{}`", suggestion);
607                 }
608
609                 diag.span_suggestion(
610                     sp,
611                     &format!("to link to the {}, {}", res.descr(), action),
612                     suggestion,
613                     Applicability::MaybeIncorrect,
614                 );
615             }
616         } else {
617             // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
618             //                       ^     ~~~~
619             //                       |     link_range
620             //                       last_new_line_offset
621             let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
622             let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
623
624             // Print the line containing the `link_range` and manually mark it with '^'s.
625             diag.note(&format!(
626                 "the link appears in this line:\n\n{line}\n\
627                  {indicator: <before$}{indicator:^<found$}",
628                 line=line,
629                 indicator="",
630                 before=link_range.start - last_new_line_offset,
631                 found=link_range.len(),
632             ));
633         }
634     }
635
636     diag.emit();
637 }
638
639 /// Given an enum variant's res, return the res of its enum and the associated fragment.
640 fn handle_variant(cx: &DocContext<'_>, res: Res) -> Result<(Res, Option<String>), ()> {
641     use rustc::ty::DefIdTree;
642
643     let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
644         parent
645     } else {
646         return Err(())
647     };
648     let parent_def = Res::Def(DefKind::Enum, parent);
649     let variant = cx.tcx.expect_variant_res(res);
650     Ok((parent_def, Some(format!("{}.v", variant.ident.name))))
651 }
652
653 const PRIMITIVES: &[(&str, Res)] = &[
654     ("u8",    Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U8))),
655     ("u16",   Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U16))),
656     ("u32",   Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U32))),
657     ("u64",   Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U64))),
658     ("u128",  Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U128))),
659     ("usize", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::Usize))),
660     ("i8",    Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I8))),
661     ("i16",   Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I16))),
662     ("i32",   Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I32))),
663     ("i64",   Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I64))),
664     ("i128",  Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I128))),
665     ("isize", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::Isize))),
666     ("f32",   Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F32))),
667     ("f64",   Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F64))),
668     ("str",   Res::PrimTy(hir::PrimTy::Str)),
669     ("bool",  Res::PrimTy(hir::PrimTy::Bool)),
670     ("char",  Res::PrimTy(hir::PrimTy::Char)),
671 ];
672
673 fn is_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
674     if ns == TypeNS {
675         PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1)
676     } else {
677         None
678     }
679 }
680
681 fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<DefId> {
682     let tcx = cx.tcx;
683     match path_str {
684         "u8" => tcx.lang_items().u8_impl(),
685         "u16" => tcx.lang_items().u16_impl(),
686         "u32" => tcx.lang_items().u32_impl(),
687         "u64" => tcx.lang_items().u64_impl(),
688         "u128" => tcx.lang_items().u128_impl(),
689         "usize" => tcx.lang_items().usize_impl(),
690         "i8" => tcx.lang_items().i8_impl(),
691         "i16" => tcx.lang_items().i16_impl(),
692         "i32" => tcx.lang_items().i32_impl(),
693         "i64" => tcx.lang_items().i64_impl(),
694         "i128" => tcx.lang_items().i128_impl(),
695         "isize" => tcx.lang_items().isize_impl(),
696         "f32" => tcx.lang_items().f32_impl(),
697         "f64" => tcx.lang_items().f64_impl(),
698         "str" => tcx.lang_items().str_impl(),
699         "bool" => tcx.lang_items().bool_impl(),
700         "char" => tcx.lang_items().char_impl(),
701         _ => None,
702     }
703 }