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