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.
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.
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/")]
20 #![feature(custom_attribute)]
21 #![allow(unused_attributes)]
22 #![feature(rustc_private)]
23 #![feature(staged_api)]
25 #[macro_use] extern crate rustc;
27 #[macro_use] extern crate log;
28 #[macro_use] extern crate syntax;
29 extern crate serialize as rustc_serialize;
30 extern crate syntax_pos;
32 extern crate rls_data;
41 pub mod external_data;
46 use rustc::hir::def::Def;
47 use rustc::hir::map::Node;
48 use rustc::hir::def_id::DefId;
49 use rustc::session::config::CrateType::CrateTypeExecutable;
50 use rustc::ty::{self, TyCtxt};
54 use std::path::{Path, PathBuf};
56 use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID};
57 use syntax::parse::lexer::comments::strip_doc_comment_decoration;
58 use syntax::parse::token;
59 use syntax::symbol::{Symbol, keywords};
60 use syntax::visit::{self, Visitor};
61 use syntax::print::pprust::{ty_to_string, arg_to_string};
62 use syntax::codemap::MacroAttribute;
65 pub use self::csv_dumper::CsvDumper;
66 pub use self::json_api_dumper::JsonApiDumper;
67 pub use self::json_dumper::JsonDumper;
68 pub use self::data::*;
69 pub use self::external_data::make_def_id;
70 pub use self::dump::Dump;
71 pub use self::dump_visitor::DumpVisitor;
72 use self::span_utils::SpanUtils;
74 // FIXME this is legacy code and should be removed
78 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
87 pub struct SaveContext<'l, 'tcx: 'l> {
88 tcx: TyCtxt<'l, 'tcx, 'tcx>,
89 tables: &'l ty::TypeckTables<'tcx>,
90 analysis: &'l ty::CrateAnalysis,
91 span_utils: SpanUtils<'tcx>,
94 macro_rules! option_try(
95 ($e:expr) => (match $e { Some(e) => e, None => return None })
98 impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
99 // List external crates used by the current crate.
100 pub fn get_external_crates(&self) -> Vec<CrateData> {
101 let mut result = Vec::new();
103 for n in self.tcx.sess.cstore.crates() {
104 let span = match self.tcx.sess.cstore.extern_crate(n) {
105 Some(ref c) => c.span,
107 debug!("Skipping crate {}, no data", n);
111 result.push(CrateData {
112 name: self.tcx.sess.cstore.crate_name(n).to_string(),
121 pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
123 ast::ItemKind::Fn(ref decl, .., ref generics, _) => {
124 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
125 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
126 filter!(self.span_utils, sub_span, item.span, None);
129 Some(Data::FunctionData(FunctionData {
131 name: item.ident.to_string(),
134 span: sub_span.unwrap(),
135 scope: self.enclosing_scope(item.id),
136 value: make_signature(decl, generics),
137 visibility: From::from(&item.vis),
139 docs: docs_for_attrs(&item.attrs),
140 sig: self.sig_base(item),
141 attributes: item.attrs.clone(),
144 ast::ItemKind::Static(ref typ, mt, ref expr) => {
145 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
147 // If the variable is immutable, save the initialising expression.
148 let (value, keyword) = match mt {
149 ast::Mutability::Mutable => (String::from("<mutable>"), keywords::Mut),
150 ast::Mutability::Immutable => {
151 (self.span_utils.snippet(expr.span), keywords::Static)
155 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
156 filter!(self.span_utils, sub_span, item.span, None);
157 Some(Data::VariableData(VariableData {
159 kind: VariableKind::Static,
160 name: item.ident.to_string(),
162 span: sub_span.unwrap(),
163 scope: self.enclosing_scope(item.id),
166 type_value: ty_to_string(&typ),
167 visibility: From::from(&item.vis),
168 docs: docs_for_attrs(&item.attrs),
169 sig: Some(self.sig_base(item)),
170 attributes: item.attrs.clone(),
173 ast::ItemKind::Const(ref typ, ref expr) => {
174 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
175 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const);
176 filter!(self.span_utils, sub_span, item.span, None);
177 Some(Data::VariableData(VariableData {
179 kind: VariableKind::Const,
180 name: item.ident.to_string(),
182 span: sub_span.unwrap(),
183 scope: self.enclosing_scope(item.id),
185 value: self.span_utils.snippet(expr.span),
186 type_value: ty_to_string(&typ),
187 visibility: From::from(&item.vis),
188 docs: docs_for_attrs(&item.attrs),
189 sig: Some(self.sig_base(item)),
190 attributes: item.attrs.clone(),
193 ast::ItemKind::Mod(ref m) => {
194 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
196 let cm = self.tcx.sess.codemap();
197 let filename = cm.span_to_filename(m.inner);
199 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod);
200 filter!(self.span_utils, sub_span, item.span, None);
202 Some(Data::ModData(ModData {
204 name: item.ident.to_string(),
206 span: sub_span.unwrap(),
207 scope: self.enclosing_scope(item.id),
209 items: m.items.iter().map(|i| i.id).collect(),
210 visibility: From::from(&item.vis),
211 docs: docs_for_attrs(&item.attrs),
212 sig: self.sig_base(item),
213 attributes: item.attrs.clone(),
216 ast::ItemKind::Enum(ref def, _) => {
217 let name = item.ident.to_string();
218 let qualname = format!("::{}", self.tcx.node_path_str(item.id));
219 let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum);
220 filter!(self.span_utils, sub_span, item.span, None);
221 let variants_str = def.variants.iter()
222 .map(|v| v.node.name.to_string())
225 let val = format!("{}::{{{}}}", name, variants_str);
226 Some(Data::EnumData(EnumData {
230 span: sub_span.unwrap(),
232 scope: self.enclosing_scope(item.id),
233 variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
234 visibility: From::from(&item.vis),
235 docs: docs_for_attrs(&item.attrs),
236 sig: self.sig_base(item),
237 attributes: item.attrs.clone(),
240 ast::ItemKind::Impl(.., ref trait_ref, ref typ, _) => {
241 let mut type_data = None;
244 let parent = self.enclosing_scope(item.id);
247 // Common case impl for a struct or something basic.
248 ast::TyKind::Path(None, ref path) => {
249 if generated_code(path.span) {
252 sub_span = self.span_utils.sub_span_for_type_name(path.span);
253 type_data = self.lookup_ref_id(typ.id).map(|id| {
255 span: sub_span.unwrap(),
258 qualname: String::new() // FIXME: generate the real qualname
263 // Less useful case, impl for a compound type.
265 sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
269 let trait_data = trait_ref.as_ref()
270 .and_then(|tr| self.get_trait_ref_data(tr, parent));
272 filter!(self.span_utils, sub_span, typ.span, None);
273 Some(Data::ImplData(ImplData2 {
275 span: sub_span.unwrap(),
277 trait_ref: trait_data,
288 pub fn get_field_data(&self,
289 field: &ast::StructField,
291 -> Option<VariableData> {
292 if let Some(ident) = field.ident {
293 let name = ident.to_string();
294 let qualname = format!("::{}::{}", self.tcx.node_path_str(scope), ident);
295 let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon);
296 filter!(self.span_utils, sub_span, field.span, None);
297 let def_id = self.tcx.hir.local_def_id(field.id);
298 let typ = self.tcx.item_type(def_id).to_string();
300 let span = field.span;
301 let text = self.span_utils.snippet(field.span);
302 let ident_start = text.find(&name).unwrap();
303 let ident_end = ident_start + name.len();
304 let sig = Signature {
307 ident_start: ident_start,
308 ident_end: ident_end,
314 kind: VariableKind::Field,
317 span: sub_span.unwrap(),
319 parent: Some(make_def_id(scope, &self.tcx.hir)),
320 value: "".to_owned(),
322 visibility: From::from(&field.vis),
323 docs: docs_for_attrs(&field.attrs),
325 attributes: field.attrs.clone(),
332 // FIXME would be nice to take a MethodItem here, but the ast provides both
333 // trait and impl flavours, so the caller must do the disassembly.
334 pub fn get_method_data(&self, id: ast::NodeId,
335 name: ast::Name, span: Span) -> Option<FunctionData> {
336 // The qualname for a method is the trait name or name of the struct in an impl in
337 // which the method is declared in, followed by the method's name.
338 let (qualname, parent_scope, decl_id, vis, docs, attributes) =
339 match self.tcx.impl_of_method(self.tcx.hir.local_def_id(id)) {
340 Some(impl_id) => match self.tcx.hir.get_if_local(impl_id) {
341 Some(Node::NodeItem(item)) => {
343 hir::ItemImpl(.., ref ty, _) => {
344 let mut result = String::from("<");
345 result.push_str(&self.tcx.hir.node_to_pretty_string(ty.id));
347 let trait_id = self.tcx.trait_id_of_impl(impl_id);
348 let mut decl_id = None;
349 if let Some(def_id) = trait_id {
350 result.push_str(" as ");
351 result.push_str(&self.tcx.item_path_str(def_id));
352 self.tcx.associated_items(def_id)
353 .find(|item| item.name == name)
354 .map(|item| decl_id = Some(item.def_id));
356 result.push_str(">");
358 (result, trait_id, decl_id,
359 From::from(&item.vis),
360 docs_for_attrs(&item.attrs),
365 "Container {:?} for method {} not an impl?",
373 "Container {:?} for method {} is not a node item {:?}",
379 None => match self.tcx.trait_of_item(self.tcx.hir.local_def_id(id)) {
381 match self.tcx.hir.get_if_local(def_id) {
382 Some(Node::NodeItem(item)) => {
383 (format!("::{}", self.tcx.item_path_str(def_id)),
385 From::from(&item.vis),
386 docs_for_attrs(&item.attrs),
391 "Could not find container {:?} for \
392 method {}, got {:?}",
400 debug!("Could not find container for method {} at {:?}", id, span);
401 // This is not necessarily a bug, if there was a compilation error, the tables
402 // we need might not exist.
408 let qualname = format!("{}::{}", qualname, name);
410 let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
411 filter!(self.span_utils, sub_span, span, None);
413 let name = name.to_string();
414 let text = self.span_utils.signature_string_for_span(span);
415 let ident_start = text.find(&name).unwrap();
416 let ident_end = ident_start + name.len();
417 let sig = Signature {
420 ident_start: ident_start,
421 ident_end: ident_end,
430 declaration: decl_id,
431 span: sub_span.unwrap(),
432 scope: self.enclosing_scope(id),
433 // FIXME you get better data here by using the visitor.
434 value: String::new(),
436 parent: parent_scope,
439 attributes: attributes,
443 pub fn get_trait_ref_data(&self,
444 trait_ref: &ast::TraitRef,
446 -> Option<TypeRefData> {
447 self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
448 let span = trait_ref.path.span;
449 if generated_code(span) {
452 let sub_span = self.span_utils.sub_span_for_type_name(span).or(Some(span));
453 filter!(self.span_utils, sub_span, span, None);
455 span: sub_span.unwrap(),
457 ref_id: Some(def_id),
458 qualname: String::new() // FIXME: generate the real qualname
463 pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
464 let hir_node = self.tcx.hir.expect_expr(expr.id);
465 let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
466 if ty.is_none() || ty.unwrap().sty == ty::TyError {
470 ast::ExprKind::Field(ref sub_ex, ident) => {
471 let hir_node = match self.tcx.hir.find(sub_ex.id) {
472 Some(Node::NodeExpr(expr)) => expr,
474 debug!("Missing or weird node for sub-expression {} in {:?}",
479 match self.tables.expr_ty_adjusted(&hir_node).sty {
480 ty::TyAdt(def, _) if !def.is_enum() => {
481 let f = def.struct_variant().field_named(ident.node.name);
482 let sub_span = self.span_utils.span_for_last_ident(expr.span);
483 filter!(self.span_utils, sub_span, expr.span, None);
484 return Some(Data::VariableRefData(VariableRefData {
485 name: ident.node.to_string(),
486 span: sub_span.unwrap(),
487 scope: self.enclosing_scope(expr.id),
492 debug!("Expected struct or union type, found {:?}", ty);
497 ast::ExprKind::Struct(ref path, ..) => {
498 match self.tables.expr_ty_adjusted(&hir_node).sty {
499 ty::TyAdt(def, _) if !def.is_enum() => {
500 let sub_span = self.span_utils.span_for_last_ident(path.span);
501 filter!(self.span_utils, sub_span, path.span, None);
502 Some(Data::TypeRefData(TypeRefData {
503 span: sub_span.unwrap(),
504 scope: self.enclosing_scope(expr.id),
505 ref_id: Some(def.did),
506 qualname: String::new() // FIXME: generate the real qualname
510 // FIXME ty could legitimately be an enum, but then we will fail
511 // later if we try to look up the fields.
512 debug!("expected struct or union, found {:?}", ty);
517 ast::ExprKind::MethodCall(..) => {
518 let method_call = ty::MethodCall::expr(expr.id);
519 let method_id = self.tables.method_map[&method_call].def_id;
520 let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
521 ty::ImplContainer(_) => (Some(method_id), None),
522 ty::TraitContainer(_) => (None, Some(method_id)),
524 let sub_span = self.span_utils.sub_span_for_meth_name(expr.span);
525 filter!(self.span_utils, sub_span, expr.span, None);
526 let parent = self.enclosing_scope(expr.id);
527 Some(Data::MethodCallData(MethodCallData {
528 span: sub_span.unwrap(),
534 ast::ExprKind::Path(_, ref path) => {
535 self.get_path_data(expr.id, path)
544 pub fn get_path_def(&self, id: NodeId) -> Def {
545 match self.tcx.hir.get(id) {
546 Node::NodeTraitRef(tr) => tr.path.def,
548 Node::NodeItem(&hir::Item { node: hir::ItemUse(ref path, _), .. }) |
549 Node::NodeVisibility(&hir::Visibility::Restricted { ref path, .. }) => path.def,
551 Node::NodeExpr(&hir::Expr { node: hir::ExprPath(ref qpath), .. }) |
552 Node::NodeExpr(&hir::Expr { node: hir::ExprStruct(ref qpath, ..), .. }) |
553 Node::NodePat(&hir::Pat { node: hir::PatKind::Path(ref qpath), .. }) |
554 Node::NodePat(&hir::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }) |
555 Node::NodePat(&hir::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }) => {
556 self.tables.qpath_def(qpath, id)
559 Node::NodeLocal(&hir::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }) => {
563 Node::NodeTy(&hir::Ty { node: hir::TyPath(ref qpath), .. }) => {
565 hir::QPath::Resolved(_, ref path) => path.def,
566 hir::QPath::TypeRelative(..) => {
567 if let Some(ty) = self.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
568 if let ty::TyProjection(proj) = ty.sty {
569 for item in self.tcx.associated_items(proj.trait_ref.def_id) {
570 if item.kind == ty::AssociatedKind::Type {
571 if item.name == proj.item_name {
572 return Def::AssociatedTy(item.def_id);
587 pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Data> {
588 let def = self.get_path_def(id);
589 let sub_span = self.span_utils.span_for_last_ident(path.span);
590 filter!(self.span_utils, sub_span, path.span, None);
596 Def::AssociatedConst(..) |
597 Def::StructCtor(..) |
598 Def::VariantCtor(..) => {
599 Some(Data::VariableRefData(VariableRefData {
600 name: self.span_utils.snippet(sub_span.unwrap()),
601 span: sub_span.unwrap(),
602 scope: self.enclosing_scope(id),
603 ref_id: def.def_id(),
606 Def::Struct(def_id) |
607 Def::Variant(def_id, ..) |
610 Def::TyAlias(def_id) |
611 Def::AssociatedTy(def_id) |
613 Def::TyParam(def_id) => {
614 Some(Data::TypeRefData(TypeRefData {
615 span: sub_span.unwrap(),
616 ref_id: Some(def_id),
617 scope: self.enclosing_scope(id),
618 qualname: String::new() // FIXME: generate the real qualname
621 Def::Method(decl_id) => {
622 let sub_span = self.span_utils.sub_span_for_meth_name(path.span);
623 filter!(self.span_utils, sub_span, path.span, None);
624 let def_id = if decl_id.is_local() {
625 let ti = self.tcx.associated_item(decl_id);
626 self.tcx.associated_items(ti.container.id())
627 .find(|item| item.name == ti.name && item.defaultness.has_value())
628 .map(|item| item.def_id)
632 Some(Data::MethodCallData(MethodCallData {
633 span: sub_span.unwrap(),
634 scope: self.enclosing_scope(id),
636 decl_id: Some(decl_id),
640 Some(Data::FunctionCallData(FunctionCallData {
642 span: sub_span.unwrap(),
643 scope: self.enclosing_scope(id),
646 Def::Mod(def_id) => {
647 Some(Data::ModRefData(ModRefData {
648 ref_id: Some(def_id),
649 span: sub_span.unwrap(),
650 scope: self.enclosing_scope(id),
651 qualname: String::new() // FIXME: generate the real qualname
662 pub fn get_field_ref_data(&self,
663 field_ref: &ast::Field,
664 variant: &ty::VariantDef,
666 -> Option<VariableRefData> {
667 let f = variant.field_named(field_ref.ident.node.name);
668 // We don't really need a sub-span here, but no harm done
669 let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
670 filter!(self.span_utils, sub_span, field_ref.ident.span, None);
671 Some(VariableRefData {
672 name: field_ref.ident.node.to_string(),
673 span: sub_span.unwrap(),
679 /// Attempt to return MacroUseData for any AST node.
681 /// For a given piece of AST defined by the supplied Span and NodeId,
682 /// returns None if the node is not macro-generated or the span is malformed,
683 /// else uses the expansion callsite and callee to return some MacroUseData.
684 pub fn get_macro_use_data(&self, span: Span, id: NodeId) -> Option<MacroUseData> {
685 if !generated_code(span) {
688 // Note we take care to use the source callsite/callee, to handle
689 // nested expansions and ensure we only generate data for source-visible
691 let callsite = self.tcx.sess.codemap().source_callsite(span);
692 let callee = self.tcx.sess.codemap().source_callee(span);
693 let callee = option_try!(callee);
694 let callee_span = option_try!(callee.span);
696 // Ignore attribute macros, their spans are usually mangled
697 if let MacroAttribute(_) = callee.format {
701 // If the callee is an imported macro from an external crate, need to get
702 // the source span and name from the session, as their spans are localized
703 // when read in, and no longer correspond to the source.
704 if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee_span) {
705 let &(ref mac_name, mac_span) = mac;
706 return Some(MacroUseData {
708 name: mac_name.clone(),
709 callee_span: mac_span,
710 scope: self.enclosing_scope(id),
712 qualname: String::new()// FIXME: generate the real qualname
718 name: callee.name().to_string(),
719 callee_span: callee_span,
720 scope: self.enclosing_scope(id),
722 qualname: String::new() // FIXME: generate the real qualname
726 pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
731 fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
732 match self.get_path_def(ref_id) {
733 Def::PrimTy(_) | Def::SelfTy(..) | Def::Err => None,
734 def => Some(def.def_id()),
738 fn sig_base(&self, item: &ast::Item) -> Signature {
739 let text = self.span_utils.signature_string_for_span(item.span);
740 let name = item.ident.to_string();
741 let ident_start = text.find(&name).expect("Name not in signature?");
742 let ident_end = ident_start + name.len();
744 span: mk_sp(item.span.lo, item.span.lo + BytePos(text.len() as u32)),
746 ident_start: ident_start,
747 ident_end: ident_end,
754 pub fn enclosing_scope(&self, id: NodeId) -> NodeId {
755 self.tcx.hir.get_enclosing_scope(id).unwrap_or(CRATE_NODE_ID)
759 fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
760 let mut sig = "fn ".to_owned();
761 if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
763 sig.push_str(&generics.lifetimes.iter()
764 .map(|l| l.lifetime.name.to_string())
767 if !generics.lifetimes.is_empty() {
770 sig.push_str(&generics.ty_params.iter()
771 .map(|l| l.ident.to_string())
777 sig.push_str(&decl.inputs.iter().map(arg_to_string).collect::<Vec<_>>().join(", "));
780 ast::FunctionRetTy::Default(_) => sig.push_str(" -> ()"),
781 ast::FunctionRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
787 // An AST visitor for collecting paths from patterns.
788 struct PathCollector {
789 // The Row field identifies the kind of pattern.
790 collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
794 fn new() -> PathCollector {
795 PathCollector { collected_paths: vec![] }
799 impl<'a> Visitor<'a> for PathCollector {
800 fn visit_pat(&mut self, p: &ast::Pat) {
802 PatKind::Struct(ref path, ..) => {
803 self.collected_paths.push((p.id, path.clone(),
804 ast::Mutability::Mutable, recorder::TypeRef));
806 PatKind::TupleStruct(ref path, ..) |
807 PatKind::Path(_, ref path) => {
808 self.collected_paths.push((p.id, path.clone(),
809 ast::Mutability::Mutable, recorder::VarRef));
811 PatKind::Ident(bm, ref path1, _) => {
812 debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
816 let immut = match bm {
817 // Even if the ref is mut, you can't change the ref, only
818 // the data pointed at, so showing the initialising expression
819 // is still worthwhile.
820 ast::BindingMode::ByRef(_) => ast::Mutability::Immutable,
821 ast::BindingMode::ByValue(mt) => mt,
823 // collect path for either visit_local or visit_arm
824 let path = ast::Path::from_ident(path1.span, path1.node);
825 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
829 visit::walk_pat(self, p);
833 fn docs_for_attrs(attrs: &[Attribute]) -> String {
834 let doc = Symbol::intern("doc");
835 let mut result = String::new();
838 if attr.name() == doc {
839 if let Some(val) = attr.value_str() {
840 if attr.is_sugared_doc {
841 result.push_str(&strip_doc_comment_decoration(&val.as_str()));
843 result.push_str(&val.as_str());
853 #[derive(Clone, Copy, Debug, RustcEncodable)]
861 fn extension(&self) -> &'static str {
863 Format::Csv => ".csv",
864 Format::Json | Format::JsonApi => ".json",
869 pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
871 analysis: &'l ty::CrateAnalysis,
875 let _ignore = tcx.dep_graph.in_ignore();
877 assert!(analysis.glob_map.is_some());
879 info!("Dumping crate {}", cratename);
881 // find a path to dump our data to
882 let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
883 Some(val) => PathBuf::from(val),
885 Some(val) => val.join("save-analysis"),
886 None => PathBuf::from("save-analysis-temp"),
890 if let Err(e) = rustc::util::fs::create_dir_racy(&root_path) {
891 tcx.sess.err(&format!("Could not create directory {}: {}",
897 let disp = root_path.display();
898 info!("Writing output to {}", disp);
901 // Create output file.
902 let executable = tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
903 let mut out_name = if executable {
908 out_name.push_str(&cratename);
909 out_name.push_str(&tcx.sess.opts.cg.extra_filename);
910 out_name.push_str(format.extension());
911 root_path.push(&out_name);
912 let mut output_file = File::create(&root_path).unwrap_or_else(|e| {
913 let disp = root_path.display();
914 tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
917 let output = &mut output_file;
919 let save_ctxt = SaveContext {
921 tables: &ty::TypeckTables::empty(),
923 span_utils: SpanUtils::new(&tcx.sess),
927 ($new_dumper: expr) => {{
928 let mut dumper = $new_dumper;
929 let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
931 visitor.dump_crate_info(cratename, krate);
932 visit::walk_crate(&mut visitor, krate);
937 Format::Csv => dump!(CsvDumper::new(output)),
938 Format::Json => dump!(JsonDumper::new(output)),
939 Format::JsonApi => dump!(JsonApiDumper::new(output)),
943 // Utility functions for the module.
945 // Helper function to escape quotes in a string
946 fn escape(s: String) -> String {
947 s.replace("\"", "\"\"")
950 // Helper function to determine if a span came from a
951 // macro expansion or syntax extension.
952 pub fn generated_code(span: Span) -> bool {
953 span.expn_id != NO_EXPANSION || span == DUMMY_SP