]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_intra_doc_links.rs
Rollup merge of #56027 - Xanewok:docs-backtick, r=QuietMisdreavus
[rust.git] / src / librustdoc / passes / collect_intra_doc_links.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use clean::*;
12
13 use rustc::lint as lint;
14 use rustc::hir;
15 use rustc::hir::def::Def;
16 use rustc::ty;
17 use syntax;
18 use syntax::ast::{self, Ident, NodeId};
19 use syntax::feature_gate::UnstableFeatures;
20 use syntax::symbol::Symbol;
21 use syntax_pos::{self, DUMMY_SP};
22
23 use std::ops::Range;
24
25 use core::DocContext;
26 use fold::DocFolder;
27 use html::markdown::{find_testable_code, markdown_links, ErrorCodes, LangString};
28
29 use passes::Pass;
30
31 pub const COLLECT_INTRA_DOC_LINKS: Pass =
32     Pass::early("collect-intra-doc-links", collect_intra_doc_links,
33                 "reads a crate's documentation to resolve intra-doc-links");
34
35 pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext) -> Crate {
36     if !UnstableFeatures::from_environment().is_nightly_build() {
37         krate
38     } else {
39         let mut coll = LinkCollector::new(cx);
40
41         coll.fold_crate(krate)
42     }
43 }
44
45 #[derive(Debug)]
46 enum PathKind {
47     /// can be either value or type, not a macro
48     Unknown,
49     /// macro
50     Macro,
51     /// values, functions, consts, statics, everything in the value namespace
52     Value,
53     /// types, traits, everything in the type namespace
54     Type,
55 }
56
57 struct LinkCollector<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx> {
58     cx: &'a DocContext<'a, 'tcx, 'rcx, 'cstore>,
59     mod_ids: Vec<NodeId>,
60     is_nightly_build: bool,
61 }
62
63 impl<'a, 'tcx, 'rcx, 'cstore> LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
64     fn new(cx: &'a DocContext<'a, 'tcx, 'rcx, 'cstore>) -> Self {
65         LinkCollector {
66             cx,
67             mod_ids: Vec::new(),
68             is_nightly_build: UnstableFeatures::from_environment().is_nightly_build(),
69         }
70     }
71
72     /// Resolve a given string as a path, along with whether or not it is
73     /// in the value namespace. Also returns an optional URL fragment in the case
74     /// of variants and methods
75     fn resolve(&self,
76                path_str: &str,
77                is_val: bool,
78                current_item: &Option<String>,
79                parent_id: Option<NodeId>)
80         -> Result<(Def, Option<String>), ()>
81     {
82         let cx = self.cx;
83
84         // In case we're in a module, try to resolve the relative
85         // path
86         if let Some(id) = parent_id.or(self.mod_ids.last().cloned()) {
87             // FIXME: `with_scope` requires the NodeId of a module
88             let result = cx.resolver.borrow_mut()
89                                     .with_scope(id,
90                 |resolver| {
91                     resolver.resolve_str_path_error(DUMMY_SP,
92                                                     &path_str, is_val)
93             });
94
95             if let Ok(result) = result {
96                 // In case this is a trait item, skip the
97                 // early return and try looking for the trait
98                 let value = match result.def {
99                     Def::Method(_) | Def::AssociatedConst(_) => true,
100                     Def::AssociatedTy(_) => false,
101                     Def::Variant(_) => return handle_variant(cx, result.def),
102                     // not a trait item, just return what we found
103                     _ => return Ok((result.def, None))
104                 };
105
106                 if value != is_val {
107                     return Err(())
108                 }
109             } else if let Some(prim) = is_primitive(path_str, is_val) {
110                 return Ok((prim, Some(path_str.to_owned())))
111             } else {
112                 // If resolution failed, it may still be a method
113                 // because methods are not handled by the resolver
114                 // If so, bail when we're not looking for a value
115                 if !is_val {
116                     return Err(())
117                 }
118             }
119
120             // Try looking for methods and associated items
121             let mut split = path_str.rsplitn(2, "::");
122             let item_name = if let Some(first) = split.next() {
123                 first
124             } else {
125                 return Err(())
126             };
127
128             let mut path = if let Some(second) = split.next() {
129                 second.to_owned()
130             } else {
131                 return Err(())
132             };
133
134             if path == "self" || path == "Self" {
135                 if let Some(name) = current_item.as_ref() {
136                     path = name.clone();
137                 }
138             }
139
140             // FIXME: `with_scope` requires the NodeId of a module
141             let ty = cx.resolver.borrow_mut()
142                                 .with_scope(id,
143                 |resolver| {
144                     resolver.resolve_str_path_error(DUMMY_SP, &path, false)
145             })?;
146             match ty.def {
147                 Def::Struct(did) | Def::Union(did) | Def::Enum(did) | Def::TyAlias(did) => {
148                     let item = cx.tcx.inherent_impls(did)
149                                      .iter()
150                                      .flat_map(|imp| cx.tcx.associated_items(*imp))
151                                      .find(|item| item.ident.name == item_name);
152                     if let Some(item) = item {
153                         let out = match item.kind {
154                             ty::AssociatedKind::Method if is_val => "method",
155                             ty::AssociatedKind::Const if is_val => "associatedconstant",
156                             _ => return Err(())
157                         };
158                         Ok((ty.def, Some(format!("{}.{}", out, item_name))))
159                     } else {
160                         match cx.tcx.type_of(did).sty {
161                             ty::Adt(def, _) => {
162                                 if let Some(item) = if def.is_enum() {
163                                     def.all_fields().find(|item| item.ident.name == item_name)
164                                 } else {
165                                     def.non_enum_variant()
166                                        .fields
167                                        .iter()
168                                        .find(|item| item.ident.name == item_name)
169                                 } {
170                                     Ok((ty.def,
171                                         Some(format!("{}.{}",
172                                                      if def.is_enum() {
173                                                          "variant"
174                                                      } else {
175                                                          "structfield"
176                                                      },
177                                                      item.ident))))
178                                 } else {
179                                     Err(())
180                                 }
181                             }
182                             _ => Err(()),
183                         }
184                     }
185                 }
186                 Def::Trait(did) => {
187                     let item = cx.tcx.associated_item_def_ids(did).iter()
188                                  .map(|item| cx.tcx.associated_item(*item))
189                                  .find(|item| item.ident.name == item_name);
190                     if let Some(item) = item {
191                         let kind = match item.kind {
192                             ty::AssociatedKind::Const if is_val => "associatedconstant",
193                             ty::AssociatedKind::Type if !is_val => "associatedtype",
194                             ty::AssociatedKind::Method if is_val => {
195                                 if item.defaultness.has_value() {
196                                     "method"
197                                 } else {
198                                     "tymethod"
199                                 }
200                             }
201                             _ => return Err(())
202                         };
203
204                         Ok((ty.def, Some(format!("{}.{}", kind, item_name))))
205                     } else {
206                         Err(())
207                     }
208                 }
209                 _ => Err(())
210             }
211         } else {
212             Err(())
213         }
214     }
215 }
216
217 fn look_for_tests<'a, 'tcx: 'a, 'rcx: 'a, 'cstore: 'rcx>(
218     cx: &'a DocContext<'a, 'tcx, 'rcx, 'cstore>,
219     dox: &str,
220     item: &Item,
221 ) {
222     if (item.is_mod() && cx.tcx.hir.as_local_node_id(item.def_id).is_none()) ||
223        cx.as_local_node_id(item.def_id).is_none() {
224         // If non-local, no need to check anything.
225         return;
226     }
227
228     struct Tests {
229         found_tests: usize,
230     }
231
232     impl ::test::Tester for Tests {
233         fn add_test(&mut self, _: String, _: LangString, _: usize) {
234             self.found_tests += 1;
235         }
236     }
237
238     let mut tests = Tests {
239         found_tests: 0,
240     };
241
242     if find_testable_code(&dox, &mut tests, ErrorCodes::No).is_ok() {
243         if tests.found_tests == 0 {
244             let mut diag = cx.tcx.struct_span_lint_node(
245                 lint::builtin::MISSING_DOC_CODE_EXAMPLES,
246                 NodeId::from_u32(0),
247                 span_of_attrs(&item.attrs),
248                 "Missing code example in this documentation");
249             diag.emit();
250         }
251     }
252 }
253
254 impl<'a, 'tcx, 'rcx, 'cstore> DocFolder for LinkCollector<'a, 'tcx, 'rcx, 'cstore> {
255     fn fold_item(&mut self, mut item: Item) -> Option<Item> {
256         let item_node_id = if item.is_mod() {
257             if let Some(id) = self.cx.tcx.hir.as_local_node_id(item.def_id) {
258                 Some(id)
259             } else {
260                 debug!("attempting to fold on a non-local item: {:?}", item);
261                 return self.fold_item_recur(item);
262             }
263         } else {
264             None
265         };
266
267         // FIXME: get the resolver to work with non-local resolve scopes
268         let parent_node = self.cx.as_local_node_id(item.def_id).and_then(|node_id| {
269             // FIXME: this fails hard for impls in non-module scope, but is necessary for the
270             // current resolve() implementation
271             match self.cx.tcx.hir.get_module_parent_node(node_id) {
272                 id if id != node_id => Some(id),
273                 _ => None,
274             }
275         });
276
277         if parent_node.is_some() {
278             debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
279         }
280
281         let current_item = match item.inner {
282             ModuleItem(..) => {
283                 if item.attrs.inner_docs {
284                     if item_node_id.unwrap() != NodeId::from_u32(0) {
285                         item.name.clone()
286                     } else {
287                         None
288                     }
289                 } else {
290                     match parent_node.or(self.mod_ids.last().cloned()) {
291                         Some(parent) if parent != NodeId::from_u32(0) => {
292                             //FIXME: can we pull the parent module's name from elsewhere?
293                             Some(self.cx.tcx.hir.name(parent).to_string())
294                         }
295                         _ => None,
296                     }
297                 }
298             }
299             ImplItem(Impl { ref for_, .. }) => {
300                 for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string())
301             }
302             // we don't display docs on `extern crate` items anyway, so don't process them
303             ExternCrateItem(..) => return self.fold_item_recur(item),
304             ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()),
305             MacroItem(..) => None,
306             _ => item.name.clone(),
307         };
308
309         if item.is_mod() && item.attrs.inner_docs {
310             self.mod_ids.push(item_node_id.unwrap());
311         }
312
313         let cx = self.cx;
314         let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
315
316         look_for_tests(&cx, &dox, &item);
317
318         if !self.is_nightly_build {
319             return None;
320         }
321
322         for (ori_link, link_range) in markdown_links(&dox) {
323             // bail early for real links
324             if ori_link.contains('/') {
325                 continue;
326             }
327             let link = ori_link.replace("`", "");
328             let (def, fragment) = {
329                 let mut kind = PathKind::Unknown;
330                 let path_str = if let Some(prefix) =
331                     ["struct@", "enum@", "type@",
332                      "trait@", "union@"].iter()
333                                       .find(|p| link.starts_with(**p)) {
334                     kind = PathKind::Type;
335                     link.trim_left_matches(prefix)
336                 } else if let Some(prefix) =
337                     ["const@", "static@",
338                      "value@", "function@", "mod@",
339                      "fn@", "module@", "method@"]
340                         .iter().find(|p| link.starts_with(**p)) {
341                     kind = PathKind::Value;
342                     link.trim_left_matches(prefix)
343                 } else if link.ends_with("()") {
344                     kind = PathKind::Value;
345                     link.trim_right_matches("()")
346                 } else if link.starts_with("macro@") {
347                     kind = PathKind::Macro;
348                     link.trim_left_matches("macro@")
349                 } else if link.ends_with('!') {
350                     kind = PathKind::Macro;
351                     link.trim_right_matches('!')
352                 } else {
353                     &link[..]
354                 }.trim();
355
356                 if path_str.contains(|ch: char| !(ch.is_alphanumeric() ||
357                                                   ch == ':' || ch == '_')) {
358                     continue;
359                 }
360
361                 match kind {
362                     PathKind::Value => {
363                         if let Ok(def) = self.resolve(path_str, true, &current_item, parent_node) {
364                             def
365                         } else {
366                             resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
367                             // this could just be a normal link or a broken link
368                             // we could potentially check if something is
369                             // "intra-doc-link-like" and warn in that case
370                             continue;
371                         }
372                     }
373                     PathKind::Type => {
374                         if let Ok(def) = self.resolve(path_str, false, &current_item, parent_node) {
375                             def
376                         } else {
377                             resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
378                             // this could just be a normal link
379                             continue;
380                         }
381                     }
382                     PathKind::Unknown => {
383                         // try everything!
384                         if let Some(macro_def) = macro_resolve(cx, path_str) {
385                             if let Ok(type_def) =
386                                 self.resolve(path_str, false, &current_item, parent_node)
387                             {
388                                 let (type_kind, article, type_disambig)
389                                     = type_ns_kind(type_def.0, path_str);
390                                 ambiguity_error(cx, &item.attrs, path_str,
391                                                 article, type_kind, &type_disambig,
392                                                 "a", "macro", &format!("macro@{}", path_str));
393                                 continue;
394                             } else if let Ok(value_def) =
395                                 self.resolve(path_str, true, &current_item, parent_node)
396                             {
397                                 let (value_kind, value_disambig)
398                                     = value_ns_kind(value_def.0, path_str)
399                                         .expect("struct and mod cases should have been \
400                                                  caught in previous branch");
401                                 ambiguity_error(cx, &item.attrs, path_str,
402                                                 "a", value_kind, &value_disambig,
403                                                 "a", "macro", &format!("macro@{}", path_str));
404                             }
405                             (macro_def, None)
406                         } else if let Ok(type_def) =
407                             self.resolve(path_str, false, &current_item, parent_node)
408                         {
409                             // It is imperative we search for not-a-value first
410                             // Otherwise we will find struct ctors for when we are looking
411                             // for structs, and the link won't work.
412                             // if there is something in both namespaces
413                             if let Ok(value_def) =
414                                 self.resolve(path_str, true, &current_item, parent_node)
415                             {
416                                 let kind = value_ns_kind(value_def.0, path_str);
417                                 if let Some((value_kind, value_disambig)) = kind {
418                                     let (type_kind, article, type_disambig)
419                                         = type_ns_kind(type_def.0, path_str);
420                                     ambiguity_error(cx, &item.attrs, path_str,
421                                                     article, type_kind, &type_disambig,
422                                                     "a", value_kind, &value_disambig);
423                                     continue;
424                                 }
425                             }
426                             type_def
427                         } else if let Ok(value_def) =
428                             self.resolve(path_str, true, &current_item, parent_node)
429                         {
430                             value_def
431                         } else {
432                             resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
433                             // this could just be a normal link
434                             continue;
435                         }
436                     }
437                     PathKind::Macro => {
438                         if let Some(def) = macro_resolve(cx, path_str) {
439                             (def, None)
440                         } else {
441                             resolution_failure(cx, &item.attrs, path_str, &dox, link_range);
442                             continue
443                         }
444                     }
445                 }
446             };
447
448             if let Def::PrimTy(_) = def {
449                 item.attrs.links.push((ori_link, None, fragment));
450             } else {
451                 let id = register_def(cx, def);
452                 item.attrs.links.push((ori_link, Some(id), fragment));
453             }
454         }
455
456         if item.is_mod() && !item.attrs.inner_docs {
457             self.mod_ids.push(item_node_id.unwrap());
458         }
459
460         if item.is_mod() {
461             let ret = self.fold_item_recur(item);
462
463             self.mod_ids.pop();
464
465             ret
466         } else {
467             self.fold_item_recur(item)
468         }
469     }
470 }
471
472 /// Resolve a string as a macro
473 fn macro_resolve(cx: &DocContext, path_str: &str) -> Option<Def> {
474     use syntax::ext::base::{MacroKind, SyntaxExtension};
475     let segment = ast::PathSegment::from_ident(Ident::from_str(path_str));
476     let path = ast::Path { segments: vec![segment], span: DUMMY_SP };
477     let mut resolver = cx.resolver.borrow_mut();
478     let parent_scope = resolver.dummy_parent_scope();
479     if let Ok(def) = resolver.resolve_macro_to_def_inner(&path, MacroKind::Bang,
480                                                          &parent_scope, false) {
481         if let SyntaxExtension::DeclMacro { .. } = *resolver.get_macro(def) {
482             return Some(def);
483         }
484     }
485     if let Some(def) = resolver.all_macros.get(&Symbol::intern(path_str)) {
486         return Some(*def);
487     }
488     None
489 }
490
491 fn span_of_attrs(attrs: &Attributes) -> syntax_pos::Span {
492     if attrs.doc_strings.is_empty() {
493         return DUMMY_SP;
494     }
495     let start = attrs.doc_strings[0].span();
496     let end = attrs.doc_strings.last().expect("No doc strings provided").span();
497     start.to(end)
498 }
499
500 fn resolution_failure(
501     cx: &DocContext,
502     attrs: &Attributes,
503     path_str: &str,
504     dox: &str,
505     link_range: Option<Range<usize>>,
506 ) {
507     let sp = span_of_attrs(attrs);
508     let msg = format!("`[{}]` cannot be resolved, ignoring it...", path_str);
509
510     let code_dox = sp.to_src(cx);
511
512     let doc_comment_padding = 3;
513     let mut diag = if let Some(link_range) = link_range {
514         // blah blah blah\nblah\nblah [blah] blah blah\nblah blah
515         //                       ^    ~~~~~~
516         //                       |    link_range
517         //                       last_new_line_offset
518
519         let mut diag;
520         if dox.lines().count() == code_dox.lines().count() {
521             let line_offset = dox[..link_range.start].lines().count();
522             // The span starts in the `///`, so we don't have to account for the leading whitespace
523             let code_dox_len = if line_offset <= 1 {
524                 doc_comment_padding
525             } else {
526                 // The first `///`
527                 doc_comment_padding +
528                     // Each subsequent leading whitespace and `///`
529                     code_dox.lines().skip(1).take(line_offset - 1).fold(0, |sum, line| {
530                         sum + doc_comment_padding + line.len() - line.trim_start().len()
531                     })
532             };
533
534             // Extract the specific span
535             let sp = sp.from_inner_byte_pos(
536                 link_range.start + code_dox_len,
537                 link_range.end + code_dox_len,
538             );
539
540             diag = cx.tcx.struct_span_lint_node(lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
541                                                 NodeId::from_u32(0),
542                                                 sp,
543                                                 &msg);
544             diag.span_label(sp, "cannot be resolved, ignoring");
545         } else {
546             diag = cx.tcx.struct_span_lint_node(lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
547                                                 NodeId::from_u32(0),
548                                                 sp,
549                                                 &msg);
550
551             let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
552             let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
553
554             // Print the line containing the `link_range` and manually mark it with '^'s
555             diag.note(&format!(
556                 "the link appears in this line:\n\n{line}\n\
557                  {indicator: <before$}{indicator:^<found$}",
558                 line=line,
559                 indicator="",
560                 before=link_range.start - last_new_line_offset,
561                 found=link_range.len(),
562             ));
563         }
564         diag
565     } else {
566         cx.tcx.struct_span_lint_node(lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE,
567                                      NodeId::from_u32(0),
568                                      sp,
569                                      &msg)
570     };
571     diag.help("to escape `[` and `]` characters, just add '\\' before them like \
572                `\\[` or `\\]`");
573     diag.emit();
574 }
575
576 fn ambiguity_error(cx: &DocContext, attrs: &Attributes,
577                    path_str: &str,
578                    article1: &str, kind1: &str, disambig1: &str,
579                    article2: &str, kind2: &str, disambig2: &str) {
580     let sp = span_of_attrs(attrs);
581     cx.sess()
582       .struct_span_warn(sp,
583                         &format!("`{}` is both {} {} and {} {}",
584                                  path_str, article1, kind1,
585                                  article2, kind2))
586       .help(&format!("try `{}` if you want to select the {}, \
587                       or `{}` if you want to \
588                       select the {}",
589                       disambig1, kind1, disambig2,
590                       kind2))
591       .emit();
592 }
593
594 /// Given a def, returns its name and disambiguator
595 /// for a value namespace
596 ///
597 /// Returns None for things which cannot be ambiguous since
598 /// they exist in both namespaces (structs and modules)
599 fn value_ns_kind(def: Def, path_str: &str) -> Option<(&'static str, String)> {
600     match def {
601         // structs, variants, and mods exist in both namespaces. skip them
602         Def::StructCtor(..) | Def::Mod(..) | Def::Variant(..) |
603         Def::VariantCtor(..) | Def::SelfCtor(..)
604             => None,
605         Def::Fn(..)
606             => Some(("function", format!("{}()", path_str))),
607         Def::Method(..)
608             => Some(("method", format!("{}()", path_str))),
609         Def::Const(..)
610             => Some(("const", format!("const@{}", path_str))),
611         Def::Static(..)
612             => Some(("static", format!("static@{}", path_str))),
613         _ => Some(("value", format!("value@{}", path_str))),
614     }
615 }
616
617 /// Given a def, returns its name, the article to be used, and a disambiguator
618 /// for the type namespace
619 fn type_ns_kind(def: Def, path_str: &str) -> (&'static str, &'static str, String) {
620     let (kind, article) = match def {
621         // we can still have non-tuple structs
622         Def::Struct(..) => ("struct", "a"),
623         Def::Enum(..) => ("enum", "an"),
624         Def::Trait(..) => ("trait", "a"),
625         Def::Union(..) => ("union", "a"),
626         _ => ("type", "a"),
627     };
628     (kind, article, format!("{}@{}", kind, path_str))
629 }
630
631 /// Given an enum variant's def, return the def of its enum and the associated fragment
632 fn handle_variant(cx: &DocContext, def: Def) -> Result<(Def, Option<String>), ()> {
633     use rustc::ty::DefIdTree;
634
635     let parent = if let Some(parent) = cx.tcx.parent(def.def_id()) {
636         parent
637     } else {
638         return Err(())
639     };
640     let parent_def = Def::Enum(parent);
641     let variant = cx.tcx.expect_variant_def(def);
642     Ok((parent_def, Some(format!("{}.v", variant.name))))
643 }
644
645 const PRIMITIVES: &[(&str, Def)] = &[
646     ("u8",    Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U8))),
647     ("u16",   Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U16))),
648     ("u32",   Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U32))),
649     ("u64",   Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U64))),
650     ("u128",  Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::U128))),
651     ("usize", Def::PrimTy(hir::PrimTy::Uint(syntax::ast::UintTy::Usize))),
652     ("i8",    Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I8))),
653     ("i16",   Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I16))),
654     ("i32",   Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I32))),
655     ("i64",   Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I64))),
656     ("i128",  Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::I128))),
657     ("isize", Def::PrimTy(hir::PrimTy::Int(syntax::ast::IntTy::Isize))),
658     ("f32",   Def::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F32))),
659     ("f64",   Def::PrimTy(hir::PrimTy::Float(syntax::ast::FloatTy::F64))),
660     ("str",   Def::PrimTy(hir::PrimTy::Str)),
661     ("bool",  Def::PrimTy(hir::PrimTy::Bool)),
662     ("char",  Def::PrimTy(hir::PrimTy::Char)),
663 ];
664
665 fn is_primitive(path_str: &str, is_val: bool) -> Option<Def> {
666     if is_val {
667         None
668     } else {
669         PRIMITIVES.iter().find(|x| x.0 == path_str).map(|x| x.1)
670     }
671 }