]> git.lizzy.rs Git - rust.git/blob - src/librustc_save_analysis/lib.rs
Rollup merge of #40842 - Mic92:RawFd, r=aturon
[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 = span.source_callsite();
694         let callee = option_try!(span.source_callee());
695         let callee_span = option_try!(callee.span);
696
697         // Ignore attribute macros, their spans are usually mangled
698         if let MacroAttribute(_) = callee.format {
699             return None;
700         }
701
702         // If the callee is an imported macro from an external crate, need to get
703         // the source span and name from the session, as their spans are localized
704         // when read in, and no longer correspond to the source.
705         if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
706             let &(ref mac_name, mac_span) = mac;
707             return Some(MacroUseData {
708                                         span: callsite,
709                                         name: mac_name.clone(),
710                                         callee_span: mac_span,
711                                         scope: self.enclosing_scope(id),
712                                         imported: true,
713                                         qualname: String::new()// FIXME: generate the real qualname
714                                     });
715         }
716
717         Some(MacroUseData {
718             span: callsite,
719             name: callee.name().to_string(),
720             callee_span: callee_span,
721             scope: self.enclosing_scope(id),
722             imported: false,
723             qualname: String::new() // FIXME: generate the real qualname
724         })
725     }
726
727     pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
728         // FIXME
729         bug!();
730     }
731
732     fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
733         match self.get_path_def(ref_id) {
734             Def::PrimTy(_) | Def::SelfTy(..) | Def::Err => None,
735             def => Some(def.def_id()),
736         }
737     }
738
739     fn sig_base(&self, item: &ast::Item) -> Signature {
740         let text = self.span_utils.signature_string_for_span(item.span);
741         let name = item.ident.to_string();
742         let ident_start = text.find(&name).expect("Name not in signature?");
743         let ident_end = ident_start + name.len();
744         Signature {
745             span: Span { hi: item.span.lo + BytePos(text.len() as u32), ..item.span },
746             text: text,
747             ident_start: ident_start,
748             ident_end: ident_end,
749             defs: vec![],
750             refs: vec![],
751         }
752     }
753
754     #[inline]
755     pub fn enclosing_scope(&self, id: NodeId) -> NodeId {
756         self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID)
757     }
758 }
759
760 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
761     let mut sig = "fn ".to_owned();
762     if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
763         sig.push('<');
764         sig.push_str(&generics.lifetimes.iter()
765                               .map(|l| l.lifetime.name.to_string())
766                               .collect::<Vec<_>>()
767                               .join(", "));
768         if !generics.lifetimes.is_empty() {
769             sig.push_str(", ");
770         }
771         sig.push_str(&generics.ty_params.iter()
772                               .map(|l| l.ident.to_string())
773                               .collect::<Vec<_>>()
774                               .join(", "));
775         sig.push_str("> ");
776     }
777     sig.push('(');
778     sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
779     sig.push(')');
780     match decl.output {
781         ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
782         ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
783     }
784
785     sig
786 }
787
788 // An AST visitor for collecting paths from patterns.
789 struct PathCollector {
790     // The Row field identifies the kind of pattern.
791     collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
792 }
793
794 impl PathCollector {
795     fn new() -> PathCollector {
796         PathCollector { collected_paths: vec![] }
797     }
798 }
799
800 impl<'a> Visitor<'a> for PathCollector {
801     fn visit_pat(&mut self, p: &ast::Pat) {
802         match p.node {
803             PatKind::Struct(ref path, ..) => {
804                 self.collected_paths.push((p.id, path.clone(),
805                                            ast::Mutability::Mutable, recorder::TypeRef));
806             }
807             PatKind::TupleStruct(ref path, ..) |
808             PatKind::Path(_, ref path) => {
809                 self.collected_paths.push((p.id, path.clone(),
810                                            ast::Mutability::Mutable, recorder::VarRef));
811             }
812             PatKind::Ident(bm, ref path1, _) => {
813                 debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
814                        path1.node,
815                        p.span,
816                        path1.span);
817                 let immut = match bm {
818                     // Even if the ref is mut, you can't change the ref, only
819                     // the data pointed at, so showing the initialising expression
820                     // is still worthwhile.
821                     ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
822                     ast::BindingMode::ByValue(mt) => mt,
823                 };
824                 // collect path for either visit_local or visit_arm
825                 let path = ast::Path::from_ident(path1.span, path1.node);
826                 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
827             }
828             _ => {}
829         }
830         visit::walk_pat(self, p);
831     }
832 }
833
834 fn docs_for_attrs(attrs: &[Attribute]) -> String {
835     let mut result = String::new();
836
837     for attr in attrs {
838         if attr.check_name("doc") {
839             if let Some(val) = attr.value_str() {
840                 if attr.is_sugared_doc {
841                     result.push_str(&strip_doc_comment_decoration(&val.as_str()));
842                 } else {
843                     result.push_str(&val.as_str());
844                 }
845                 result.push('\n');
846             }
847         }
848     }
849
850     result
851 }
852
853 #[derive(Clone, Copy, Debug, RustcEncodable)]
854 pub enum Format {
855     Csv,
856     Json,
857     JsonApi,
858 }
859
860 impl Format {
861     fn extension(&self) -> &'static str {
862         match *self {
863             Format::Csv => ".csv",
864             Format::Json | Format::JsonApi => ".json",
865         }
866     }
867 }
868
869 /// Defines what to do with the results of saving the analysis.
870 pub trait SaveHandler {
871     fn save<'l, 'tcx>(&mut self,
872                       save_ctxt: SaveContext<'l, 'tcx>,
873                       krate: &ast::Crate,
874                       cratename: &str);
875 }
876
877 /// Dump the save-analysis results to a file.
878 pub struct DumpHandler<'a> {
879     format: Format,
880     odir: Option<&'a Path>,
881     cratename: String
882 }
883
884 impl<'a> DumpHandler<'a> {
885     pub fn new(format: Format, odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
886         DumpHandler {
887             format: format,
888             odir: odir,
889             cratename: cratename.to_owned()
890         }
891     }
892
893     fn output_file(&self, sess: &Session) -> File {
894         let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
895             Some(val) => PathBuf::from(val),
896             None => match self.odir {
897                 Some(val) => val.join("save-analysis"),
898                 None => PathBuf::from("save-analysis-temp"),
899             },
900         };
901
902         if let Err(e) = std::fs::create_dir_all(&root_path) {
903             error!("Could not create directory {}: {}", root_path.display(), e);
904         }
905
906         {
907             let disp = root_path.display();
908             info!("Writing output to {}", disp);
909         }
910
911         let executable = sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
912         let mut out_name = if executable {
913             "".to_owned()
914         } else {
915             "lib".to_owned()
916         };
917         out_name.push_str(&self.cratename);
918         out_name.push_str(&sess.opts.cg.extra_filename);
919         out_name.push_str(self.format.extension());
920         root_path.push(&out_name);
921         let output_file = File::create(&root_path).unwrap_or_else(|e| {
922             let disp = root_path.display();
923             sess.fatal(&format!("Could not open {}: {}", disp, e));
924         });
925         root_path.pop();
926         output_file
927     }
928 }
929
930 impl<'a> SaveHandler for DumpHandler<'a> {
931     fn save<'l, 'tcx>(&mut self,
932                       save_ctxt: SaveContext<'l, 'tcx>,
933                       krate: &ast::Crate,
934                       cratename: &str) {
935         macro_rules! dump {
936             ($new_dumper: expr) => {{
937                 let mut dumper = $new_dumper;
938                 let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
939
940                 visitor.dump_crate_info(cratename, krate);
941                 visit::walk_crate(&mut visitor, krate);
942             }}
943         }
944
945         let output = &mut self.output_file(&save_ctxt.tcx.sess);
946
947         match self.format {
948             Format::Csv => dump!(CsvDumper::new(output)),
949             Format::Json => dump!(JsonDumper::new(output)),
950             Format::JsonApi => dump!(JsonApiDumper::new(output)),
951         }
952     }
953 }
954
955 /// Call a callback with the results of save-analysis.
956 pub struct CallbackHandler<'b> {
957     pub callback: &'b mut FnMut(&rls_data::Analysis),
958 }
959
960 impl<'b> SaveHandler for CallbackHandler<'b> {
961     fn save<'l, 'tcx>(&mut self,
962                       save_ctxt: SaveContext<'l, 'tcx>,
963                       krate: &ast::Crate,
964                       cratename: &str) {
965         macro_rules! dump {
966             ($new_dumper: expr) => {{
967                 let mut dumper = $new_dumper;
968                 let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
969
970                 visitor.dump_crate_info(cratename, krate);
971                 visit::walk_crate(&mut visitor, krate);
972             }}
973         }
974
975         // We're using the JsonDumper here because it has the format of the
976         // save-analysis results that we will pass to the callback. IOW, we are
977         // using the JsonDumper to collect the save-analysis results, but not
978         // actually to dump them to a file. This is all a bit convoluted and
979         // there is certainly a simpler design here trying to get out (FIXME).
980         dump!(JsonDumper::with_callback(self.callback))
981     }
982 }
983
984 pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
985                                                krate: &ast::Crate,
986                                                analysis: &'l ty::CrateAnalysis,
987                                                cratename: &str,
988                                                mut handler: H) {
989     let _ignore = tcx.dep_graph.in_ignore();
990
991     assert!(analysis.glob_map.is_some());
992
993     info!("Dumping crate {}", cratename);
994
995     let save_ctxt = SaveContext {
996         tcx: tcx,
997         tables: &ty::TypeckTables::empty(),
998         analysis: analysis,
999         span_utils: SpanUtils::new(&tcx.sess),
1000     };
1001
1002     handler.save(save_ctxt, krate, cratename)
1003 }
1004
1005 // Utility functions for the module.
1006
1007 // Helper function to escape quotes in a string
1008 fn escape(s: String) -> String {
1009     s.replace("\"", "\"\"")
1010 }
1011
1012 // Helper function to determine if a span came from a
1013 // macro expansion or syntax extension.
1014 pub fn generated_code(span: Span) -> bool {
1015     span.ctxt != NO_EXPANSION || span == DUMMY_SP
1016 }