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