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.
13 use middle
::def_id
::DefId
;
16 use std
::fs
::{self, File}
;
17 use std
::path
::{Path, PathBuf}
;
20 use rustc
::front
::map
::NodeItem
;
21 use rustc_front
::{hir, lowering}
;
23 use syntax
::ast
::{self, NodeId}
;
25 use syntax
::codemap
::*;
26 use syntax
::parse
::token
::{self, keywords}
;
27 use syntax
::visit
::{self, Visitor}
;
28 use syntax
::print
::pprust
::ty_to_string
;
30 use self::span_utils
::SpanUtils
;
38 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
39 tcx
: &'l ty
::ctxt
<'tcx
>,
40 lcx
: &'l lowering
::LoweringContext
<'l
>,
41 span_utils
: SpanUtils
<'l
>,
44 pub struct CrateData
{
49 /// Data for any entity in the Rust language. The actual data contained varied
50 /// with the kind of entity being queried. See the nested structs for details.
53 /// Data for all kinds of functions and methods.
54 FunctionData(FunctionData
),
55 /// Data for local and global variables (consts and statics), and fields.
56 VariableData(VariableData
),
64 /// Data for the use of some variable (e.g., the use of a local variable, which
65 /// will refere to that variables declaration).
66 VariableRefData(VariableRefData
),
67 /// Data for a reference to a type or trait.
68 TypeRefData(TypeRefData
),
69 /// Data for a reference to a module.
70 ModRefData(ModRefData
),
71 /// Data about a function call.
72 FunctionCallData(FunctionCallData
),
73 /// Data about a method call.
74 MethodCallData(MethodCallData
),
77 /// Data for all kinds of functions and methods.
79 pub struct FunctionData
{
83 pub declaration
: Option
<DefId
>,
88 /// Data for local and global variables (consts and statics).
90 pub struct VariableData
{
97 pub type_value
: String
,
100 /// Data for modules.
105 pub qualname
: String
,
108 pub filename
: String
,
111 /// Data for enum declarations.
113 pub struct EnumData
{
116 pub qualname
: String
,
122 pub struct ImplData
{
126 // FIXME: I'm not really sure inline data is the best way to do this. Seems
127 // OK in this case, but generalising leads to returning chunks of AST, which
129 pub trait_ref
: Option
<TypeRefData
>,
130 pub self_ref
: Option
<TypeRefData
>,
133 /// Data for the use of some item (e.g., the use of a local variable, which
134 /// will refer to that variables declaration (by ref_id)).
136 pub struct VariableRefData
{
143 /// Data for a reference to a type or trait.
145 pub struct TypeRefData
{
151 /// Data for a reference to a module.
153 pub struct ModRefData
{
159 /// Data about a function call.
161 pub struct FunctionCallData
{
167 /// Data about a method call.
169 pub struct MethodCallData
{
172 pub ref_id
: Option
<DefId
>,
173 pub decl_id
: Option
<DefId
>,
178 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
179 pub fn new(tcx
: &'l ty
::ctxt
<'tcx
>,
180 lcx
: &'l lowering
::LoweringContext
<'l
>)
181 -> SaveContext
<'l
, 'tcx
> {
182 let span_utils
= SpanUtils
::new(&tcx
.sess
);
183 SaveContext
::from_span_utils(tcx
, lcx
, span_utils
)
186 pub fn from_span_utils(tcx
: &'l ty
::ctxt
<'tcx
>,
187 lcx
: &'l lowering
::LoweringContext
<'l
>,
188 span_utils
: SpanUtils
<'l
>)
189 -> SaveContext
<'l
, 'tcx
> {
193 span_utils
: span_utils
,
197 // List external crates used by the current crate.
198 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
199 let mut result
= Vec
::new();
201 self.tcx
.sess
.cstore
.iter_crate_data(|n
, cmd
| {
202 result
.push(CrateData
{
203 name
: cmd
.name
.clone(),
211 pub fn get_item_data(&self, item
: &ast
::Item
) -> Data
{
214 let name
= self.tcx
.map
.path_to_string(item
.id
);
215 let qualname
= format
!("::{}", name
);
216 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
218 Data
::FunctionData(FunctionData
{
223 span
: sub_span
.unwrap(),
224 scope
: self.enclosing_scope(item
.id
),
227 ast
::ItemStatic(ref typ
, mt
, ref expr
) => {
228 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
230 // If the variable is immutable, save the initialising expression.
231 let (value
, keyword
) = match mt
{
232 ast
::MutMutable
=> (String
::from("<mutable>"), keywords
::Mut
),
233 ast
::MutImmutable
=> (self.span_utils
.snippet(expr
.span
), keywords
::Static
),
236 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
238 Data
::VariableData(VariableData
{
240 name
: item
.ident
.to_string(),
242 span
: sub_span
.unwrap(),
243 scope
: self.enclosing_scope(item
.id
),
245 type_value
: ty_to_string(&typ
),
248 ast
::ItemConst(ref typ
, ref expr
) => {
249 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
250 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
252 Data
::VariableData(VariableData
{
254 name
: item
.ident
.to_string(),
256 span
: sub_span
.unwrap(),
257 scope
: self.enclosing_scope(item
.id
),
258 value
: self.span_utils
.snippet(expr
.span
),
259 type_value
: ty_to_string(&typ
),
262 ast
::ItemMod(ref m
) => {
263 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
265 let cm
= self.tcx
.sess
.codemap();
266 let filename
= cm
.span_to_filename(m
.inner
);
268 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
270 Data
::ModData(ModData
{
272 name
: item
.ident
.to_string(),
274 span
: sub_span
.unwrap(),
275 scope
: self.enclosing_scope(item
.id
),
279 ast
::ItemEnum(..) => {
280 let enum_name
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
281 let val
= self.span_utils
.snippet(item
.span
);
282 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
284 Data
::EnumData(EnumData
{
287 span
: sub_span
.unwrap(),
289 scope
: self.enclosing_scope(item
.id
),
292 ast
::ItemImpl(_
, _
, _
, ref trait_ref
, ref typ
, _
) => {
293 let mut type_data
= None
;
296 let parent
= self.enclosing_scope(item
.id
);
299 // Common case impl for a struct or something basic.
300 ast
::TyPath(None
, ref path
) => {
301 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
).unwrap();
302 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| {
311 // Less useful case, impl for a compound type.
313 sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
317 let trait_data
= trait_ref
.as_ref()
318 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
320 Data
::ImplData(ImplData
{
324 trait_ref
: trait_data
,
335 pub fn get_field_data(&self, field
: &ast
::StructField
, scope
: NodeId
) -> Option
<VariableData
> {
336 match field
.node
.kind
{
337 ast
::NamedField(ident
, _
) => {
338 let qualname
= format
!("::{}::{}", self.tcx
.map
.path_to_string(scope
), ident
);
339 let typ
= self.tcx
.node_types().get(&field
.node
.id
).unwrap().to_string();
340 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
343 name
: ident
.to_string(),
345 span
: sub_span
.unwrap(),
347 value
: "".to_owned(),
355 // FIXME would be nice to take a MethodItem here, but the ast provides both
356 // trait and impl flavours, so the caller must do the disassembly.
357 pub fn get_method_data(&self, id
: ast
::NodeId
, name
: ast
::Name
, span
: Span
) -> FunctionData
{
358 // The qualname for a method is the trait name or name of the struct in an impl in
359 // which the method is declared in, followed by the method's name.
360 let qualname
= match self.tcx
.impl_of_method(self.tcx
.map
.local_def_id(id
)) {
361 Some(impl_id
) => match self.tcx
.map
.get_if_local(impl_id
) {
362 Some(NodeItem(item
)) => {
364 hir
::ItemImpl(_
, _
, _
, _
, ref ty
, _
) => {
365 let mut result
= String
::from("<");
366 result
.push_str(&rustc_front
::print
::pprust
::ty_to_string(&**ty
));
368 match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
370 result
.push_str(" as ");
371 result
.push_str(&self.tcx
.item_path_str(def_id
));
375 result
.push_str(">");
379 self.tcx
.sess
.span_bug(span
,
380 &format
!("Container {:?} for method {} not \
388 self.tcx
.sess
.span_bug(span
,
389 &format
!("Container {:?} for method {} is not a node \
396 None
=> match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
398 match self.tcx
.map
.get_if_local(def_id
) {
399 Some(NodeItem(_
)) => {
400 format
!("::{}", self.tcx
.item_path_str(def_id
))
403 self.tcx
.sess
.span_bug(span
,
404 &format
!("Could not find container {:?} for \
405 method {}, got {:?}",
413 self.tcx
.sess
.span_bug(span
,
414 &format
!("Could not find container for method {}", id
));
419 let qualname
= format
!("{}::{}", qualname
, name
);
421 let def_id
= self.tcx
.map
.local_def_id(id
);
422 let decl_id
= self.tcx
.trait_item_of_item(def_id
).and_then(|new_id
| {
423 let new_def_id
= new_id
.def_id();
424 if new_def_id
!= def_id
{
431 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
435 name
: name
.to_string(),
437 declaration
: decl_id
,
438 span
: sub_span
.unwrap(),
439 scope
: self.enclosing_scope(id
),
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
).map(|def_id
| {
448 let span
= trait_ref
.path
.span
;
449 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
458 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
460 ast
::ExprField(ref sub_ex
, ident
) => {
461 let hir_node
= lowering
::lower_expr(self.lcx
, sub_ex
);
462 let ty
= &self.tcx
.expr_ty_adjusted(&hir_node
).sty
;
464 ty
::TyStruct(def
, _
) => {
465 let f
= def
.struct_variant().field_named(ident
.node
.name
);
466 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
467 return Some(Data
::VariableRefData(VariableRefData
{
468 name
: ident
.node
.to_string(),
469 span
: sub_span
.unwrap(),
470 scope
: self.enclosing_scope(expr
.id
),
475 debug
!("Expected struct type, found {:?}", ty
);
480 ast
::ExprStruct(ref path
, _
, _
) => {
481 let hir_node
= lowering
::lower_expr(self.lcx
, expr
);
482 let ty
= &self.tcx
.expr_ty_adjusted(&hir_node
).sty
;
484 ty
::TyStruct(def
, _
) => {
485 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
486 Some(Data
::TypeRefData(TypeRefData
{
487 span
: sub_span
.unwrap(),
488 scope
: self.enclosing_scope(expr
.id
),
493 // FIXME ty could legitimately be a TyEnum, but then we will fail
494 // later if we try to look up the fields.
495 debug
!("expected TyStruct, found {:?}", ty
);
500 ast
::ExprMethodCall(..) => {
501 let method_call
= ty
::MethodCall
::expr(expr
.id
);
502 let method_id
= self.tcx
.tables
.borrow().method_map
[&method_call
].def_id
;
503 let (def_id
, decl_id
) = match self.tcx
.impl_or_trait_item(method_id
).container() {
504 ty
::ImplContainer(_
) => (Some(method_id
), None
),
505 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
507 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
508 let parent
= self.enclosing_scope(expr
.id
);
509 Some(Data
::MethodCallData(MethodCallData
{
510 span
: sub_span
.unwrap(),
516 ast
::ExprPath(_
, ref path
) => {
517 self.get_path_data(expr
.id
, path
)
526 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Data
> {
527 let def_map
= self.tcx
.def_map
.borrow();
528 if !def_map
.contains_key(&id
) {
529 self.tcx
.sess
.span_bug(path
.span
,
530 &format
!("def_map has no key for {} in visit_expr", id
));
532 let def
= def_map
.get(&id
).unwrap().full_def();
533 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
539 def
::DefAssociatedConst(..) |
540 def
::DefVariant(..) => {
541 Some(Data
::VariableRefData(VariableRefData
{
542 name
: self.span_utils
.snippet(sub_span
.unwrap()),
543 span
: sub_span
.unwrap(),
544 scope
: self.enclosing_scope(id
),
545 ref_id
: def
.def_id(),
548 def
::DefStruct(def_id
) |
549 def
::DefTy(def_id
, _
) |
550 def
::DefTrait(def_id
) |
551 def
::DefTyParam(_
, _
, def_id
, _
) => {
552 Some(Data
::TypeRefData(TypeRefData
{
553 span
: sub_span
.unwrap(),
555 scope
: self.enclosing_scope(id
),
558 def
::DefMethod(decl_id
) => {
559 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
560 let def_id
= if decl_id
.is_local() {
561 let ti
= self.tcx
.impl_or_trait_item(decl_id
);
562 match ti
.container() {
563 ty
::TraitContainer(def_id
) => {
567 .find(|mr
| mr
.name() == ti
.name() && self.trait_method_has_body(mr
))
568 .map(|mr
| mr
.def_id())
570 ty
::ImplContainer(def_id
) => {
571 let impl_items
= self.tcx
.impl_items
.borrow();
572 Some(impl_items
.get(&def_id
)
576 self.tcx
.impl_or_trait_item(mr
.def_id()).name() ==
586 Some(Data
::MethodCallData(MethodCallData
{
587 span
: sub_span
.unwrap(),
588 scope
: self.enclosing_scope(id
),
590 decl_id
: Some(decl_id
),
593 def
::DefFn(def_id
, _
) => {
594 Some(Data
::FunctionCallData(FunctionCallData
{
596 span
: sub_span
.unwrap(),
597 scope
: self.enclosing_scope(id
),
600 def
::DefMod(def_id
) => {
601 Some(Data
::ModRefData(ModRefData
{
603 span
: sub_span
.unwrap(),
604 scope
: self.enclosing_scope(id
),
611 fn trait_method_has_body(&self, mr
: &ty
::ImplOrTraitItem
) -> bool
{
612 let def_id
= mr
.def_id();
613 if let Some(node_id
) = self.tcx
.map
.as_local_node_id(def_id
) {
614 let trait_item
= self.tcx
.map
.expect_trait_item(node_id
);
615 if let hir
::TraitItem_
::MethodTraitItem(_
, Some(_
)) = trait_item
.node
{
625 pub fn get_field_ref_data(&self,
626 field_ref
: &ast
::Field
,
627 variant
: ty
::VariantDef
,
630 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
631 // We don't really need a sub-span here, but no harm done
632 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
634 name
: field_ref
.ident
.node
.to_string(),
635 span
: sub_span
.unwrap(),
641 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
646 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
647 if !self.tcx
.def_map
.borrow().contains_key(&ref_id
) {
648 self.tcx
.sess
.bug(&format
!("def_map has no key for {} in lookup_type_ref",
651 let def
= self.tcx
.def_map
.borrow().get(&ref_id
).unwrap().full_def();
653 def
::DefPrimTy(_
) | def
::DefSelfTy(..) => None
,
654 _
=> Some(def
.def_id()),
659 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
660 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(0)
664 // An AST visitor for collecting paths from patterns.
665 struct PathCollector
{
666 // The Row field identifies the kind of pattern.
667 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
671 fn new() -> PathCollector
{
672 PathCollector { collected_paths: vec![] }
676 impl<'v
> Visitor
<'v
> for PathCollector
{
677 fn visit_pat(&mut self, p
: &ast
::Pat
) {
678 if generated_code(p
.span
) {
683 ast
::PatStruct(ref path
, _
, _
) => {
684 self.collected_paths
.push((p
.id
, path
.clone(), ast
::MutMutable
, recorder
::TypeRef
));
686 ast
::PatEnum(ref path
, _
) |
687 ast
::PatQPath(_
, ref path
) => {
688 self.collected_paths
.push((p
.id
, path
.clone(), ast
::MutMutable
, recorder
::VarRef
));
690 ast
::PatIdent(bm
, ref path1
, _
) => {
691 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
695 let immut
= match bm
{
696 // Even if the ref is mut, you can't change the ref, only
697 // the data pointed at, so showing the initialising expression
698 // is still worthwhile.
699 ast
::BindByRef(_
) => ast
::MutImmutable
,
700 ast
::BindByValue(mt
) => mt
,
702 // collect path for either visit_local or visit_arm
703 let path
= ast_util
::ident_to_path(path1
.span
, path1
.node
);
704 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
708 visit
::walk_pat(self, p
);
712 pub fn process_crate
<'l
, 'tcx
>(tcx
: &'l ty
::ctxt
<'tcx
>,
713 lcx
: &'l lowering
::LoweringContext
<'l
>,
715 analysis
: &ty
::CrateAnalysis
,
717 odir
: Option
<&Path
>) {
718 if generated_code(krate
.span
) {
722 assert
!(analysis
.glob_map
.is_some());
724 info
!("Dumping crate {}", cratename
);
726 // find a path to dump our data to
727 let mut root_path
= match env
::var_os("DXR_RUST_TEMP_FOLDER") {
728 Some(val
) => PathBuf
::from(val
),
730 Some(val
) => val
.join("dxr"),
731 None
=> PathBuf
::from("dxr-temp"),
735 if let Err(e
) = fs
::create_dir_all(&root_path
) {
736 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
742 let disp
= root_path
.display();
743 info
!("Writing output to {}", disp
);
746 // Create output file.
747 let mut out_name
= cratename
.to_owned();
748 out_name
.push_str(".csv");
749 root_path
.push(&out_name
);
750 let output_file
= match File
::create(&root_path
) {
753 let disp
= root_path
.display();
754 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
759 let mut visitor
= dump_csv
::DumpCsvVisitor
::new(tcx
, lcx
, analysis
, output_file
);
761 visitor
.dump_crate_info(cratename
, krate
);
762 visit
::walk_crate(&mut visitor
, krate
);
765 // Utility functions for the module.
767 // Helper function to escape quotes in a string
768 fn escape(s
: String
) -> String
{
769 s
.replace("\"", "\"\"")
772 // If the expression is a macro expansion or other generated code, run screaming
774 pub fn generated_code(span
: Span
) -> bool
{
775 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP