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