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