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/")]
18 #![cfg_attr(not(stage0), deny(warnings))]
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
;
37 pub mod external_data
;
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}
;
49 use std
::fs
::{self, File}
;
50 use std
::path
::{Path, PathBuf}
;
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
;
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
;
66 // FIXME this is legacy code and should be removed
70 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
79 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
80 tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
81 span_utils
: SpanUtils
<'tcx
>,
84 macro_rules
! option_try(
85 ($e
:expr
) => (match $e { Some(e) => e, None => return None }
)
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
)
94 pub fn from_span_utils(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
95 span_utils
: SpanUtils
<'tcx
>)
96 -> SaveContext
<'l
, 'tcx
> {
99 span_utils
: span_utils
,
103 // List external crates used by the current crate.
104 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
105 let mut result
= Vec
::new();
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
,
111 debug
!("Skipping crate {}, no data", n
);
115 result
.push(CrateData
{
116 name
: (&self.tcx
.sess
.cstore
.crate_name(n
)[..]).to_owned(),
125 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
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
);
133 Some(Data
::FunctionData(FunctionData
{
135 name
: item
.ident
.to_string(),
138 span
: sub_span
.unwrap(),
139 scope
: self.enclosing_scope(item
.id
),
140 value
: make_signature(decl
, generics
),
143 ast
::ItemKind
::Static(ref typ
, mt
, ref expr
) => {
144 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
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
)
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
{
158 kind
: VariableKind
::Static
,
159 name
: item
.ident
.to_string(),
161 span
: sub_span
.unwrap(),
162 scope
: self.enclosing_scope(item
.id
),
164 type_value
: ty_to_string(&typ
),
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
{
173 kind
: VariableKind
::Const
,
174 name
: item
.ident
.to_string(),
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
),
182 ast
::ItemKind
::Mod(ref m
) => {
183 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
185 let cm
= self.tcx
.sess
.codemap();
186 let filename
= cm
.span_to_filename(m
.inner
);
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
{
192 name
: item
.ident
.to_string(),
194 span
: sub_span
.unwrap(),
195 scope
: self.enclosing_scope(item
.id
),
197 items
: m
.items
.iter().map(|i
| i
.id
).collect(),
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())
209 let val
= format
!("{}::{{{}}}", name
, variants_str
);
210 Some(Data
::EnumData(EnumData
{
214 span
: sub_span
.unwrap(),
216 scope
: self.enclosing_scope(item
.id
),
217 variants
: def
.variants
.iter().map(|v
| v
.node
.data
.id()).collect(),
220 ast
::ItemKind
::Impl(_
, _
, _
, ref trait_ref
, ref typ
, _
) => {
221 let mut type_data
= None
;
224 let parent
= self.enclosing_scope(item
.id
);
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
| {
233 span
: sub_span
.unwrap(),
236 qualname
: String
::new() // FIXME: generate the real qualname
241 // Less useful case, impl for a compound type.
243 sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
247 let trait_data
= trait_ref
.as_ref()
248 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
250 filter
!(self.span_utils
, sub_span
, typ
.span
, None
);
251 Some(Data
::ImplData(ImplData2
{
253 span
: sub_span
.unwrap(),
255 trait_ref
: trait_data
,
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
);
275 kind
: VariableKind
::Field
,
276 name
: ident
.to_string(),
278 span
: sub_span
.unwrap(),
280 value
: "".to_owned(),
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
)) => {
298 hir
::ItemImpl(_
, _
, _
, _
, ref ty
, _
) => {
299 let mut result
= String
::from("<");
300 result
.push_str(&rustc
::hir
::print
::ty_to_string(&ty
));
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
));
307 result
.push_str(">");
312 "Container {:?} for method {} not an impl?",
320 "Container {:?} for method {} is not a node item {:?}",
326 None
=> match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
328 match self.tcx
.map
.get_if_local(def_id
) {
329 Some(NodeItem(_
)) => {
330 format
!("::{}", self.tcx
.item_path_str(def_id
))
334 "Could not find container {:?} for \
335 method {}, got {:?}",
343 span_bug
!(span
, "Could not find container for method {}", id
);
348 let qualname
= format
!("{}::{}", qualname
, name
);
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
{
360 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
361 filter
!(self.span_utils
, sub_span
, span
, None
);
364 name
: name
.to_string(),
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(),
374 pub fn get_trait_ref_data(&self,
375 trait_ref
: &ast
::TraitRef
,
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
);
383 span
: sub_span
.unwrap(),
385 ref_id
: Some(def_id
),
386 qualname
: String
::new() // FIXME: generate the real qualname
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
{
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
,
402 debug
!("Missing or weird node for sub-expression {} in {:?}",
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
),
420 debug
!("Expected struct type, found {:?}", ty
);
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
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
);
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
)),
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(),
462 ast
::ExprKind
::Path(_
, ref path
) => {
463 self.get_path_data(expr
.id
, path
)
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
);
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(),
490 Def
::Struct(def_id
) |
492 Def
::TyAlias(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
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
) => {
512 .find(|mr
| mr
.name() == ti
.name() && self.trait_method_has_body(mr
))
513 .map(|mr
| mr
.def_id())
515 ty
::ImplContainer(def_id
) => {
516 let impl_items
= self.tcx
.impl_items
.borrow();
517 Some(impl_items
.get(&def_id
)
521 self.tcx
.impl_or_trait_item(mr
.def_id()).name() ==
531 Some(Data
::MethodCallData(MethodCallData
{
532 span
: sub_span
.unwrap(),
533 scope
: self.enclosing_scope(id
),
535 decl_id
: Some(decl_id
),
539 Some(Data
::FunctionCallData(FunctionCallData
{
541 span
: sub_span
.unwrap(),
542 scope
: self.enclosing_scope(id
),
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
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
{
571 pub fn get_field_ref_data(&self,
572 field_ref
: &ast
::Field
,
573 variant
: ty
::VariantDef
,
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(),
588 /// Attempt to return MacroUseData for any AST node.
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
) {
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
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
);
605 // Ignore attribute macros, their spans are usually mangled
606 if let MacroAttribute(_
) = callee
.format
{
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
{
617 name
: mac_name
.clone(),
618 callee_span
: mac_span
,
619 scope
: self.enclosing_scope(id
),
621 qualname
: String
::new()// FIXME: generate the real qualname
627 name
: callee
.name().to_string(),
628 callee_span
: callee_span
,
629 scope
: self.enclosing_scope(id
),
631 qualname
: String
::new() // FIXME: generate the real qualname
635 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
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()),
648 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
649 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(0)
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() {
657 sig
.push_str(&generics
.lifetimes
.iter()
658 .map(|l
| l
.lifetime
.name
.to_string())
661 if !generics
.lifetimes
.is_empty() {
664 sig
.push_str(&generics
.ty_params
.iter()
665 .map(|l
| l
.ident
.to_string())
671 sig
.push_str(&decl
.inputs
.iter().map(arg_to_string
).collect
::<Vec
<_
>>().join(", "));
674 ast
::FunctionRetTy
::Default(_
) => {}
675 ast
::FunctionRetTy
::Ty(ref t
) => sig
.push_str(&format
!(" -> {}", ty_to_string(t
))),
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
)>,
688 fn new() -> PathCollector
{
689 PathCollector { collected_paths: vec![] }
693 impl Visitor
for PathCollector
{
694 fn visit_pat(&mut self, p
: &ast
::Pat
) {
696 PatKind
::Struct(ref path
, _
, _
) => {
697 self.collected_paths
.push((p
.id
, path
.clone(),
698 ast
::Mutability
::Mutable
, recorder
::TypeRef
));
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
));
705 PatKind
::Ident(bm
, ref path1
, _
) => {
706 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
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
,
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
));
723 visit
::walk_pat(self, p
);
727 #[derive(Clone, Copy, Debug)]
734 fn extension(&self) -> &'
static str {
736 Format
::Csv
=> ".csv",
737 Format
::Json
=> ".json",
742 pub fn process_crate
<'l
, 'tcx
>(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
744 analysis
: &'l ty
::CrateAnalysis
<'l
>,
748 let _ignore
= tcx
.dep_graph
.in_ignore();
750 assert
!(analysis
.glob_map
.is_some());
752 info
!("Dumping crate {}", cratename
);
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
),
758 Some(val
) => val
.join("save-analysis"),
759 None
=> PathBuf
::from("save-analysis-temp"),
763 if let Err(e
) = fs
::create_dir_all(&root_path
) {
764 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
770 let disp
= root_path
.display();
771 info
!("Writing output to {}", disp
);
774 // Create output file.
775 let executable
= tcx
.sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
776 let mut out_name
= if executable
{
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
));
790 let output
= &mut output_file
;
792 let save_ctxt
= SaveContext
::new(tcx
);
795 ($new_dumper
: expr
) => {{
796 let mut dumper
= $new_dumper
;
797 let mut visitor
= DumpVisitor
::new(tcx
, save_ctxt
, analysis
, &mut dumper
);
799 visitor
.dump_crate_info(cratename
, krate
);
800 visit
::walk_crate(&mut visitor
, krate
);
805 Format
::Csv
=> dump
!(CsvDumper
::new(output
)),
806 Format
::Json
=> dump
!(JsonDumper
::new(output
)),
810 // Utility functions for the module.
812 // Helper function to escape quotes in a string
813 fn escape(s
: String
) -> String
{
814 s
.replace("\"", "\"\"")
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