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
;
39 pub mod external_data
;
44 use rustc
::hir
::def
::Def
;
45 use rustc
::hir
::map
::Node
;
46 use rustc
::hir
::def_id
::DefId
;
47 use rustc
::session
::config
::CrateType
::CrateTypeExecutable
;
48 use rustc
::ty
::{self, TyCtxt}
;
52 use std
::path
::{Path, PathBuf}
;
54 use syntax
::ast
::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}
;
55 use syntax
::parse
::lexer
::comments
::strip_doc_comment_decoration
;
56 use syntax
::parse
::token
;
57 use syntax
::symbol
::{Symbol, keywords}
;
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 tables
: &'l ty
::TypeckTables
<'tcx
>,
88 analysis
: &'l ty
::CrateAnalysis
<'tcx
>,
89 span_utils
: SpanUtils
<'tcx
>,
92 macro_rules
! option_try(
93 ($e
:expr
) => (match $e { Some(e) => e, None => return None }
)
96 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
97 // List external crates used by the current crate.
98 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
99 let mut result
= Vec
::new();
101 for n
in self.tcx
.sess
.cstore
.crates() {
102 let span
= match self.tcx
.sess
.cstore
.extern_crate(n
) {
103 Some(ref c
) => c
.span
,
105 debug
!("Skipping crate {}, no data", n
);
109 result
.push(CrateData
{
110 name
: self.tcx
.sess
.cstore
.crate_name(n
).to_string(),
119 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
121 ast
::ItemKind
::Fn(ref decl
, .., ref generics
, _
) => {
122 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
123 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
124 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
127 Some(Data
::FunctionData(FunctionData
{
129 name
: item
.ident
.to_string(),
132 span
: sub_span
.unwrap(),
133 scope
: self.enclosing_scope(item
.id
),
134 value
: make_signature(decl
, generics
),
135 visibility
: From
::from(&item
.vis
),
137 docs
: docs_for_attrs(&item
.attrs
),
138 sig
: self.sig_base(item
),
141 ast
::ItemKind
::Static(ref typ
, mt
, ref expr
) => {
142 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
144 // If the variable is immutable, save the initialising expression.
145 let (value
, keyword
) = match mt
{
146 ast
::Mutability
::Mutable
=> (String
::from("<mutable>"), keywords
::Mut
),
147 ast
::Mutability
::Immutable
=> {
148 (self.span_utils
.snippet(expr
.span
), keywords
::Static
)
152 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
153 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
154 Some(Data
::VariableData(VariableData
{
156 kind
: VariableKind
::Static
,
157 name
: item
.ident
.to_string(),
159 span
: sub_span
.unwrap(),
160 scope
: self.enclosing_scope(item
.id
),
163 type_value
: ty_to_string(&typ
),
164 visibility
: From
::from(&item
.vis
),
165 docs
: docs_for_attrs(&item
.attrs
),
166 sig
: Some(self.sig_base(item
)),
169 ast
::ItemKind
::Const(ref typ
, ref expr
) => {
170 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
171 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
172 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
173 Some(Data
::VariableData(VariableData
{
175 kind
: VariableKind
::Const
,
176 name
: item
.ident
.to_string(),
178 span
: sub_span
.unwrap(),
179 scope
: self.enclosing_scope(item
.id
),
181 value
: self.span_utils
.snippet(expr
.span
),
182 type_value
: ty_to_string(&typ
),
183 visibility
: From
::from(&item
.vis
),
184 docs
: docs_for_attrs(&item
.attrs
),
185 sig
: Some(self.sig_base(item
)),
188 ast
::ItemKind
::Mod(ref m
) => {
189 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
191 let cm
= self.tcx
.sess
.codemap();
192 let filename
= cm
.span_to_filename(m
.inner
);
194 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
195 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
197 Some(Data
::ModData(ModData
{
199 name
: item
.ident
.to_string(),
201 span
: sub_span
.unwrap(),
202 scope
: self.enclosing_scope(item
.id
),
204 items
: m
.items
.iter().map(|i
| i
.id
).collect(),
205 visibility
: From
::from(&item
.vis
),
206 docs
: docs_for_attrs(&item
.attrs
),
207 sig
: self.sig_base(item
),
210 ast
::ItemKind
::Enum(ref def
, _
) => {
211 let name
= item
.ident
.to_string();
212 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
213 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
214 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
215 let variants_str
= def
.variants
.iter()
216 .map(|v
| v
.node
.name
.to_string())
219 let val
= format
!("{}::{{{}}}", name
, variants_str
);
220 Some(Data
::EnumData(EnumData
{
224 span
: sub_span
.unwrap(),
226 scope
: self.enclosing_scope(item
.id
),
227 variants
: def
.variants
.iter().map(|v
| v
.node
.data
.id()).collect(),
228 visibility
: From
::from(&item
.vis
),
229 docs
: docs_for_attrs(&item
.attrs
),
230 sig
: self.sig_base(item
),
233 ast
::ItemKind
::Impl(.., ref trait_ref
, ref typ
, _
) => {
234 let mut type_data
= None
;
237 let parent
= self.enclosing_scope(item
.id
);
240 // Common case impl for a struct or something basic.
241 ast
::TyKind
::Path(None
, ref path
) => {
242 filter
!(self.span_utils
, None
, path
.span
, None
);
243 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
);
244 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| {
246 span
: sub_span
.unwrap(),
249 qualname
: String
::new() // FIXME: generate the real qualname
254 // Less useful case, impl for a compound type.
256 sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
260 let trait_data
= trait_ref
.as_ref()
261 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
263 filter
!(self.span_utils
, sub_span
, typ
.span
, None
);
264 Some(Data
::ImplData(ImplData2
{
266 span
: sub_span
.unwrap(),
268 trait_ref
: trait_data
,
279 pub fn get_field_data(&self,
280 field
: &ast
::StructField
,
282 -> Option
<VariableData
> {
283 if let Some(ident
) = field
.ident
{
284 let name
= ident
.to_string();
285 let qualname
= format
!("::{}::{}", self.tcx
.node_path_str(scope
), ident
);
286 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
287 filter
!(self.span_utils
, sub_span
, field
.span
, None
);
288 let def_id
= self.tcx
.hir
.local_def_id(field
.id
);
289 let typ
= self.tcx
.item_type(def_id
).to_string();
291 let span
= field
.span
;
292 let text
= self.span_utils
.snippet(field
.span
);
293 let ident_start
= text
.find(&name
).unwrap();
294 let ident_end
= ident_start
+ name
.len();
295 let sig
= Signature
{
298 ident_start
: ident_start
,
299 ident_end
: ident_end
,
305 kind
: VariableKind
::Field
,
308 span
: sub_span
.unwrap(),
310 parent
: Some(make_def_id(scope
, &self.tcx
.hir
)),
311 value
: "".to_owned(),
313 visibility
: From
::from(&field
.vis
),
314 docs
: docs_for_attrs(&field
.attrs
),
322 // FIXME would be nice to take a MethodItem here, but the ast provides both
323 // trait and impl flavours, so the caller must do the disassembly.
324 pub fn get_method_data(&self, id
: ast
::NodeId
,
325 name
: ast
::Name
, span
: Span
) -> Option
<FunctionData
> {
326 // The qualname for a method is the trait name or name of the struct in an impl in
327 // which the method is declared in, followed by the method's name.
328 let (qualname
, parent_scope
, decl_id
, vis
, docs
) =
329 match self.tcx
.impl_of_method(self.tcx
.hir
.local_def_id(id
)) {
330 Some(impl_id
) => match self.tcx
.hir
.get_if_local(impl_id
) {
331 Some(Node
::NodeItem(item
)) => {
333 hir
::ItemImpl(.., ref ty
, _
) => {
334 let mut result
= String
::from("<");
335 result
.push_str(&self.tcx
.hir
.node_to_pretty_string(ty
.id
));
337 let trait_id
= self.tcx
.trait_id_of_impl(impl_id
);
338 let mut decl_id
= None
;
339 if let Some(def_id
) = trait_id
{
340 result
.push_str(" as ");
341 result
.push_str(&self.tcx
.item_path_str(def_id
));
342 self.tcx
.associated_items(def_id
)
343 .find(|item
| item
.name
== name
)
344 .map(|item
| decl_id
= Some(item
.def_id
));
346 result
.push_str(">");
348 (result
, trait_id
, decl_id
,
349 From
::from(&item
.vis
),
350 docs_for_attrs(&item
.attrs
))
354 "Container {:?} for method {} not an impl?",
362 "Container {:?} for method {} is not a node item {:?}",
368 None
=> match self.tcx
.trait_of_item(self.tcx
.hir
.local_def_id(id
)) {
370 match self.tcx
.hir
.get_if_local(def_id
) {
371 Some(Node
::NodeItem(item
)) => {
372 (format
!("::{}", self.tcx
.item_path_str(def_id
)),
374 From
::from(&item
.vis
),
375 docs_for_attrs(&item
.attrs
))
379 "Could not find container {:?} for \
380 method {}, got {:?}",
388 span_bug
!(span
, "Could not find container for method {}", id
);
393 let qualname
= format
!("{}::{}", qualname
, name
);
395 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
396 filter
!(self.span_utils
, sub_span
, span
, None
);
398 let name
= name
.to_string();
399 let text
= self.span_utils
.signature_string_for_span(span
);
400 let ident_start
= text
.find(&name
).unwrap();
401 let ident_end
= ident_start
+ name
.len();
402 let sig
= Signature
{
405 ident_start
: ident_start
,
406 ident_end
: ident_end
,
415 declaration
: decl_id
,
416 span
: sub_span
.unwrap(),
417 scope
: self.enclosing_scope(id
),
418 // FIXME you get better data here by using the visitor.
419 value
: String
::new(),
421 parent
: parent_scope
,
427 pub fn get_trait_ref_data(&self,
428 trait_ref
: &ast
::TraitRef
,
430 -> Option
<TypeRefData
> {
431 self.lookup_ref_id(trait_ref
.ref_id
).and_then(|def_id
| {
432 let span
= trait_ref
.path
.span
;
433 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
434 filter
!(self.span_utils
, sub_span
, span
, None
);
436 span
: sub_span
.unwrap(),
438 ref_id
: Some(def_id
),
439 qualname
: String
::new() // FIXME: generate the real qualname
444 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
445 let hir_node
= self.tcx
.hir
.expect_expr(expr
.id
);
446 let ty
= self.tables
.expr_ty_adjusted_opt(&hir_node
);
447 if ty
.is_none() || ty
.unwrap().sty
== ty
::TyError
{
451 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
452 let hir_node
= match self.tcx
.hir
.find(sub_ex
.id
) {
453 Some(Node
::NodeExpr(expr
)) => expr
,
455 debug
!("Missing or weird node for sub-expression {} in {:?}",
460 match self.tables
.expr_ty_adjusted(&hir_node
).sty
{
461 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
462 let f
= def
.struct_variant().field_named(ident
.node
.name
);
463 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
464 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
465 return Some(Data
::VariableRefData(VariableRefData
{
466 name
: ident
.node
.to_string(),
467 span
: sub_span
.unwrap(),
468 scope
: self.enclosing_scope(expr
.id
),
473 debug
!("Expected struct or union type, found {:?}", ty
);
478 ast
::ExprKind
::Struct(ref path
, ..) => {
479 match self.tables
.expr_ty_adjusted(&hir_node
).sty
{
480 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
481 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
482 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
483 Some(Data
::TypeRefData(TypeRefData
{
484 span
: sub_span
.unwrap(),
485 scope
: self.enclosing_scope(expr
.id
),
486 ref_id
: Some(def
.did
),
487 qualname
: String
::new() // FIXME: generate the real qualname
491 // FIXME ty could legitimately be an enum, but then we will fail
492 // later if we try to look up the fields.
493 debug
!("expected struct or union, found {:?}", ty
);
498 ast
::ExprKind
::MethodCall(..) => {
499 let method_call
= ty
::MethodCall
::expr(expr
.id
);
500 let method_id
= self.tables
.method_map
[&method_call
].def_id
;
501 let (def_id
, decl_id
) = match self.tcx
.associated_item(method_id
).container
{
502 ty
::ImplContainer(_
) => (Some(method_id
), None
),
503 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
505 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
506 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
507 let parent
= self.enclosing_scope(expr
.id
);
508 Some(Data
::MethodCallData(MethodCallData
{
509 span
: sub_span
.unwrap(),
515 ast
::ExprKind
::Path(_
, ref path
) => {
516 self.get_path_data(expr
.id
, path
)
525 pub fn get_path_def(&self, id
: NodeId
) -> Def
{
526 match self.tcx
.hir
.get(id
) {
527 Node
::NodeTraitRef(tr
) => tr
.path
.def
,
529 Node
::NodeItem(&hir
::Item { node: hir::ItemUse(ref path, _), .. }
) |
530 Node
::NodeVisibility(&hir
::Visibility
::Restricted { ref path, .. }
) => path
.def
,
532 Node
::NodeExpr(&hir
::Expr { node: hir::ExprPath(ref qpath), .. }
) |
533 Node
::NodeExpr(&hir
::Expr { node: hir::ExprStruct(ref qpath, ..), .. }
) |
534 Node
::NodePat(&hir
::Pat { node: hir::PatKind::Path(ref qpath), .. }
) |
535 Node
::NodePat(&hir
::Pat { node: hir::PatKind::Struct(ref qpath, ..), .. }
) |
536 Node
::NodePat(&hir
::Pat { node: hir::PatKind::TupleStruct(ref qpath, ..), .. }
) => {
537 self.tables
.qpath_def(qpath
, id
)
540 Node
::NodeLocal(&hir
::Pat { node: hir::PatKind::Binding(_, def_id, ..), .. }
) => {
544 Node
::NodeTy(&hir
::Ty { node: hir::TyPath(ref qpath), .. }
) => {
546 hir
::QPath
::Resolved(_
, ref path
) => path
.def
,
547 hir
::QPath
::TypeRelative(..) => {
548 if let Some(ty
) = self.analysis
.hir_ty_to_ty
.get(&id
) {
549 if let ty
::TyProjection(proj
) = ty
.sty
{
550 for item
in self.tcx
.associated_items(proj
.trait_ref
.def_id
) {
551 if item
.kind
== ty
::AssociatedKind
::Type
{
552 if item
.name
== proj
.item_name
{
553 return Def
::AssociatedTy(item
.def_id
);
568 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Data
> {
569 let def
= self.get_path_def(id
);
570 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
571 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
577 Def
::AssociatedConst(..) |
578 Def
::StructCtor(..) |
579 Def
::VariantCtor(..) => {
580 Some(Data
::VariableRefData(VariableRefData
{
581 name
: self.span_utils
.snippet(sub_span
.unwrap()),
582 span
: sub_span
.unwrap(),
583 scope
: self.enclosing_scope(id
),
584 ref_id
: def
.def_id(),
587 Def
::Struct(def_id
) |
588 Def
::Variant(def_id
, ..) |
591 Def
::TyAlias(def_id
) |
592 Def
::AssociatedTy(def_id
) |
594 Def
::TyParam(def_id
) => {
595 Some(Data
::TypeRefData(TypeRefData
{
596 span
: sub_span
.unwrap(),
597 ref_id
: Some(def_id
),
598 scope
: self.enclosing_scope(id
),
599 qualname
: String
::new() // FIXME: generate the real qualname
602 Def
::Method(decl_id
) => {
603 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
604 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
605 let def_id
= if decl_id
.is_local() {
606 let ti
= self.tcx
.associated_item(decl_id
);
607 self.tcx
.associated_items(ti
.container
.id())
608 .find(|item
| item
.name
== ti
.name
&& item
.defaultness
.has_value())
609 .map(|item
| item
.def_id
)
613 Some(Data
::MethodCallData(MethodCallData
{
614 span
: sub_span
.unwrap(),
615 scope
: self.enclosing_scope(id
),
617 decl_id
: Some(decl_id
),
621 Some(Data
::FunctionCallData(FunctionCallData
{
623 span
: sub_span
.unwrap(),
624 scope
: self.enclosing_scope(id
),
627 Def
::Mod(def_id
) => {
628 Some(Data
::ModRefData(ModRefData
{
629 ref_id
: Some(def_id
),
630 span
: sub_span
.unwrap(),
631 scope
: self.enclosing_scope(id
),
632 qualname
: String
::new() // FIXME: generate the real qualname
643 pub fn get_field_ref_data(&self,
644 field_ref
: &ast
::Field
,
645 variant
: &ty
::VariantDef
,
647 -> Option
<VariableRefData
> {
648 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
649 // We don't really need a sub-span here, but no harm done
650 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
651 filter
!(self.span_utils
, sub_span
, field_ref
.ident
.span
, None
);
652 Some(VariableRefData
{
653 name
: field_ref
.ident
.node
.to_string(),
654 span
: sub_span
.unwrap(),
660 /// Attempt to return MacroUseData for any AST node.
662 /// For a given piece of AST defined by the supplied Span and NodeId,
663 /// returns None if the node is not macro-generated or the span is malformed,
664 /// else uses the expansion callsite and callee to return some MacroUseData.
665 pub fn get_macro_use_data(&self, span
: Span
, id
: NodeId
) -> Option
<MacroUseData
> {
666 if !generated_code(span
) {
669 // Note we take care to use the source callsite/callee, to handle
670 // nested expansions and ensure we only generate data for source-visible
672 let callsite
= self.tcx
.sess
.codemap().source_callsite(span
);
673 let callee
= self.tcx
.sess
.codemap().source_callee(span
);
674 let callee
= option_try
!(callee
);
675 let callee_span
= option_try
!(callee
.span
);
677 // Ignore attribute macros, their spans are usually mangled
678 if let MacroAttribute(_
) = callee
.format
{
682 // If the callee is an imported macro from an external crate, need to get
683 // the source span and name from the session, as their spans are localized
684 // when read in, and no longer correspond to the source.
685 if let Some(mac
) = self.tcx
.sess
.imported_macro_spans
.borrow().get(&callee_span
) {
686 let &(ref mac_name
, mac_span
) = mac
;
687 return Some(MacroUseData
{
689 name
: mac_name
.clone(),
690 callee_span
: mac_span
,
691 scope
: self.enclosing_scope(id
),
693 qualname
: String
::new()// FIXME: generate the real qualname
699 name
: callee
.name().to_string(),
700 callee_span
: callee_span
,
701 scope
: self.enclosing_scope(id
),
703 qualname
: String
::new() // FIXME: generate the real qualname
707 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
712 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
713 match self.get_path_def(ref_id
) {
714 Def
::PrimTy(_
) | Def
::SelfTy(..) | Def
::Err
=> None
,
715 def
=> Some(def
.def_id()),
719 fn sig_base(&self, item
: &ast
::Item
) -> Signature
{
720 let text
= self.span_utils
.signature_string_for_span(item
.span
);
721 let name
= item
.ident
.to_string();
722 let ident_start
= text
.find(&name
).expect("Name not in signature?");
723 let ident_end
= ident_start
+ name
.len();
725 span
: mk_sp(item
.span
.lo
, item
.span
.lo
+ BytePos(text
.len() as u32)),
727 ident_start
: ident_start
,
728 ident_end
: ident_end
,
735 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
736 self.tcx
.hir
.get_enclosing_scope(id
).unwrap_or(CRATE_NODE_ID
)
740 fn make_signature(decl
: &ast
::FnDecl
, generics
: &ast
::Generics
) -> String
{
741 let mut sig
= "fn ".to_owned();
742 if !generics
.lifetimes
.is_empty() || !generics
.ty_params
.is_empty() {
744 sig
.push_str(&generics
.lifetimes
.iter()
745 .map(|l
| l
.lifetime
.name
.to_string())
748 if !generics
.lifetimes
.is_empty() {
751 sig
.push_str(&generics
.ty_params
.iter()
752 .map(|l
| l
.ident
.to_string())
758 sig
.push_str(&decl
.inputs
.iter().map(arg_to_string
).collect
::<Vec
<_
>>().join(", "));
761 ast
::FunctionRetTy
::Default(_
) => sig
.push_str(" -> ()"),
762 ast
::FunctionRetTy
::Ty(ref t
) => sig
.push_str(&format
!(" -> {}", ty_to_string(t
))),
768 // An AST visitor for collecting paths from patterns.
769 struct PathCollector
{
770 // The Row field identifies the kind of pattern.
771 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
775 fn new() -> PathCollector
{
776 PathCollector { collected_paths: vec![] }
780 impl<'a
> Visitor
<'a
> for PathCollector
{
781 fn visit_pat(&mut self, p
: &ast
::Pat
) {
783 PatKind
::Struct(ref path
, ..) => {
784 self.collected_paths
.push((p
.id
, path
.clone(),
785 ast
::Mutability
::Mutable
, recorder
::TypeRef
));
787 PatKind
::TupleStruct(ref path
, ..) |
788 PatKind
::Path(_
, ref path
) => {
789 self.collected_paths
.push((p
.id
, path
.clone(),
790 ast
::Mutability
::Mutable
, recorder
::VarRef
));
792 PatKind
::Ident(bm
, ref path1
, _
) => {
793 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
797 let immut
= match bm
{
798 // Even if the ref is mut, you can't change the ref, only
799 // the data pointed at, so showing the initialising expression
800 // is still worthwhile.
801 ast
::BindingMode
::ByRef(_
) => ast
::Mutability
::Immutable
,
802 ast
::BindingMode
::ByValue(mt
) => mt
,
804 // collect path for either visit_local or visit_arm
805 let path
= ast
::Path
::from_ident(path1
.span
, path1
.node
);
806 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
810 visit
::walk_pat(self, p
);
814 fn docs_for_attrs(attrs
: &[Attribute
]) -> String
{
815 let doc
= Symbol
::intern("doc");
816 let mut result
= String
::new();
819 if attr
.name() == doc
{
820 if let Some(val
) = attr
.value_str() {
821 if attr
.is_sugared_doc
{
822 result
.push_str(&strip_doc_comment_decoration(&val
.as_str()));
824 result
.push_str(&val
.as_str());
834 #[derive(Clone, Copy, Debug, RustcEncodable)]
842 fn extension(&self) -> &'
static str {
844 Format
::Csv
=> ".csv",
845 Format
::Json
| Format
::JsonApi
=> ".json",
850 pub fn process_crate
<'l
, 'tcx
>(tcx
: TyCtxt
<'l
, 'tcx
, 'tcx
>,
852 analysis
: &'l ty
::CrateAnalysis
<'tcx
>,
856 let _ignore
= tcx
.dep_graph
.in_ignore();
858 assert
!(analysis
.glob_map
.is_some());
860 info
!("Dumping crate {}", cratename
);
862 // find a path to dump our data to
863 let mut root_path
= match env
::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
864 Some(val
) => PathBuf
::from(val
),
866 Some(val
) => val
.join("save-analysis"),
867 None
=> PathBuf
::from("save-analysis-temp"),
871 if let Err(e
) = rustc
::util
::fs
::create_dir_racy(&root_path
) {
872 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
878 let disp
= root_path
.display();
879 info
!("Writing output to {}", disp
);
882 // Create output file.
883 let executable
= tcx
.sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
884 let mut out_name
= if executable
{
889 out_name
.push_str(&cratename
);
890 out_name
.push_str(&tcx
.sess
.opts
.cg
.extra_filename
);
891 out_name
.push_str(format
.extension());
892 root_path
.push(&out_name
);
893 let mut output_file
= File
::create(&root_path
).unwrap_or_else(|e
| {
894 let disp
= root_path
.display();
895 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
898 let output
= &mut output_file
;
900 let save_ctxt
= SaveContext
{
902 tables
: &ty
::TypeckTables
::empty(),
904 span_utils
: SpanUtils
::new(&tcx
.sess
),
908 ($new_dumper
: expr
) => {{
909 let mut dumper
= $new_dumper
;
910 let mut visitor
= DumpVisitor
::new(save_ctxt
, &mut dumper
);
912 visitor
.dump_crate_info(cratename
, krate
);
913 visit
::walk_crate(&mut visitor
, krate
);
918 Format
::Csv
=> dump
!(CsvDumper
::new(output
)),
919 Format
::Json
=> dump
!(JsonDumper
::new(output
)),
920 Format
::JsonApi
=> dump
!(JsonApiDumper
::new(output
)),
924 // Utility functions for the module.
926 // Helper function to escape quotes in a string
927 fn escape(s
: String
) -> String
{
928 s
.replace("\"", "\"\"")
931 // Helper function to determine if a span came from a
932 // macro expansion or syntax extension.
933 pub fn generated_code(span
: Span
) -> bool
{
934 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP