]> git.lizzy.rs Git - rust.git/blob - src/librustc_save_analysis/lib.rs
Inspect def locally instead of using a method
[rust.git] / src / librustc_save_analysis / lib.rs
1 // Copyright 2012-2015 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 #![crate_name = "rustc_save_analysis"]
12 #![unstable(feature = "rustc_private", issue = "27812")]
13 #![crate_type = "dylib"]
14 #![crate_type = "rlib"]
15 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
16       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
17       html_root_url = "https://doc.rust-lang.org/nightly/")]
18 #![cfg_attr(not(stage0), deny(warnings))]
19
20 #![feature(custom_attribute)]
21 #![cfg_attr(stage0, feature(dotdot_in_tuple_patterns))]
22 #![allow(unused_attributes)]
23 #![feature(rustc_private)]
24 #![feature(staged_api)]
25
26 #[macro_use] extern crate rustc;
27
28 #[macro_use] extern crate log;
29 #[macro_use] extern crate syntax;
30 extern crate serialize as rustc_serialize;
31 extern crate syntax_pos;
32
33
34 mod csv_dumper;
35 mod json_api_dumper;
36 mod json_dumper;
37 mod data;
38 mod dump;
39 mod dump_visitor;
40 pub mod external_data;
41 #[macro_use]
42 pub mod span_utils;
43
44 use rustc::hir;
45 use rustc::hir::map::{Node, NodeItem};
46 use rustc::hir::def::Def;
47 use rustc::hir::def_id::DefId;
48 use rustc::session::config::CrateType::CrateTypeExecutable;
49 use rustc::ty::{self, TyCtxt};
50
51 use std::env;
52 use std::fs::{self, File};
53 use std::path::{Path, PathBuf};
54
55 use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
56 use syntax::parse::lexer::comments::strip_doc_comment_decoration;
57 use syntax::parse::token::{self, keywords, InternedString};
58 use syntax::visit::{self, Visitor};
59 use syntax::print::pprust::{ty_to_string, arg_to_string};
60 use syntax::codemap::MacroAttribute;
61 use syntax_pos::*;
62
63 pub use self::csv_dumper::CsvDumper;
64 pub use self::json_api_dumper::JsonApiDumper;
65 pub use self::json_dumper::JsonDumper;
66 pub use self::data::*;
67 pub use self::external_data::make_def_id;
68 pub use self::dump::Dump;
69 pub use self::dump_visitor::DumpVisitor;
70 use self::span_utils::SpanUtils;
71
72 // FIXME this is legacy code and should be removed
73 pub mod recorder {
74     pub use self::Row::*;
75
76     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
77     pub enum Row {
78         TypeRef,
79         ModRef,
80         VarRef,
81         FnRef,
82     }
83 }
84
85 pub struct SaveContext<'l, 'tcx: 'l> {
86     tcx: TyCtxt<'l, 'tcx, 'tcx>,
87     span_utils: SpanUtils<'tcx>,
88 }
89
90 macro_rules! option_try(
91     ($e:expr) => (match $e { Some(e) => e, None => return None })
92 );
93
94 impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
95     pub fn new(tcx: TyCtxt<'l, 'tcx, 'tcx>) -> SaveContext<'l, 'tcx> {
96         let span_utils = SpanUtils::new(&tcx.sess);
97         SaveContext::from_span_utils(tcx, span_utils)
98     }
99
100     pub fn from_span_utils(tcx: TyCtxt<'l, 'tcx, 'tcx>,
101                            span_utils: SpanUtils<'tcx>)
102                            -> SaveContext<'l, 'tcx> {
103         SaveContext {
104             tcx: tcx,
105             span_utils: span_utils,
106         }
107     }
108
109     // List external crates used by the current crate.
110     pub fn get_external_crates(&self) -> Vec<CrateData> {
111         let mut result = Vec::new();
112
113         for n in self.tcx.sess.cstore.crates() {
114             let span = match self.tcx.sess.cstore.extern_crate(n) {
115                 Some(ref c) => c.span,
116                 None => {
117                     debug!("Skipping crate {}, no data", n);
118                     continue;
119                 }
120             };
121             result.push(CrateData {
122                 name: (&self.tcx.sess.cstore.crate_name(n)[..]).to_owned(),
123                 number: n.as_u32(),
124                 span: span,
125             });
126         }
127
128         result
129     }
130
131     pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
132         match item.node {
133             ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
134                 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
135                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
136                 filter!(self.span_utils, sub_span, item.span, None);
137
138
139                 Some(Data::FunctionData(FunctionData {
140                     id: item.id,
141                     name: item.ident.to_string(),
142                     qualname: qualname,
143                     declaration: None,
144                     span: sub_span.unwrap(),
145                     scope: self.enclosing_scope(item.id),
146                     value: make_signature(decl, generics),
147                     visibility: From::from(&item.vis),
148                     parent: None,
149                     docs: docs_for_attrs(&item.attrs),
150                 }))
151             }
152             ast::ItemKind::Static(ref typ, mt, ref expr) => {
153                 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
154
155                 // If the variable is immutable, save the initialising expression.
156                 let (value, keyword) = match mt {
157                     ast::Mutability::Mutable => (String::from("<mutable>"), keywords::Mut),
158                     ast::Mutability::Immutable => {
159                         (self.span_utils.snippet(expr.span), keywords::Static)
160                     },
161                 };
162
163                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
164                 filter!(self.span_utils, sub_span, item.span, None);
165                 Some(Data::VariableData(VariableData {
166                     id: item.id,
167                     kind: VariableKind::Static,
168                     name: item.ident.to_string(),
169                     qualname: qualname,
170                     span: sub_span.unwrap(),
171                     scope: self.enclosing_scope(item.id),
172                     parent: None,
173                     value: value,
174                     type_value: ty_to_string(&typ),
175                     visibility: From::from(&item.vis),
176                     docs: docs_for_attrs(&item.attrs),
177                 }))
178             }
179             ast::ItemKind::Const(ref typ, ref expr) => {
180                 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
181                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const);
182                 filter!(self.span_utils, sub_span, item.span, None);
183                 Some(Data::VariableData(VariableData {
184                     id: item.id,
185                     kind: VariableKind::Const,
186                     name: item.ident.to_string(),
187                     qualname: qualname,
188                     span: sub_span.unwrap(),
189                     scope: self.enclosing_scope(item.id),
190                     parent: None,
191                     value: self.span_utils.snippet(expr.span),
192                     type_value: ty_to_string(&typ),
193                     visibility: From::from(&item.vis),
194                     docs: docs_for_attrs(&item.attrs),
195                 }))
196             }
197             ast::ItemKind::Mod(ref m) => {
198                 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
199
200                 let cm = self.tcx.sess.codemap();
201                 let filename = cm.span_to_filename(m.inner);
202
203                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod);
204                 filter!(self.span_utils, sub_span, item.span, None);
205                 Some(Data::ModData(ModData {
206                     id: item.id,
207                     name: item.ident.to_string(),
208                     qualname: qualname,
209                     span: sub_span.unwrap(),
210                     scope: self.enclosing_scope(item.id),
211                     filename: filename,
212                     items: m.items.iter().map(|i| i.id).collect(),
213                     visibility: From::from(&item.vis),
214                     docs: docs_for_attrs(&item.attrs),
215                 }))
216             }
217             ast::ItemKind::Enum(ref def, _) => {
218                 let name = item.ident.to_string();
219                 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
220                 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum);
221                 filter!(self.span_utils, sub_span, item.span, None);
222                 let variants_str = def.variants.iter()
223                                       .map(|v| v.node.name.to_string())
224                                       .collect::<Vec<_>>()
225                                       .join(", ");
226                 let val = format!("{}::{{{}}}", name, variants_str);
227                 Some(Data::EnumData(EnumData {
228                     id: item.id,
229                     name: name,
230                     value: val,
231                     span: sub_span.unwrap(),
232                     qualname: qualname,
233                     scope: self.enclosing_scope(item.id),
234                     variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
235                     visibility: From::from(&item.vis),
236                     docs: docs_for_attrs(&item.attrs),
237                 }))
238             }
239             ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => {
240                 let mut type_data = None;
241                 let sub_span;
242
243                 let parent = self.enclosing_scope(item.id);
244
245                 match typ.node {
246                     // Common case impl for a struct or something basic.
247                     ast::TyKind::Path(None, ref path) => {
248                         sub_span = self.span_utils.sub_span_for_type_name(path.span);
249                         filter!(self.span_utils, sub_span, path.span, None);
250                         type_data = self.lookup_ref_id(typ.id).map(|id| {
251                             TypeRefData {
252                                 span: sub_span.unwrap(),
253                                 scope: parent,
254                                 ref_id: Some(id),
255                                 qualname: String::new() // FIXME: generate the real qualname
256                             }
257                         });
258                     }
259                     _ => {
260                         // Less useful case, impl for a compound type.
261                         let span = typ.span;
262                         sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
263                     }
264                 }
265
266                 let trait_data = trait_ref.as_ref()
267                                           .and_then(|tr| self.get_trait_ref_data(tr, parent));
268
269                 filter!(self.span_utils, sub_span, typ.span, None);
270                 Some(Data::ImplData(ImplData2 {
271                     id: item.id,
272                     span: sub_span.unwrap(),
273                     scope: parent,
274                     trait_ref: trait_data,
275                     self_ref: type_data,
276                 }))
277             }
278             _ => {
279                 // FIXME
280                 bug!();
281             }
282         }
283     }
284
285     pub fn get_field_data(&self, field: &ast::StructField,
286                           scope: NodeId) -> Option<VariableData> {
287         if let Some(ident) = field.ident {
288             let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident);
289             let def_id = self.tcx.map.local_def_id(field.id);
290             let typ = self.tcx.item_type(def_id).to_string();
291             let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon);
292             filter!(self.span_utils, sub_span, field.span, None);
293             Some(VariableData {
294                 id: field.id,
295                 kind: VariableKind::Field,
296                 name: ident.to_string(),
297                 qualname: qualname,
298                 span: sub_span.unwrap(),
299                 scope: scope,
300                 parent: Some(make_def_id(scope, &self.tcx.map)),
301                 value: "".to_owned(),
302                 type_value: typ,
303                 visibility: From::from(&field.vis),
304                 docs: docs_for_attrs(&field.attrs),
305             })
306         } else {
307             None
308         }
309     }
310
311     // FIXME would be nice to take a MethodItem here, but the ast provides both
312     // trait and impl flavours, so the caller must do the disassembly.
313     pub fn get_method_data(&self, id: ast::NodeId,
314                            name: ast::Name, span: Span) -> Option<FunctionData> {
315         // The qualname for a method is the trait name or name of the struct in an impl in
316         // which the method is declared in, followed by the method's name.
317         let (qualname, parent_scope, decl_id, vis, docs) =
318           match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) {
319             Some(impl_id) => match self.tcx.map.get_if_local(impl_id) {
320                 Some(NodeItem(item)) => {
321                     match item.node {
322                         hir::ItemImpl(.., ref ty, _) => {
323                             let mut result = String::from("<");
324                             result.push_str(&rustc::hir::print::ty_to_string(&ty));
325
326                             let trait_id = self.tcx.trait_id_of_impl(impl_id);
327                             let mut decl_id = None;
328                             if let Some(def_id) = trait_id {
329                                 result.push_str(" as ");
330                                 result.push_str(&self.tcx.item_path_str(def_id));
331                                 self.tcx.associated_items(def_id)
332                                     .find(|item| item.name == name)
333                                     .map(|item| decl_id = Some(item.def_id));
334                             }
335                             result.push_str(">");
336
337                             (result, trait_id, decl_id,
338                              From::from(&item.vis),
339                              docs_for_attrs(&item.attrs))
340                         }
341                         _ => {
342                             span_bug!(span,
343                                       "Container {:?} for method {} not an impl?",
344                                       impl_id,
345                                       id);
346                         }
347                     }
348                 }
349                 r => {
350                     span_bug!(span,
351                               "Container {:?} for method {} is not a node item {:?}",
352                               impl_id,
353                               id,
354                               r);
355                 }
356             },
357             None => match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) {
358                 Some(def_id) => {
359                     match self.tcx.map.get_if_local(def_id) {
360                         Some(NodeItem(item)) => {
361                             (format!("::{}", self.tcx.item_path_str(def_id)),
362                              Some(def_id), None,
363                              From::from(&item.vis),
364                              docs_for_attrs(&item.attrs))
365                         }
366                         r => {
367                             span_bug!(span,
368                                       "Could not find container {:?} for \
369                                        method {}, got {:?}",
370                                       def_id,
371                                       id,
372                                       r);
373                         }
374                     }
375                 }
376                 None => {
377                     span_bug!(span, "Could not find container for method {}", id);
378                 }
379             },
380         };
381
382         let qualname = format!("{}::{}", qualname, name);
383
384         let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
385         filter!(self.span_utils, sub_span, span, None);
386         Some(FunctionData {
387             id: id,
388             name: name.to_string(),
389             qualname: qualname,
390             declaration: decl_id,
391             span: sub_span.unwrap(),
392             scope: self.enclosing_scope(id),
393             // FIXME you get better data here by using the visitor.
394             value: String::new(),
395             visibility: vis,
396             parent: parent_scope,
397             docs: docs,
398         })
399     }
400
401     pub fn get_trait_ref_data(&self,
402                               trait_ref: &ast::TraitRef,
403                               parent: NodeId)
404                               -> Option<TypeRefData> {
405         self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
406             let span = trait_ref.path.span;
407             let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
408             filter!(self.span_utils, sub_span, span, None);
409             Some(TypeRefData {
410                 span: sub_span.unwrap(),
411                 scope: parent,
412                 ref_id: Some(def_id),
413                 qualname: String::new() // FIXME: generate the real qualname
414             })
415         })
416     }
417
418     pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
419         let hir_node = self.tcx.map.expect_expr(expr.id);
420         let ty = self.tcx.tables().expr_ty_adjusted_opt(&hir_node);
421         if ty.is_none() || ty.unwrap().sty == ty::TyError {
422             return None;
423         }
424         match expr.node {
425             ast::ExprKind::Field(ref sub_ex, ident) => {
426                 let hir_node = match self.tcx.map.find(sub_ex.id) {
427                     Some(Node::NodeExpr(expr)) => expr,
428                     _ => {
429                         debug!("Missing or weird node for sub-expression {} in {:?}",
430                                sub_ex.id, expr);
431                         return None;
432                     }
433                 };
434                 match self.tcx.tables().expr_ty_adjusted(&hir_node).sty {
435                     ty::TyAdt(def, _) if !def.is_enum() => {
436                         let f = def.struct_variant().field_named(ident.node.name);
437                         let sub_span = self.span_utils.span_for_last_ident(expr.span);
438                         filter!(self.span_utils, sub_span, expr.span, None);
439                         return Some(Data::VariableRefData(VariableRefData {
440                             name: ident.node.to_string(),
441                             span: sub_span.unwrap(),
442                             scope: self.enclosing_scope(expr.id),
443                             ref_id: f.did,
444                         }));
445                     }
446                     _ => {
447                         debug!("Expected struct or union type, found {:?}", ty);
448                         None
449                     }
450                 }
451             }
452             ast::ExprKind::Struct(ref path, ..) => {
453                 match self.tcx.tables().expr_ty_adjusted(&hir_node).sty {
454                     ty::TyAdt(def, _) if !def.is_enum() => {
455                         let sub_span = self.span_utils.span_for_last_ident(path.span);
456                         filter!(self.span_utils, sub_span, path.span, None);
457                         Some(Data::TypeRefData(TypeRefData {
458                             span: sub_span.unwrap(),
459                             scope: self.enclosing_scope(expr.id),
460                             ref_id: Some(def.did),
461                             qualname: String::new() // FIXME: generate the real qualname
462                         }))
463                     }
464                     _ => {
465                         // FIXME ty could legitimately be an enum, but then we will fail
466                         // later if we try to look up the fields.
467                         debug!("expected struct or union, found {:?}", ty);
468                         None
469                     }
470                 }
471             }
472             ast::ExprKind::MethodCall(..) => {
473                 let method_call = ty::MethodCall::expr(expr.id);
474                 let method_id = self.tcx.tables().method_map[&method_call].def_id;
475                 let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
476                     ty::ImplContainer(_) => (Some(method_id), None),
477                     ty::TraitContainer(_) => (None, Some(method_id)),
478                 };
479                 let sub_span = self.span_utils.sub_span_for_meth_name(expr.span);
480                 filter!(self.span_utils, sub_span, expr.span, None);
481                 let parent = self.enclosing_scope(expr.id);
482                 Some(Data::MethodCallData(MethodCallData {
483                     span: sub_span.unwrap(),
484                     scope: parent,
485                     ref_id: def_id,
486                     decl_id: decl_id,
487                 }))
488             }
489             ast::ExprKind::Path(_, ref path) => {
490                 self.get_path_data(expr.id, path)
491             }
492             _ => {
493                 // FIXME
494                 bug!();
495             }
496         }
497     }
498
499     pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> {
500         let resolution = self.tcx.expect_resolution(id);
501         if resolution.depth != 0 {
502             return None;
503         }
504         let def = resolution.base_def;
505
506         let sub_span = self.span_utils.span_for_last_ident(path.span);
507         filter!(self.span_utils, sub_span, path.span, None);
508         match def {
509             Def::Upvar(..) |
510             Def::Local(..) |
511             Def::Static(..) |
512             Def::Const(..) |
513             Def::AssociatedConst(..) |
514             Def::StructCtor(..) |
515             Def::VariantCtor(..) => {
516                 Some(Data::VariableRefData(VariableRefData {
517                     name: self.span_utils.snippet(sub_span.unwrap()),
518                     span: sub_span.unwrap(),
519                     scope: self.enclosing_scope(id),
520                     ref_id: def.def_id(),
521                 }))
522             }
523             Def::Struct(def_id) |
524             Def::Variant(def_id, ..) |
525             Def::Union(def_id) |
526             Def::Enum(def_id) |
527             Def::TyAlias(def_id) |
528             Def::AssociatedTy(def_id) |
529             Def::Trait(def_id) |
530             Def::TyParam(def_id) => {
531                 Some(Data::TypeRefData(TypeRefData {
532                     span: sub_span.unwrap(),
533                     ref_id: Some(def_id),
534                     scope: self.enclosing_scope(id),
535                     qualname: String::new() // FIXME: generate the real qualname
536                 }))
537             }
538             Def::Method(decl_id) => {
539                 let sub_span = self.span_utils.sub_span_for_meth_name(path.span);
540                 filter!(self.span_utils, sub_span, path.span, None);
541                 let def_id = if decl_id.is_local() {
542                     let ti = self.tcx.associated_item(decl_id);
543                     self.tcx.associated_items(ti.container.id())
544                         .find(|item| item.name == ti.name && item.defaultness.has_value())
545                         .map(|item| item.def_id)
546                 } else {
547                     None
548                 };
549                 Some(Data::MethodCallData(MethodCallData {
550                     span: sub_span.unwrap(),
551                     scope: self.enclosing_scope(id),
552                     ref_id: def_id,
553                     decl_id: Some(decl_id),
554                 }))
555             }
556             Def::Fn(def_id) => {
557                 Some(Data::FunctionCallData(FunctionCallData {
558                     ref_id: def_id,
559                     span: sub_span.unwrap(),
560                     scope: self.enclosing_scope(id),
561                 }))
562             }
563             Def::Mod(def_id) => {
564                 Some(Data::ModRefData(ModRefData {
565                     ref_id: Some(def_id),
566                     span: sub_span.unwrap(),
567                     scope: self.enclosing_scope(id),
568                     qualname: String::new() // FIXME: generate the real qualname
569                 }))
570             }
571             Def::PrimTy(..) |
572             Def::SelfTy(..) |
573             Def::Label(..) |
574             Def::Macro(..) |
575             Def::Err => None,
576         }
577     }
578
579     pub fn get_field_ref_data(&self,
580                               field_ref: &ast::Field,
581                               variant: ty::VariantDef,
582                               parent: NodeId)
583                               -> Option<VariableRefData> {
584         let f = variant.field_named(field_ref.ident.node.name);
585         // We don't really need a sub-span here, but no harm done
586         let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
587         filter!(self.span_utils, sub_span, field_ref.ident.span, None);
588         Some(VariableRefData {
589             name: field_ref.ident.node.to_string(),
590             span: sub_span.unwrap(),
591             scope: parent,
592             ref_id: f.did,
593         })
594     }
595
596     /// Attempt to return MacroUseData for any AST node.
597     ///
598     /// For a given piece of AST defined by the supplied Span and NodeId,
599     /// returns None if the node is not macro-generated or the span is malformed,
600     /// else uses the expansion callsite and callee to return some MacroUseData.
601     pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> {
602         if !generated_code(span) {
603             return None;
604         }
605         // Note we take care to use the source callsite/callee, to handle
606         // nested expansions and ensure we only generate data for source-visible
607         // macro uses.
608         let callsite = self.tcx.sess.codemap().source_callsite(span);
609         let callee = self.tcx.sess.codemap().source_callee(span);
610         let callee = option_try!(callee);
611         let callee_span = option_try!(callee.span);
612
613         // Ignore attribute macros, their spans are usually mangled
614         if let MacroAttribute(_) = callee.format {
615             return None;
616         }
617
618         // If the callee is an imported macro from an external crate, need to get
619         // the source span and name from the session, as their spans are localized
620         // when read in, and no longer correspond to the source.
621         if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
622             let &(ref mac_name, mac_span) = mac;
623             return Some(MacroUseData {
624                                         span: callsite,
625                                         name: mac_name.clone(),
626                                         callee_span: mac_span,
627                                         scope: self.enclosing_scope(id),
628                                         imported: true,
629                                         qualname: String::new()// FIXME: generate the real qualname
630                                     });
631         }
632
633         Some(MacroUseData {
634             span: callsite,
635             name: callee.name().to_string(),
636             callee_span: callee_span,
637             scope: self.enclosing_scope(id),
638             imported: false,
639             qualname: String::new() // FIXME: generate the real qualname
640         })
641     }
642
643     pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
644         // FIXME
645         bug!();
646     }
647
648     fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
649         match self.tcx.expect_def(ref_id) {
650             Def::PrimTy(_) | Def::SelfTy(..) => None,
651             def => Some(def.def_id()),
652         }
653     }
654
655     #[inline]
656     pub fn enclosing_scope(&self, id: NodeId) -> NodeId {
657         self.tcx.map.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID)
658     }
659 }
660
661 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
662     let mut sig = "fn ".to_owned();
663     if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
664         sig.push('<');
665         sig.push_str(&generics.lifetimes.iter()
666                               .map(|l| l.lifetime.name.to_string())
667                               .collect::<Vec<_>>()
668                               .join(", "));
669         if !generics.lifetimes.is_empty() {
670             sig.push_str(", ");
671         }
672         sig.push_str(&generics.ty_params.iter()
673                               .map(|l| l.ident.to_string())
674                               .collect::<Vec<_>>()
675                               .join(", "));
676         sig.push_str("> ");
677     }
678     sig.push('(');
679     sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
680     sig.push(')');
681     match decl.output {
682         ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
683         ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
684     }
685
686     sig
687 }
688
689 // An AST visitor for collecting paths from patterns.
690 struct PathCollector {
691     // The Row field identifies the kind of pattern.
692     collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
693 }
694
695 impl PathCollector {
696     fn new() -> PathCollector {
697         PathCollector { collected_paths: vec![] }
698     }
699 }
700
701 impl Visitor for PathCollector {
702     fn visit_pat(&mut self, p: &ast::Pat) {
703         match p.node {
704             PatKind::Struct(ref path, ..) => {
705                 self.collected_paths.push((p.id, path.clone(),
706                                            ast::Mutability::Mutable, recorder::TypeRef));
707             }
708             PatKind::TupleStruct(ref path, ..) |
709             PatKind::Path(_, ref path) => {
710                 self.collected_paths.push((p.id, path.clone(),
711                                            ast::Mutability::Mutable, recorder::VarRef));
712             }
713             PatKind::Ident(bm, ref path1, _) => {
714                 debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
715                        path1.node,
716                        p.span,
717                        path1.span);
718                 let immut = match bm {
719                     // Even if the ref is mut, you can't change the ref, only
720                     // the data pointed at, so showing the initialising expression
721                     // is still worthwhile.
722                     ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
723                     ast::BindingMode::ByValue(mt) => mt,
724                 };
725                 // collect path for either visit_local or visit_arm
726                 let path = ast::Path::from_ident(path1.span, path1.node);
727                 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
728             }
729             _ => {}
730         }
731         visit::walk_pat(self, p);
732     }
733 }
734
735 fn docs_for_attrs(attrs: &[Attribute]) -> String {
736     let doc = InternedString::new("doc");
737     let mut result = String::new();
738
739     for attr in attrs {
740         if attr.name() == doc {
741             if let Some(ref val) = attr.value_str() {
742                 if attr.node.is_sugared_doc {
743                     result.push_str(&strip_doc_comment_decoration(val));
744                 } else {
745                     result.push_str(val);
746                 }
747                 result.push('\n');
748             }
749         }
750     }
751
752     result
753 }
754
755 #[derive(Clone, Copy, Debug, RustcEncodable)]
756 pub enum Format {
757     Csv,
758     Json,
759     JsonApi,
760 }
761
762 impl Format {
763     fn extension(&self) -> &'static str {
764         match *self {
765             Format::Csv => ".csv",
766             Format::Json | Format::JsonApi => ".json",
767         }
768     }
769 }
770
771 pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
772                                krate: &ast::Crate,
773                                analysis: &'l ty::CrateAnalysis<'l>,
774                                cratename: &str,
775                                odir: Option<&Path>,
776                                format: Format) {
777     let _ignore = tcx.dep_graph.in_ignore();
778
779     assert!(analysis.glob_map.is_some());
780
781     info!("Dumping crate {}", cratename);
782
783     // find a path to dump our data to
784     let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
785         Some(val) => PathBuf::from(val),
786         None => match odir {
787             Some(val) => val.join("save-analysis"),
788             None => PathBuf::from("save-analysis-temp"),
789         },
790     };
791
792     if let Err(e) = fs::create_dir_all(&root_path) {
793         tcx.sess.err(&format!("Could not create directory {}: {}",
794                               root_path.display(),
795                               e));
796     }
797
798     {
799         let disp = root_path.display();
800         info!("Writing output to {}", disp);
801     }
802
803     // Create output file.
804     let executable = tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
805     let mut out_name = if executable {
806         "".to_owned()
807     } else {
808         "lib".to_owned()
809     };
810     out_name.push_str(&cratename);
811     out_name.push_str(&tcx.sess.opts.cg.extra_filename);
812     out_name.push_str(format.extension());
813     root_path.push(&out_name);
814     let mut output_file = File::create(&root_path).unwrap_or_else(|e| {
815         let disp = root_path.display();
816         tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
817     });
818     root_path.pop();
819     let output = &mut output_file;
820
821     let save_ctxt = SaveContext::new(tcx);
822
823     macro_rules! dump {
824         ($new_dumper: expr) => {{
825             let mut dumper = $new_dumper;
826             let mut visitor = DumpVisitor::new(tcx, save_ctxt, analysis, &mut dumper);
827
828             visitor.dump_crate_info(cratename, krate);
829             visit::walk_crate(&mut visitor, krate);
830         }}
831     }
832
833     match format {
834         Format::Csv => dump!(CsvDumper::new(output)),
835         Format::Json => dump!(JsonDumper::new(output)),
836         Format::JsonApi => dump!(JsonApiDumper::new(output)),
837     }
838 }
839
840 // Utility functions for the module.
841
842 // Helper function to escape quotes in a string
843 fn escape(s: String) -> String {
844     s.replace("\"", "\"\"")
845 }
846
847 // Helper function to determine if a span came from a
848 // macro expansion or syntax extension.
849 pub fn generated_code(span: Span) -> bool {
850     span.expn_id != NO_EXPANSION || span == DUMMY_SP
851 }