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