3 use rustc_errors::Applicability;
4 use rustc_expand::base::SyntaxExtensionKind;
5 use rustc_feature::UnstableFeatures;
12 use rustc_hir::def_id::DefId;
13 use rustc_resolve::ParentScope;
14 use rustc_span::symbol::Symbol;
15 use rustc_span::DUMMY_SP;
16 use syntax::ast::{self, Ident};
21 use crate::core::DocContext;
22 use crate::fold::DocFolder;
23 use crate::html::markdown::markdown_links;
24 use crate::passes::{look_for_tests, Pass};
26 use super::span_of_attrs;
28 pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
29 name: "collect-intra-doc-links",
30 run: collect_intra_doc_links,
31 description: "reads a crate's documentation to resolve intra-doc-links",
34 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate {
35 if !UnstableFeatures::from_environment().is_nightly_build() {
38 let mut coll = LinkCollector::new(cx);
40 coll.fold_crate(krate)
46 AnchorFailure(&'static str),
49 struct LinkCollector<'a, 'tcx> {
50 cx: &'a DocContext<'tcx>,
51 mod_ids: Vec<hir::HirId>,
54 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
55 fn new(cx: &'a DocContext<'tcx>) -> Self {
56 LinkCollector { cx, mod_ids: Vec::new() }
62 current_item: &Option<String>,
63 module_id: syntax::ast::NodeId,
64 ) -> Result<(Res, Option<String>), ErrorKind> {
67 let mut split = path_str.rsplitn(3, "::");
68 let variant_field_name =
69 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
71 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
75 if f == "self" || f == "Self" {
76 if let Some(name) = current_item.as_ref() {
82 .ok_or(ErrorKind::ResolutionFailure)?;
84 .enter_resolver(|resolver| {
85 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
87 .map_err(|_| ErrorKind::ResolutionFailure)?;
88 if let Res::Err = ty_res {
89 return Err(ErrorKind::ResolutionFailure);
91 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
93 Res::Def(DefKind::Enum, did) => {
98 .flat_map(|imp| cx.tcx.associated_items(*imp))
99 .any(|item| item.ident.name == variant_name)
101 return Err(ErrorKind::ResolutionFailure);
103 match cx.tcx.type_of(did).kind {
104 ty::Adt(def, _) if def.is_enum() => {
105 if def.all_fields().any(|item| item.ident.name == variant_field_name) {
109 "variant.{}.field.{}",
110 variant_name, variant_field_name
114 Err(ErrorKind::ResolutionFailure)
117 _ => Err(ErrorKind::ResolutionFailure),
120 _ => Err(ErrorKind::ResolutionFailure),
124 /// Resolves a string as a path within a particular namespace. Also returns an optional
125 /// URL fragment in the case of variants and methods.
130 current_item: &Option<String>,
131 parent_id: Option<hir::HirId>,
132 extra_fragment: &Option<String>,
133 ) -> Result<(Res, Option<String>), ErrorKind> {
136 // In case we're in a module, try to resolve the relative path.
137 if let Some(module_id) = parent_id.or(self.mod_ids.last().cloned()) {
138 let module_id = cx.tcx.hir().hir_to_node_id(module_id);
139 let result = cx.enter_resolver(|resolver| {
140 resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
142 let result = match result {
143 Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
144 _ => result.map_err(|_| ErrorKind::ResolutionFailure),
147 if let Ok((_, res)) = result {
148 let res = res.map_id(|_| panic!("unexpected node_id"));
149 // In case this is a trait item, skip the
150 // early return and try looking for the trait.
151 let value = match res {
152 Res::Def(DefKind::Method, _) | Res::Def(DefKind::AssocConst, _) => true,
153 Res::Def(DefKind::AssocTy, _) => false,
154 Res::Def(DefKind::Variant, _) => {
155 return handle_variant(cx, res, extra_fragment);
157 // Not a trait item; just return what we found.
159 if extra_fragment.is_some() {
160 return Err(ErrorKind::AnchorFailure(
161 "primitive types cannot be followed by anchors",
164 return Ok((res, Some(path_str.to_owned())));
166 _ => return Ok((res, extra_fragment.clone())),
169 if value != (ns == ValueNS) {
170 return Err(ErrorKind::ResolutionFailure);
172 } else if let Some(prim) = is_primitive(path_str, ns) {
173 if extra_fragment.is_some() {
174 return Err(ErrorKind::AnchorFailure(
175 "primitive types cannot be followed by anchors",
178 return Ok((prim, Some(path_str.to_owned())));
180 // If resolution failed, it may still be a method
181 // because methods are not handled by the resolver
182 // If so, bail when we're not looking for a value.
184 return Err(ErrorKind::ResolutionFailure);
188 // Try looking for methods and associated items.
189 let mut split = path_str.rsplitn(2, "::");
191 split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?;
195 if f == "self" || f == "Self" {
196 if let Some(name) = current_item.as_ref() {
202 .ok_or(ErrorKind::ResolutionFailure)?;
204 if let Some(prim) = is_primitive(&path, TypeNS) {
205 let did = primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)?;
208 .associated_items(did)
209 .find(|item| item.ident.name == item_name)
210 .and_then(|item| match item.kind {
211 ty::AssocKind::Method => Some("method"),
214 .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))))
215 .ok_or(ErrorKind::ResolutionFailure);
219 .enter_resolver(|resolver| {
220 resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id)
222 .map_err(|_| ErrorKind::ResolutionFailure)?;
223 if let Res::Err = ty_res {
224 return self.variant_field(path_str, current_item, module_id);
226 let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
228 Res::Def(DefKind::Struct, did)
229 | Res::Def(DefKind::Union, did)
230 | Res::Def(DefKind::Enum, did)
231 | Res::Def(DefKind::TyAlias, did) => {
236 .flat_map(|imp| cx.tcx.associated_items(*imp))
237 .find(|item| item.ident.name == item_name);
238 if let Some(item) = item {
239 let out = match item.kind {
240 ty::AssocKind::Method if ns == ValueNS => "method",
241 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
242 _ => return self.variant_field(path_str, current_item, module_id),
244 if extra_fragment.is_some() {
245 Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Method {
246 "methods cannot be followed by anchors"
248 "associated constants cannot be followed by anchors"
251 Ok((ty_res, Some(format!("{}.{}", out, item_name))))
254 match cx.tcx.type_of(did).kind {
256 if let Some(item) = if def.is_enum() {
257 def.all_fields().find(|item| item.ident.name == item_name)
259 def.non_enum_variant()
262 .find(|item| item.ident.name == item_name)
264 if extra_fragment.is_some() {
265 Err(ErrorKind::AnchorFailure(if def.is_enum() {
266 "enum variants cannot be followed by anchors"
268 "struct fields cannot be followed by anchors"
285 self.variant_field(path_str, current_item, module_id)
288 _ => self.variant_field(path_str, current_item, module_id),
292 Res::Def(DefKind::Trait, did) => {
295 .associated_item_def_ids(did)
297 .map(|item| cx.tcx.associated_item(*item))
298 .find(|item| item.ident.name == item_name);
299 if let Some(item) = item {
300 let kind = match item.kind {
301 ty::AssocKind::Const if ns == ValueNS => "associatedconstant",
302 ty::AssocKind::Type if ns == TypeNS => "associatedtype",
303 ty::AssocKind::Method if ns == ValueNS => {
304 if item.defaultness.has_value() { "method" } else { "tymethod" }
306 _ => return self.variant_field(path_str, current_item, module_id),
309 if extra_fragment.is_some() {
310 Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const {
311 "associated constants cannot be followed by anchors"
312 } else if item.kind == ty::AssocKind::Type {
313 "associated types cannot be followed by anchors"
315 "methods cannot be followed by anchors"
318 Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
321 self.variant_field(path_str, current_item, module_id)
324 _ => self.variant_field(path_str, current_item, module_id),
327 debug!("attempting to resolve item without parent module: {}", path_str);
328 Err(ErrorKind::ResolutionFailure)
333 impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
334 fn fold_item(&mut self, mut item: Item) -> Option<Item> {
335 let item_hir_id = if item.is_mod() {
336 if let Some(id) = self.cx.tcx.hir().as_local_hir_id(item.def_id) {
339 debug!("attempting to fold on a non-local item: {:?}", item);
340 return self.fold_item_recur(item);
346 // FIXME: get the resolver to work with non-local resolve scopes.
347 let parent_node = self.cx.as_local_hir_id(item.def_id).and_then(|hir_id| {
348 // FIXME: this fails hard for impls in non-module scope, but is necessary for the
349 // current `resolve()` implementation.
350 match self.cx.tcx.hir().get_module_parent_node(hir_id) {
351 id if id != hir_id => Some(id),
356 if parent_node.is_some() {
357 debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
360 let current_item = match item.inner {
362 if item.attrs.inner_docs {
363 if item_hir_id.unwrap() != hir::CRATE_HIR_ID { item.name.clone() } else { None }
365 match parent_node.or(self.mod_ids.last().cloned()) {
366 Some(parent) if parent != hir::CRATE_HIR_ID => {
367 // FIXME: can we pull the parent module's name from elsewhere?
368 Some(self.cx.tcx.hir().name(parent).to_string())
374 ImplItem(Impl { ref for_, .. }) => {
375 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
377 // we don't display docs on `extern crate` items anyway, so don't process them.
378 ExternCrateItem(..) => return self.fold_item_recur(item),
379 ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
380 MacroItem(..) => None,
381 _ => item.name.clone(),
384 if item.is_mod() && item.attrs.inner_docs {
385 self.mod_ids.push(item_hir_id.unwrap());
389 let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
391 look_for_tests(&cx, &dox, &item, true);
393 for (ori_link, link_range) in markdown_links(&dox) {
394 // Bail early for real links.
395 if ori_link.contains('/') {
399 // [] is mostly likely not supposed to be a link
400 if ori_link.is_empty() {
404 let link = ori_link.replace("`", "");
405 let parts = link.split('#').collect::<Vec<_>>();
406 let (link, extra_fragment) = if parts.len() > 2 {
413 "has an issue with the link anchor.",
414 "only one `#` is allowed in a link",
418 } else if parts.len() == 2 {
419 if parts[0].trim().is_empty() {
420 // This is an anchor to an element of the current page, nothing to do in here!
423 (parts[0].to_owned(), Some(parts[1].to_owned()))
425 (parts[0].to_owned(), None)
427 let (res, fragment) = {
429 let path_str = if let Some(prefix) =
430 ["struct@", "enum@", "type@", "trait@", "union@"]
432 .find(|p| link.starts_with(**p))
435 link.trim_start_matches(prefix)
436 } else if let Some(prefix) = [
447 .find(|p| link.starts_with(**p))
449 kind = Some(ValueNS);
450 link.trim_start_matches(prefix)
451 } else if link.ends_with("()") {
452 kind = Some(ValueNS);
453 link.trim_end_matches("()")
454 } else if link.starts_with("macro@") {
455 kind = Some(MacroNS);
456 link.trim_start_matches("macro@")
457 } else if link.ends_with('!') {
458 kind = Some(MacroNS);
459 link.trim_end_matches('!')
465 if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
469 // In order to correctly resolve intra-doc-links we need to
470 // pick a base AST node to work from. If the documentation for
471 // this module came from an inner comment (//!) then we anchor
472 // our name resolution *inside* the module. If, on the other
473 // hand it was an outer comment (///) then we anchor the name
474 // resolution in the parent module on the basis that the names
475 // used are more likely to be intended to be parent names. For
476 // this, we set base_node to None for inner comments since
477 // we've already pushed this node onto the resolution stack but
478 // for outer comments we explicitly try and resolve against the
479 // parent_node first.
481 if item.is_mod() && item.attrs.inner_docs { None } else { parent_node };
484 Some(ns @ ValueNS) => {
485 match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment)
488 Err(ErrorKind::ResolutionFailure) => {
489 resolution_failure(cx, &item, path_str, &dox, link_range);
490 // This could just be a normal link or a broken link
491 // we could potentially check if something is
492 // "intra-doc-link-like" and warn in that case.
495 Err(ErrorKind::AnchorFailure(msg)) => {
496 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
501 Some(ns @ TypeNS) => {
502 match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment)
505 Err(ErrorKind::ResolutionFailure) => {
506 resolution_failure(cx, &item, path_str, &dox, link_range);
507 // This could just be a normal link.
510 Err(ErrorKind::AnchorFailure(msg)) => {
511 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
518 let candidates = PerNS {
519 macro_ns: macro_resolve(cx, path_str)
520 .map(|res| (res, extra_fragment.clone())),
521 type_ns: match self.resolve(
528 Err(ErrorKind::AnchorFailure(msg)) => {
529 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
534 value_ns: match self.resolve(
541 Err(ErrorKind::AnchorFailure(msg)) => {
542 anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
547 .and_then(|(res, fragment)| {
548 // Constructors are picked up in the type namespace.
550 Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None,
551 _ => match (fragment, extra_fragment) {
552 (Some(fragment), Some(_)) => {
553 // Shouldn't happen but who knows?
554 Some((res, Some(fragment)))
556 (fragment, None) | (None, fragment) => {
557 Some((res, fragment))
564 if candidates.is_empty() {
565 resolution_failure(cx, &item, path_str, &dox, link_range);
566 // this could just be a normal link
570 let is_unambiguous = candidates.clone().present_items().count() == 1;
572 candidates.present_items().next().unwrap()
580 candidates.map(|candidate| candidate.map(|(res, _)| res)),
586 if let Some(res) = macro_resolve(cx, path_str) {
587 (res, extra_fragment)
589 resolution_failure(cx, &item, path_str, &dox, link_range);
596 if let Res::PrimTy(_) = res {
597 item.attrs.links.push((ori_link, None, fragment));
599 let id = register_res(cx, res);
600 item.attrs.links.push((ori_link, Some(id), fragment));
604 if item.is_mod() && !item.attrs.inner_docs {
605 self.mod_ids.push(item_hir_id.unwrap());
609 let ret = self.fold_item_recur(item);
615 self.fold_item_recur(item)
619 // FIXME: if we can resolve intra-doc links from other crates, we can use the stock
620 // `fold_crate`, but until then we should avoid scanning `krate.external_traits` since those
621 // will never resolve properly
622 fn fold_crate(&mut self, mut c: Crate) -> Crate {
623 c.module = c.module.take().and_then(|module| self.fold_item(module));
629 /// Resolves a string as a macro.
630 fn macro_resolve(cx: &DocContext<'_>, path_str: &str) -> Option<Res> {
631 let path = ast::Path::from_ident(Ident::from_str(path_str));
632 cx.enter_resolver(|resolver| {
633 if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
636 &ParentScope::module(resolver.graph_root()),
640 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
641 return Some(res.map_id(|_| panic!("unexpected id")));
644 if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
645 return Some(res.map_id(|_| panic!("unexpected id")));
656 link_range: Option<Range<usize>>,
659 help_msg: Option<&str>,
661 let hir_id = match cx.as_local_hir_id(item.def_id) {
662 Some(hir_id) => hir_id,
664 // If non-local, no need to check anything.
668 let attrs = &item.attrs;
669 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
671 let mut diag = cx.tcx.struct_span_lint_hir(
672 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
675 &format!("`[{}]` {}", path_str, err_msg),
677 if let Some(link_range) = link_range {
678 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
680 diag.span_label(sp, short_err_msg);
682 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
685 // last_new_line_offset
686 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
687 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
689 // Print the line containing the `link_range` and manually mark it with '^'s.
691 "the link appears in this line:\n\n{line}\n\
692 {indicator: <before$}{indicator:^<found$}",
695 before = link_range.start - last_new_line_offset,
696 found = link_range.len(),
700 if let Some(help_msg) = help_msg {
706 /// Reports a resolution failure diagnostic.
708 /// If we cannot find the exact source span of the resolution failure, we use the span of the
709 /// documentation attributes themselves. This is a little heavy-handed, so we display the markdown
710 /// line containing the failure as a note as well.
711 fn resolution_failure(
716 link_range: Option<Range<usize>>,
724 "cannot be resolved, ignoring it.",
725 "cannot be resolved, ignoring",
726 Some("to escape `[` and `]` characters, just add '\\' before them like `\\[` or `\\]`"),
735 link_range: Option<Range<usize>>,
744 "has an issue with the link anchor.",
755 link_range: Option<Range<usize>>,
756 candidates: PerNS<Option<Res>>,
758 let hir_id = match cx.as_local_hir_id(item.def_id) {
759 Some(hir_id) => hir_id,
761 // If non-local, no need to check anything.
765 let attrs = &item.attrs;
766 let sp = span_of_attrs(attrs).unwrap_or(item.source.span());
768 let mut msg = format!("`{}` is ", path_str);
770 let candidates = [TypeNS, ValueNS, MacroNS]
772 .filter_map(|&ns| candidates[ns].map(|res| (res, ns)))
773 .collect::<Vec<_>>();
774 match candidates.as_slice() {
775 [(first_def, _), (second_def, _)] => {
777 "both {} {} and {} {}",
780 second_def.article(),
785 let mut candidates = candidates.iter().peekable();
786 while let Some((res, _)) = candidates.next() {
787 if candidates.peek().is_some() {
788 msg += &format!("{} {}, ", res.article(), res.descr());
790 msg += &format!("and {} {}", res.article(), res.descr());
796 let mut diag = cx.tcx.struct_span_lint_hir(
797 lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
803 if let Some(link_range) = link_range {
804 if let Some(sp) = super::source_span_for_markdown_range(cx, dox, &link_range, attrs) {
806 diag.span_label(sp, "ambiguous link");
808 for (res, ns) in candidates {
809 let (action, mut suggestion) = match res {
810 Res::Def(DefKind::Method, _) | Res::Def(DefKind::Fn, _) => {
811 ("add parentheses", format!("{}()", path_str))
813 Res::Def(DefKind::Macro(..), _) => {
814 ("add an exclamation mark", format!("{}!", path_str))
817 let type_ = match (res, ns) {
818 (Res::Def(DefKind::Const, _), _) => "const",
819 (Res::Def(DefKind::Static, _), _) => "static",
820 (Res::Def(DefKind::Struct, _), _) => "struct",
821 (Res::Def(DefKind::Enum, _), _) => "enum",
822 (Res::Def(DefKind::Union, _), _) => "union",
823 (Res::Def(DefKind::Trait, _), _) => "trait",
824 (Res::Def(DefKind::Mod, _), _) => "module",
825 (_, TypeNS) => "type",
826 (_, ValueNS) => "value",
827 (_, MacroNS) => "macro",
830 // FIXME: if this is an implied shortcut link, it's bad style to suggest `@`
831 ("prefix with the item type", format!("{}@{}", type_, path_str))
835 if dox.bytes().nth(link_range.start) == Some(b'`') {
836 suggestion = format!("`{}`", suggestion);
839 diag.span_suggestion(
841 &format!("to link to the {}, {}", res.descr(), action),
843 Applicability::MaybeIncorrect,
847 // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
850 // last_new_line_offset
851 let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
852 let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
854 // Print the line containing the `link_range` and manually mark it with '^'s.
856 "the link appears in this line:\n\n{line}\n\
857 {indicator: <before$}{indicator:^<found$}",
860 before = link_range.start - last_new_line_offset,
861 found = link_range.len(),
869 /// Given an enum variant's res, return the res of its enum and the associated fragment.
873 extra_fragment: &Option<String>,
874 ) -> Result<(Res, Option<String>), ErrorKind> {
875 use rustc::ty::DefIdTree;
877 if extra_fragment.is_some() {
878 return Err(ErrorKind::AnchorFailure("variants cannot be followed by anchors"));
880 let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) {
883 return Err(ErrorKind::ResolutionFailure);
885 let parent_def = Res::Def(DefKind::Enum, parent);
886 let variant = cx.tcx.expect_variant_res(res);
887 Ok((parent_def, Some(format!("{}.v", variant.ident.name))))
890 const PRIMITIVES: &[(&str, Res)] = &[
891 ("u8", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U8))),
892 ("u16", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U16))),
893 ("u32", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U32))),
894 ("u64", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U64))),
895 ("u128", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U128))),
896 ("usize", Res::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::Usize))),
897 ("i8", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I8))),
898 ("i16", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I16))),
899 ("i32", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I32))),
900 ("i64", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I64))),
901 ("i128", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I128))),
902 ("isize", Res::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::Isize))),
903 ("f32", Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F32))),
904 ("f64", Res::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F64))),
905 ("str", Res::PrimTy(hir::PrimTy::Str)),
906 ("bool", Res::PrimTy(hir::PrimTy::Bool)),
907 ("char", Res::PrimTy(hir::PrimTy::Char)),
910 fn is_primitive(path_str: &str, ns: Namespace) -> Option<Res> {
911 if ns == TypeNS { PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1) } else { None }
914 fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<DefId> {
917 "u8" => tcx.lang_items().u8_impl(),
918 "u16" => tcx.lang_items().u16_impl(),
919 "u32" => tcx.lang_items().u32_impl(),
920 "u64" => tcx.lang_items().u64_impl(),
921 "u128" => tcx.lang_items().u128_impl(),
922 "usize" => tcx.lang_items().usize_impl(),
923 "i8" => tcx.lang_items().i8_impl(),
924 "i16" => tcx.lang_items().i16_impl(),
925 "i32" => tcx.lang_items().i32_impl(),
926 "i64" => tcx.lang_items().i64_impl(),
927 "i128" => tcx.lang_items().i128_impl(),
928 "isize" => tcx.lang_items().isize_impl(),
929 "f32" => tcx.lang_items().f32_impl(),
930 "f64" => tcx.lang_items().f64_impl(),
931 "str" => tcx.lang_items().str_impl(),
932 "bool" => tcx.lang_items().bool_impl(),
933 "char" => tcx.lang_items().char_impl(),