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