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