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 #![cfg_attr(stage0, feature(dotdot_in_tuple_patterns))]
22 #![allow(unused_attributes)]
23 #![feature(rustc_private)]
24 #![feature(staged_api)]
26 #[macro_use] extern crate rustc;
28 #[macro_use] extern crate log;
29 #[macro_use] extern crate syntax;
30 extern crate serialize
as rustc_serialize
;
31 extern crate syntax_pos
;
40 pub mod external_data
;
45 use rustc
::hir
::map
::{Node, NodeItem}
;
46 use rustc
::hir
::def
::Def
;
47 use rustc
::hir
::def_id
::DefId
;
48 use rustc
::session
::config
::CrateType
::CrateTypeExecutable
;
49 use rustc
::ty
::{self, TyCtxt}
;
52 use std
::fs
::{self, File}
;
53 use std
::path
::{Path, PathBuf}
;
55 use syntax
::ast
::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}
;
56 use syntax
::parse
::lexer
::comments
::strip_doc_comment_decoration
;
57 use syntax
::parse
::token
::{self, keywords, InternedString}
;
58 use syntax
::visit
::{self, Visitor}
;
59 use syntax
::print
::pprust
::{ty_to_string, arg_to_string}
;
60 use syntax
::codemap
::MacroAttribute
;
63 pub use self::csv_dumper
::CsvDumper
;
64 pub use self::json_api_dumper
::JsonApiDumper
;
65 pub use self::json_dumper
::JsonDumper
;
66 pub use self::data
::*;
67 pub use self::external_data
::make_def_id
;
68 pub use self::dump
::Dump
;
69 pub use self::dump_visitor
::DumpVisitor
;
70 use self::span_utils
::SpanUtils
;
72 // FIXME this is legacy code and should be removed
76 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
85 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
86 tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
87 span_utils
: SpanUtils
<'tcx
>,
90 macro_rules
! option_try(
91 ($e
:expr
) => (match $e { Some(e) => e, None => return None }
)
94 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
95 pub fn new(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>) -> SaveContext
<'l
, 'tcx
> {
96 let span_utils
= SpanUtils
::new(&tcx
.sess
);
97 SaveContext
::from_span_utils(tcx
, span_utils
)
100 pub fn from_span_utils(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
101 span_utils
: SpanUtils
<'tcx
>)
102 -> SaveContext
<'l
, 'tcx
> {
105 span_utils
: span_utils
,
109 // List external crates used by the current crate.
110 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
111 let mut result
= Vec
::new();
113 for n
in self.tcx
.sess
.cstore
.crates() {
114 let span
= match self.tcx
.sess
.cstore
.extern_crate(n
) {
115 Some(ref c
) => c
.span
,
117 debug
!("Skipping crate {}, no data", n
);
121 result
.push(CrateData
{
122 name
: (&self.tcx
.sess
.cstore
.crate_name(n
)[..]).to_owned(),
131 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
133 ast
::ItemKind
::Fn(ref decl
, .., ref generics
, _
) => {
134 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
135 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
136 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
139 Some(Data
::FunctionData(FunctionData
{
141 name
: item
.ident
.to_string(),
144 span
: sub_span
.unwrap(),
145 scope
: self.enclosing_scope(item
.id
),
146 value
: make_signature(decl
, generics
),
147 visibility
: From
::from(&item
.vis
),
149 docs
: docs_for_attrs(&item
.attrs
),
152 ast
::ItemKind
::Static(ref typ
, mt
, ref expr
) => {
153 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
155 // If the variable is immutable, save the initialising expression.
156 let (value
, keyword
) = match mt
{
157 ast
::Mutability
::Mutable
=> (String
::from("<mutable>"), keywords
::Mut
),
158 ast
::Mutability
::Immutable
=> {
159 (self.span_utils
.snippet(expr
.span
), keywords
::Static
)
163 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
164 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
165 Some(Data
::VariableData(VariableData
{
167 kind
: VariableKind
::Static
,
168 name
: item
.ident
.to_string(),
170 span
: sub_span
.unwrap(),
171 scope
: self.enclosing_scope(item
.id
),
174 type_value
: ty_to_string(&typ
),
175 visibility
: From
::from(&item
.vis
),
176 docs
: docs_for_attrs(&item
.attrs
),
179 ast
::ItemKind
::Const(ref typ
, ref expr
) => {
180 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
181 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
182 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
183 Some(Data
::VariableData(VariableData
{
185 kind
: VariableKind
::Const
,
186 name
: item
.ident
.to_string(),
188 span
: sub_span
.unwrap(),
189 scope
: self.enclosing_scope(item
.id
),
191 value
: self.span_utils
.snippet(expr
.span
),
192 type_value
: ty_to_string(&typ
),
193 visibility
: From
::from(&item
.vis
),
194 docs
: docs_for_attrs(&item
.attrs
),
197 ast
::ItemKind
::Mod(ref m
) => {
198 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
200 let cm
= self.tcx
.sess
.codemap();
201 let filename
= cm
.span_to_filename(m
.inner
);
203 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
204 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
205 Some(Data
::ModData(ModData
{
207 name
: item
.ident
.to_string(),
209 span
: sub_span
.unwrap(),
210 scope
: self.enclosing_scope(item
.id
),
212 items
: m
.items
.iter().map(|i
| i
.id
).collect(),
213 visibility
: From
::from(&item
.vis
),
214 docs
: docs_for_attrs(&item
.attrs
),
217 ast
::ItemKind
::Enum(ref def
, _
) => {
218 let name
= item
.ident
.to_string();
219 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
220 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
221 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
222 let variants_str
= def
.variants
.iter()
223 .map(|v
| v
.node
.name
.to_string())
226 let val
= format
!("{}::{{{}}}", name
, variants_str
);
227 Some(Data
::EnumData(EnumData
{
231 span
: sub_span
.unwrap(),
233 scope
: self.enclosing_scope(item
.id
),
234 variants
: def
.variants
.iter().map(|v
| v
.node
.data
.id()).collect(),
235 visibility
: From
::from(&item
.vis
),
236 docs
: docs_for_attrs(&item
.attrs
),
239 ast
::ItemKind
::Impl(.., ref trait_ref
, ref typ
, _
) => {
240 let mut type_data
= None
;
243 let parent
= self.enclosing_scope(item
.id
);
246 // Common case impl for a struct or something basic.
247 ast
::TyKind
::Path(None
, ref path
) => {
248 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
);
249 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
250 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| {
252 span
: sub_span
.unwrap(),
255 qualname
: String
::new() // FIXME: generate the real qualname
260 // Less useful case, impl for a compound type.
262 sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
266 let trait_data
= trait_ref
.as_ref()
267 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
269 filter
!(self.span_utils
, sub_span
, typ
.span
, None
);
270 Some(Data
::ImplData(ImplData2
{
272 span
: sub_span
.unwrap(),
274 trait_ref
: trait_data
,
285 pub fn get_field_data(&self, field
: &ast
::StructField
,
286 scope
: NodeId
) -> Option
<VariableData
> {
287 if let Some(ident
) = field
.ident
{
288 let qualname
= format
!("::{}::{}", self.tcx
.node_path_str(scope
), ident
);
289 let typ
= self.tcx
.tables().node_types
.get(&field
.id
).unwrap().to_string();
290 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
291 filter
!(self.span_utils
, sub_span
, field
.span
, None
);
294 kind
: VariableKind
::Field
,
295 name
: ident
.to_string(),
297 span
: sub_span
.unwrap(),
299 parent
: Some(make_def_id(scope
, &self.tcx
.map
)),
300 value
: "".to_owned(),
302 visibility
: From
::from(&field
.vis
),
303 docs
: docs_for_attrs(&field
.attrs
),
310 // FIXME would be nice to take a MethodItem here, but the ast provides both
311 // trait and impl flavours, so the caller must do the disassembly.
312 pub fn get_method_data(&self, id
: ast
::NodeId
,
313 name
: ast
::Name
, span
: Span
) -> Option
<FunctionData
> {
314 // The qualname for a method is the trait name or name of the struct in an impl in
315 // which the method is declared in, followed by the method's name.
316 let (qualname
, parent_scope
, vis
, docs
) =
317 match self.tcx
.impl_of_method(self.tcx
.map
.local_def_id(id
)) {
318 Some(impl_id
) => match self.tcx
.map
.get_if_local(impl_id
) {
319 Some(NodeItem(item
)) => {
321 hir
::ItemImpl(.., ref ty
, _
) => {
322 let mut result
= String
::from("<");
323 result
.push_str(&rustc
::hir
::print
::ty_to_string(&ty
));
325 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
326 if let Some(def_id
) = trait_id
{
327 result
.push_str(" as ");
328 result
.push_str(&self.tcx
.item_path_str(def_id
));
330 result
.push_str(">");
331 (result
, trait_id
, From
::from(&item
.vis
), docs_for_attrs(&item
.attrs
))
335 "Container {:?} for method {} not an impl?",
343 "Container {:?} for method {} is not a node item {:?}",
349 None
=> match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
351 match self.tcx
.map
.get_if_local(def_id
) {
352 Some(NodeItem(item
)) => {
353 (format
!("::{}", self.tcx
.item_path_str(def_id
)),
355 From
::from(&item
.vis
),
356 docs_for_attrs(&item
.attrs
))
360 "Could not find container {:?} for \
361 method {}, got {:?}",
369 span_bug
!(span
, "Could not find container for method {}", id
);
374 let qualname
= format
!("{}::{}", qualname
, name
);
376 let def_id
= self.tcx
.map
.local_def_id(id
);
377 let decl_id
= self.tcx
.trait_item_of_item(def_id
).and_then(|new_def_id
| {
378 if new_def_id
!= def_id
{
385 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
386 filter
!(self.span_utils
, sub_span
, span
, None
);
389 name
: name
.to_string(),
391 declaration
: decl_id
,
392 span
: sub_span
.unwrap(),
393 scope
: self.enclosing_scope(id
),
394 // FIXME you get better data here by using the visitor.
395 value
: String
::new(),
397 parent
: parent_scope
,
402 pub fn get_trait_ref_data(&self,
403 trait_ref
: &ast
::TraitRef
,
405 -> Option
<TypeRefData
> {
406 self.lookup_ref_id(trait_ref
.ref_id
).and_then(|def_id
| {
407 let span
= trait_ref
.path
.span
;
408 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
409 filter
!(self.span_utils
, sub_span
, span
, None
);
411 span
: sub_span
.unwrap(),
413 ref_id
: Some(def_id
),
414 qualname
: String
::new() // FIXME: generate the real qualname
419 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
420 let hir_node
= self.tcx
.map
.expect_expr(expr
.id
);
421 let ty
= self.tcx
.tables().expr_ty_adjusted_opt(&hir_node
);
422 if ty
.is_none() || ty
.unwrap().sty
== ty
::TyError
{
426 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
427 let hir_node
= match self.tcx
.map
.find(sub_ex
.id
) {
428 Some(Node
::NodeExpr(expr
)) => expr
,
430 debug
!("Missing or weird node for sub-expression {} in {:?}",
435 match self.tcx
.tables().expr_ty_adjusted(&hir_node
).sty
{
436 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
437 let f
= def
.struct_variant().field_named(ident
.node
.name
);
438 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
439 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
440 return Some(Data
::VariableRefData(VariableRefData
{
441 name
: ident
.node
.to_string(),
442 span
: sub_span
.unwrap(),
443 scope
: self.enclosing_scope(expr
.id
),
448 debug
!("Expected struct or union type, found {:?}", ty
);
453 ast
::ExprKind
::Struct(ref path
, ..) => {
454 match self.tcx
.tables().expr_ty_adjusted(&hir_node
).sty
{
455 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
456 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
457 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
458 Some(Data
::TypeRefData(TypeRefData
{
459 span
: sub_span
.unwrap(),
460 scope
: self.enclosing_scope(expr
.id
),
461 ref_id
: Some(def
.did
),
462 qualname
: String
::new() // FIXME: generate the real qualname
466 // FIXME ty could legitimately be an enum, but then we will fail
467 // later if we try to look up the fields.
468 debug
!("expected struct or union, found {:?}", ty
);
473 ast
::ExprKind
::MethodCall(..) => {
474 let method_call
= ty
::MethodCall
::expr(expr
.id
);
475 let method_id
= self.tcx
.tables().method_map
[&method_call
].def_id
;
476 let (def_id
, decl_id
) = match self.tcx
.impl_or_trait_item(method_id
).container() {
477 ty
::ImplContainer(_
) => (Some(method_id
), None
),
478 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
480 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
481 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
482 let parent
= self.enclosing_scope(expr
.id
);
483 Some(Data
::MethodCallData(MethodCallData
{
484 span
: sub_span
.unwrap(),
490 ast
::ExprKind
::Path(_
, ref path
) => {
491 self.get_path_data(expr
.id
, path
)
500 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Data
> {
501 let def
= self.tcx
.expect_def(id
);
502 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
503 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
509 Def
::AssociatedConst(..) |
510 Def
::StructCtor(..) |
511 Def
::VariantCtor(..) => {
512 Some(Data
::VariableRefData(VariableRefData
{
513 name
: self.span_utils
.snippet(sub_span
.unwrap()),
514 span
: sub_span
.unwrap(),
515 scope
: self.enclosing_scope(id
),
516 ref_id
: def
.def_id(),
519 Def
::Struct(def_id
) |
520 Def
::Variant(def_id
, ..) |
523 Def
::TyAlias(def_id
) |
524 Def
::AssociatedTy(def_id
) |
526 Def
::TyParam(def_id
) => {
527 Some(Data
::TypeRefData(TypeRefData
{
528 span
: sub_span
.unwrap(),
529 ref_id
: Some(def_id
),
530 scope
: self.enclosing_scope(id
),
531 qualname
: String
::new() // FIXME: generate the real qualname
534 Def
::Method(decl_id
) => {
535 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
536 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
537 let def_id
= if decl_id
.is_local() {
538 let ti
= self.tcx
.impl_or_trait_item(decl_id
);
539 match ti
.container() {
540 ty
::TraitContainer(def_id
) => {
544 .find(|mr
| mr
.name() == ti
.name() && self.trait_method_has_body(mr
))
545 .map(|mr
| mr
.def_id())
547 ty
::ImplContainer(def_id
) => {
548 Some(*self.tcx
.impl_or_trait_items(def_id
).iter().find(|&&mr
| {
549 self.tcx
.impl_or_trait_item(mr
).name() == ti
.name()
556 Some(Data
::MethodCallData(MethodCallData
{
557 span
: sub_span
.unwrap(),
558 scope
: self.enclosing_scope(id
),
560 decl_id
: Some(decl_id
),
564 Some(Data
::FunctionCallData(FunctionCallData
{
566 span
: sub_span
.unwrap(),
567 scope
: self.enclosing_scope(id
),
570 Def
::Mod(def_id
) => {
571 Some(Data
::ModRefData(ModRefData
{
572 ref_id
: Some(def_id
),
573 span
: sub_span
.unwrap(),
574 scope
: self.enclosing_scope(id
),
575 qualname
: String
::new() // FIXME: generate the real qualname
585 fn trait_method_has_body(&self, mr
: &ty
::ImplOrTraitItem
) -> bool
{
586 let def_id
= mr
.def_id();
587 if let Some(node_id
) = self.tcx
.map
.as_local_node_id(def_id
) {
588 let trait_item
= self.tcx
.map
.expect_trait_item(node_id
);
589 if let hir
::TraitItem_
::MethodTraitItem(_
, Some(_
)) = trait_item
.node
{
599 pub fn get_field_ref_data(&self,
600 field_ref
: &ast
::Field
,
601 variant
: ty
::VariantDef
,
603 -> Option
<VariableRefData
> {
604 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
605 // We don't really need a sub-span here, but no harm done
606 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
607 filter
!(self.span_utils
, sub_span
, field_ref
.ident
.span
, None
);
608 Some(VariableRefData
{
609 name
: field_ref
.ident
.node
.to_string(),
610 span
: sub_span
.unwrap(),
616 /// Attempt to return MacroUseData for any AST node.
618 /// For a given piece of AST defined by the supplied Span and NodeId,
619 /// returns None if the node is not macro-generated or the span is malformed,
620 /// else uses the expansion callsite and callee to return some MacroUseData.
621 pub fn get_macro_use_data(&self, span
: Span
, id
: NodeId
) -> Option
<MacroUseData
> {
622 if !generated_code(span
) {
625 // Note we take care to use the source callsite/callee, to handle
626 // nested expansions and ensure we only generate data for source-visible
628 let callsite
= self.tcx
.sess
.codemap().source_callsite(span
);
629 let callee
= self.tcx
.sess
.codemap().source_callee(span
);
630 let callee
= option_try
!(callee
);
631 let callee_span
= option_try
!(callee
.span
);
633 // Ignore attribute macros, their spans are usually mangled
634 if let MacroAttribute(_
) = callee
.format
{
638 // If the callee is an imported macro from an external crate, need to get
639 // the source span and name from the session, as their spans are localized
640 // when read in, and no longer correspond to the source.
641 if let Some(mac
) = self.tcx
.sess
.imported_macro_spans
.borrow().get(&callee_span
) {
642 let &(ref mac_name
, mac_span
) = mac
;
643 return Some(MacroUseData
{
645 name
: mac_name
.clone(),
646 callee_span
: mac_span
,
647 scope
: self.enclosing_scope(id
),
649 qualname
: String
::new()// FIXME: generate the real qualname
655 name
: callee
.name().to_string(),
656 callee_span
: callee_span
,
657 scope
: self.enclosing_scope(id
),
659 qualname
: String
::new() // FIXME: generate the real qualname
663 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
668 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
669 match self.tcx
.expect_def(ref_id
) {
670 Def
::PrimTy(_
) | Def
::SelfTy(..) => None
,
671 def
=> Some(def
.def_id()),
676 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
677 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(CRATE_NODE_ID
)
681 fn make_signature(decl
: &ast
::FnDecl
, generics
: &ast
::Generics
) -> String
{
682 let mut sig
= "fn ".to_owned();
683 if !generics
.lifetimes
.is_empty() || !generics
.ty_params
.is_empty() {
685 sig
.push_str(&generics
.lifetimes
.iter()
686 .map(|l
| l
.lifetime
.name
.to_string())
689 if !generics
.lifetimes
.is_empty() {
692 sig
.push_str(&generics
.ty_params
.iter()
693 .map(|l
| l
.ident
.to_string())
699 sig
.push_str(&decl
.inputs
.iter().map(arg_to_string
).collect
::<Vec
<_
>>().join(", "));
702 ast
::FunctionRetTy
::Default(_
) => sig
.push_str(" -> ()"),
703 ast
::FunctionRetTy
::Ty(ref t
) => sig
.push_str(&format
!(" -> {}", ty_to_string(t
))),
709 // An AST visitor for collecting paths from patterns.
710 struct PathCollector
{
711 // The Row field identifies the kind of pattern.
712 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
716 fn new() -> PathCollector
{
717 PathCollector { collected_paths: vec![] }
721 impl Visitor
for PathCollector
{
722 fn visit_pat(&mut self, p
: &ast
::Pat
) {
724 PatKind
::Struct(ref path
, ..) => {
725 self.collected_paths
.push((p
.id
, path
.clone(),
726 ast
::Mutability
::Mutable
, recorder
::TypeRef
));
728 PatKind
::TupleStruct(ref path
, ..) |
729 PatKind
::Path(_
, ref path
) => {
730 self.collected_paths
.push((p
.id
, path
.clone(),
731 ast
::Mutability
::Mutable
, recorder
::VarRef
));
733 PatKind
::Ident(bm
, ref path1
, _
) => {
734 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
738 let immut
= match bm
{
739 // Even if the ref is mut, you can't change the ref, only
740 // the data pointed at, so showing the initialising expression
741 // is still worthwhile.
742 ast
::BindingMode
::ByRef(_
) => ast
::Mutability
::Immutable
,
743 ast
::BindingMode
::ByValue(mt
) => mt
,
745 // collect path for either visit_local or visit_arm
746 let path
= ast
::Path
::from_ident(path1
.span
, path1
.node
);
747 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
751 visit
::walk_pat(self, p
);
755 fn docs_for_attrs(attrs
: &[Attribute
]) -> String
{
756 let doc
= InternedString
::new("doc");
757 let mut result
= String
::new();
760 if attr
.name() == doc
{
761 if let Some(ref val
) = attr
.value_str() {
762 result
.push_str(&strip_doc_comment_decoration(val
));
771 #[derive(Clone, Copy, Debug, RustcEncodable)]
779 fn extension(&self) -> &'
static str {
781 Format
::Csv
=> ".csv",
782 Format
::Json
| Format
::JsonApi
=> ".json",
787 pub fn process_crate
<'l
, 'tcx
>(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
789 analysis
: &'l ty
::CrateAnalysis
<'l
>,
793 let _ignore
= tcx
.dep_graph
.in_ignore();
795 assert
!(analysis
.glob_map
.is_some());
797 info
!("Dumping crate {}", cratename
);
799 // find a path to dump our data to
800 let mut root_path
= match env
::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
801 Some(val
) => PathBuf
::from(val
),
803 Some(val
) => val
.join("save-analysis"),
804 None
=> PathBuf
::from("save-analysis-temp"),
808 if let Err(e
) = fs
::create_dir_all(&root_path
) {
809 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
815 let disp
= root_path
.display();
816 info
!("Writing output to {}", disp
);
819 // Create output file.
820 let executable
= tcx
.sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
821 let mut out_name
= if executable
{
826 out_name
.push_str(&cratename
);
827 out_name
.push_str(&tcx
.sess
.opts
.cg
.extra_filename
);
828 out_name
.push_str(format
.extension());
829 root_path
.push(&out_name
);
830 let mut output_file
= File
::create(&root_path
).unwrap_or_else(|e
| {
831 let disp
= root_path
.display();
832 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
835 let output
= &mut output_file
;
837 let save_ctxt
= SaveContext
::new(tcx
);
840 ($new_dumper
: expr
) => {{
841 let mut dumper
= $new_dumper
;
842 let mut visitor
= DumpVisitor
::new(tcx
, save_ctxt
, analysis
, &mut dumper
);
844 visitor
.dump_crate_info(cratename
, krate
);
845 visit
::walk_crate(&mut visitor
, krate
);
850 Format
::Csv
=> dump
!(CsvDumper
::new(output
)),
851 Format
::Json
=> dump
!(JsonDumper
::new(output
)),
852 Format
::JsonApi
=> dump
!(JsonApiDumper
::new(output
)),
856 // Utility functions for the module.
858 // Helper function to escape quotes in a string
859 fn escape(s
: String
) -> String
{
860 s
.replace("\"", "\"\"")
863 // Helper function to determine if a span came from a
864 // macro expansion or syntax extension.
865 pub fn generated_code(span
: Span
) -> bool
{
866 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP