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.
15 use std
::fs
::{self, File}
;
16 use std
::path
::{Path, PathBuf}
;
18 use rustc
::ast_map
::NodeItem
;
21 use syntax
::ast
::{self, NodeId, DefId}
;
23 use syntax
::codemap
::*;
24 use syntax
::parse
::token
::{self, keywords}
;
25 use syntax
::visit
::{self, Visitor}
;
26 use syntax
::print
::pprust
::ty_to_string
;
28 use self::span_utils
::SpanUtils
;
36 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
37 tcx
: &'l ty
::ctxt
<'tcx
>,
38 span_utils
: SpanUtils
<'l
>,
41 pub struct CrateData
{
46 /// Data for any entity in the Rust language. The actual data contained varied
47 /// with the kind of entity being queried. See the nested structs for details.
50 /// Data for all kinds of functions and methods.
51 FunctionData(FunctionData
),
52 /// Data for local and global variables (consts and statics), and fields.
53 VariableData(VariableData
),
61 /// Data for the use of some variable (e.g., the use of a local variable, which
62 /// will refere to that variables declaration).
63 VariableRefData(VariableRefData
),
64 /// Data for a reference to a type or trait.
65 TypeRefData(TypeRefData
),
66 /// Data for a reference to a module.
67 ModRefData(ModRefData
),
68 /// Data about a function call.
69 FunctionCallData(FunctionCallData
),
70 /// Data about a method call.
71 MethodCallData(MethodCallData
),
74 /// Data for all kinds of functions and methods.
76 pub struct FunctionData
{
80 pub declaration
: Option
<DefId
>,
85 /// Data for local and global variables (consts and statics).
87 pub struct VariableData
{
94 pub type_value
: String
,
102 pub qualname
: String
,
105 pub filename
: String
,
108 /// Data for enum declarations.
110 pub struct EnumData
{
113 pub qualname
: String
,
119 pub struct ImplData
{
123 // FIXME: I'm not really sure inline data is the best way to do this. Seems
124 // OK in this case, but generalising leads to returning chunks of AST, which
126 pub trait_ref
: Option
<TypeRefData
>,
127 pub self_ref
: Option
<TypeRefData
>,
130 /// Data for the use of some item (e.g., the use of a local variable, which
131 /// will refer to that variables declaration (by ref_id)).
133 pub struct VariableRefData
{
140 /// Data for a reference to a type or trait.
142 pub struct TypeRefData
{
148 /// Data for a reference to a module.
150 pub struct ModRefData
{
156 /// Data about a function call.
158 pub struct FunctionCallData
{
164 /// Data about a method call.
166 pub struct MethodCallData
{
169 pub ref_id
: Option
<DefId
>,
170 pub decl_id
: Option
<DefId
>,
175 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
176 pub fn new(tcx
: &'l ty
::ctxt
<'tcx
>) -> SaveContext
<'l
, 'tcx
> {
177 let span_utils
= SpanUtils
::new(&tcx
.sess
);
178 SaveContext
::from_span_utils(tcx
, span_utils
)
181 pub fn from_span_utils(tcx
: &'l ty
::ctxt
<'tcx
>,
182 span_utils
: SpanUtils
<'l
>)
183 -> SaveContext
<'l
, 'tcx
> {
186 span_utils
: span_utils
,
190 // List external crates used by the current crate.
191 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
192 let mut result
= Vec
::new();
194 self.tcx
.sess
.cstore
.iter_crate_data(|n
, cmd
| {
195 result
.push(CrateData { name: cmd.name.clone(), number: n }
);
201 pub fn get_item_data(&self, item
: &ast
::Item
) -> Data
{
204 let name
= self.tcx
.map
.path_to_string(item
.id
);
205 let qualname
= format
!("::{}", name
);
206 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
208 Data
::FunctionData(FunctionData
{
213 span
: sub_span
.unwrap(),
214 scope
: self.enclosing_scope(item
.id
),
217 ast
::ItemStatic(ref typ
, mt
, ref expr
) => {
218 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
220 // If the variable is immutable, save the initialising expression.
221 let (value
, keyword
) = match mt
{
222 ast
::MutMutable
=> (String
::from("<mutable>"), keywords
::Mut
),
223 ast
::MutImmutable
=> (self.span_utils
.snippet(expr
.span
), keywords
::Static
),
226 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
228 Data
::VariableData(VariableData
{
230 name
: item
.ident
.to_string(),
232 span
: sub_span
.unwrap(),
233 scope
: self.enclosing_scope(item
.id
),
235 type_value
: ty_to_string(&typ
),
238 ast
::ItemConst(ref typ
, ref expr
) => {
239 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
240 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
242 Data
::VariableData(VariableData
{
244 name
: item
.ident
.to_string(),
246 span
: sub_span
.unwrap(),
247 scope
: self.enclosing_scope(item
.id
),
248 value
: self.span_utils
.snippet(expr
.span
),
249 type_value
: ty_to_string(&typ
),
252 ast
::ItemMod(ref m
) => {
253 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
255 let cm
= self.tcx
.sess
.codemap();
256 let filename
= cm
.span_to_filename(m
.inner
);
258 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
260 Data
::ModData(ModData
{
262 name
: item
.ident
.to_string(),
264 span
: sub_span
.unwrap(),
265 scope
: self.enclosing_scope(item
.id
),
269 ast
::ItemEnum(..) => {
270 let enum_name
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
271 let val
= self.span_utils
.snippet(item
.span
);
272 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
274 Data
::EnumData(EnumData
{
277 span
: sub_span
.unwrap(),
279 scope
: self.enclosing_scope(item
.id
),
282 ast
::ItemImpl(_
, _
, _
, ref trait_ref
, ref typ
, _
) => {
283 let mut type_data
= None
;
286 let parent
= self.enclosing_scope(item
.id
);
289 // Common case impl for a struct or something basic.
290 ast
::TyPath(None
, ref path
) => {
291 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
).unwrap();
292 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| TypeRefData
{
299 // Less useful case, impl for a compound type.
301 sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
306 trait_ref
.as_ref().and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
308 Data
::ImplData(ImplData
{
312 trait_ref
: trait_data
,
323 pub fn get_field_data(&self, field
: &ast
::StructField
, scope
: NodeId
) -> Option
<VariableData
> {
324 match field
.node
.kind
{
325 ast
::NamedField(ident
, _
) => {
326 let qualname
= format
!("::{}::{}",
327 self.tcx
.map
.path_to_string(scope
),
329 let typ
= self.tcx
.node_types().get(&field
.node
.id
).unwrap()
331 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
334 name
: ident
.to_string(),
336 span
: sub_span
.unwrap(),
338 value
: "".to_owned(),
346 // FIXME would be nice to take a MethodItem here, but the ast provides both
347 // trait and impl flavours, so the caller must do the disassembly.
348 pub fn get_method_data(&self,
351 span
: Span
) -> FunctionData
{
352 // The qualname for a method is the trait name or name of the struct in an impl in
353 // which the method is declared in, followed by the method's name.
354 let qualname
= match self.tcx
.impl_of_method(ast_util
::local_def(id
)) {
355 Some(impl_id
) => match self.tcx
.map
.get(impl_id
.node
) {
358 ast
::ItemImpl(_
, _
, _
, _
, ref ty
, _
) => {
359 let mut result
= String
::from("<");
360 result
.push_str(&ty_to_string(&**ty
));
362 match self.tcx
.trait_of_item(ast_util
::local_def(id
)) {
364 result
.push_str(" as ");
366 &self.tcx
.item_path_str(def_id
));
370 result
.push_str(">");
374 self.tcx
.sess
.span_bug(span
,
375 &format
!("Container {} for method {} not an impl?",
381 self.tcx
.sess
.span_bug(span
,
382 &format
!("Container {} for method {} is not a node item {:?}",
383 impl_id
.node
, id
, self.tcx
.map
.get(impl_id
.node
)));
386 None
=> match self.tcx
.trait_of_item(ast_util
::local_def(id
)) {
388 match self.tcx
.map
.get(def_id
.node
) {
390 format
!("::{}", self.tcx
.item_path_str(def_id
))
393 self.tcx
.sess
.span_bug(span
,
394 &format
!("Could not find container {} for method {}",
400 self.tcx
.sess
.span_bug(span
,
401 &format
!("Could not find container for method {}", id
));
406 let qualname
= format
!("{}::{}", qualname
, name
);
408 let decl_id
= self.tcx
.trait_item_of_item(ast_util
::local_def(id
))
410 let def_id
= new_id
.def_id();
411 if def_id
.node
!= 0 && def_id
!= ast_util
::local_def(id
) {
418 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
422 name
: name
.to_string(),
424 declaration
: decl_id
,
425 span
: sub_span
.unwrap(),
426 scope
: self.enclosing_scope(id
),
430 pub fn get_trait_ref_data(&self,
431 trait_ref
: &ast
::TraitRef
,
433 -> Option
<TypeRefData
> {
434 self.lookup_ref_id(trait_ref
.ref_id
).map(|def_id
| {
435 let span
= trait_ref
.path
.span
;
436 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
445 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
447 ast
::ExprField(ref sub_ex
, ident
) => {
448 let ty
= &self.tcx
.expr_ty_adjusted(&sub_ex
).sty
;
450 ty
::TyStruct(def_id
, _
) => {
451 let fields
= self.tcx
.lookup_struct_fields(def_id
);
453 if f
.name
== ident
.node
.name
{
454 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
455 return Some(Data
::VariableRefData(VariableRefData
{
456 name
: ident
.node
.to_string(),
457 span
: sub_span
.unwrap(),
458 scope
: self.enclosing_scope(expr
.id
),
464 self.tcx
.sess
.span_bug(expr
.span
,
465 &format
!("Couldn't find field {} on {:?}",
469 debug
!("Expected struct type, found {:?}", ty
);
474 ast
::ExprStruct(ref path
, _
, _
) => {
475 let ty
= &self.tcx
.expr_ty_adjusted(expr
).sty
;
477 ty
::TyStruct(def_id
, _
) => {
478 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
479 Some(Data
::TypeRefData(TypeRefData
{
480 span
: sub_span
.unwrap(),
481 scope
: self.enclosing_scope(expr
.id
),
486 // FIXME ty could legitimately be a TyEnum, but then we will fail
487 // later if we try to look up the fields.
488 debug
!("expected TyStruct, found {:?}", ty
);
493 ast
::ExprMethodCall(..) => {
494 let method_call
= ty
::MethodCall
::expr(expr
.id
);
495 let method_id
= self.tcx
.tables
.borrow().method_map
[&method_call
].def_id
;
496 let (def_id
, decl_id
) = match self.tcx
.impl_or_trait_item(method_id
).container() {
497 ty
::ImplContainer(_
) => (Some(method_id
), None
),
498 ty
::TraitContainer(_
) => (None
, Some(method_id
))
500 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
501 let parent
= self.enclosing_scope(expr
.id
);
502 Some(Data
::MethodCallData(MethodCallData
{
503 span
: sub_span
.unwrap(),
509 ast
::ExprPath(_
, ref path
) => {
510 self.get_path_data(expr
.id
, path
)
519 pub fn get_path_data(&self,
523 let def_map
= self.tcx
.def_map
.borrow();
524 if !def_map
.contains_key(&id
) {
525 self.tcx
.sess
.span_bug(path
.span
,
526 &format
!("def_map has no key for {} in visit_expr", id
));
528 let def
= def_map
.get(&id
).unwrap().full_def();
529 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
535 def
::DefAssociatedConst(..) |
536 def
::DefVariant(..) => {
537 Some(Data
::VariableRefData(VariableRefData
{
538 name
: self.span_utils
.snippet(sub_span
.unwrap()),
539 span
: sub_span
.unwrap(),
540 scope
: self.enclosing_scope(id
),
541 ref_id
: def
.def_id(),
544 def
::DefStruct(def_id
) |
545 def
::DefTy(def_id
, _
) |
546 def
::DefTrait(def_id
) |
547 def
::DefTyParam(_
, _
, def_id
, _
) => {
548 Some(Data
::TypeRefData(TypeRefData
{
549 span
: sub_span
.unwrap(),
551 scope
: self.enclosing_scope(id
),
554 def
::DefMethod(decl_id
) => {
555 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
556 let def_id
= if decl_id
.krate
== ast
::LOCAL_CRATE
{
557 let ti
= self.tcx
.impl_or_trait_item(decl_id
);
558 match ti
.container() {
559 ty
::TraitContainer(def_id
) => {
560 self.tcx
.trait_items(def_id
)
563 mr
.name() == ti
.name() && self.trait_method_has_body(mr
)
565 .map(|mr
| mr
.def_id())
567 ty
::ImplContainer(def_id
) => {
568 let impl_items
= self.tcx
.impl_items
.borrow();
569 Some(impl_items
.get(&def_id
)
573 self.tcx
.impl_or_trait_item(mr
.def_id()).name()
583 Some(Data
::MethodCallData(MethodCallData
{
584 span
: sub_span
.unwrap(),
585 scope
: self.enclosing_scope(id
),
587 decl_id
: Some(decl_id
),
590 def
::DefFn(def_id
, _
) => {
591 Some(Data
::FunctionCallData(FunctionCallData
{
593 span
: sub_span
.unwrap(),
594 scope
: self.enclosing_scope(id
),
597 def
::DefMod(def_id
) => {
598 Some(Data
::ModRefData(ModRefData
{
600 span
: sub_span
.unwrap(),
601 scope
: self.enclosing_scope(id
),
608 fn trait_method_has_body(&self, mr
: &ty
::ImplOrTraitItem
) -> bool
{
609 let def_id
= mr
.def_id();
610 if def_id
.krate
!= ast
::LOCAL_CRATE
{
614 let trait_item
= self.tcx
.map
.expect_trait_item(def_id
.node
);
615 if let ast
::TraitItem_
::MethodTraitItem(_
, Some(_
)) = trait_item
.node
{
622 pub fn get_field_ref_data(&self,
623 field_ref
: &ast
::Field
,
627 let fields
= self.tcx
.lookup_struct_fields(struct_id
);
628 let field_name
= field_ref
.ident
.node
.to_string();
630 if f
.name
== 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
);
633 return VariableRefData
{
635 span
: sub_span
.unwrap(),
642 self.tcx
.sess
.span_bug(field_ref
.span
,
643 &format
!("Couldn't find field {}", field_name
));
646 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
651 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
652 if !self.tcx
.def_map
.borrow().contains_key(&ref_id
) {
653 self.tcx
.sess
.bug(&format
!("def_map has no key for {} in lookup_type_ref",
656 let def
= self.tcx
.def_map
.borrow().get(&ref_id
).unwrap().full_def();
658 def
::DefPrimTy(_
) => None
,
659 _
=> Some(def
.def_id()),
664 fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
665 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(0)
669 // An AST visitor for collecting paths from patterns.
670 struct PathCollector
{
671 // The Row field identifies the kind of pattern.
672 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
676 fn new() -> PathCollector
{
678 collected_paths
: vec
![],
683 impl<'v
> Visitor
<'v
> for PathCollector
{
684 fn visit_pat(&mut self, p
: &ast
::Pat
) {
685 if generated_code(p
.span
) {
690 ast
::PatStruct(ref path
, _
, _
) => {
691 self.collected_paths
.push((p
.id
,
696 ast
::PatEnum(ref path
, _
) |
697 ast
::PatQPath(_
, ref path
) => {
698 self.collected_paths
.push((p
.id
, path
.clone(), ast
::MutMutable
, recorder
::VarRef
));
700 ast
::PatIdent(bm
, ref path1
, _
) => {
701 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
705 let immut
= match bm
{
706 // Even if the ref is mut, you can't change the ref, only
707 // the data pointed at, so showing the initialising expression
708 // is still worthwhile.
709 ast
::BindByRef(_
) => ast
::MutImmutable
,
710 ast
::BindByValue(mt
) => mt
,
712 // collect path for either visit_local or visit_arm
713 let path
= ast_util
::ident_to_path(path1
.span
, path1
.node
);
714 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
718 visit
::walk_pat(self, p
);
723 pub fn process_crate(tcx
: &ty
::ctxt
,
724 analysis
: &ty
::CrateAnalysis
,
725 odir
: Option
<&Path
>) {
726 let krate
= tcx
.map
.krate();
727 if generated_code(krate
.span
) {
731 assert
!(analysis
.glob_map
.is_some());
732 let cratename
= match attr
::find_crate_name(&krate
.attrs
) {
733 Some(name
) => name
.to_string(),
735 info
!("Could not find crate name, using 'unknown_crate'");
736 String
::from("unknown_crate")
740 info
!("Dumping crate {}", cratename
);
742 // find a path to dump our data to
743 let mut root_path
= match env
::var_os("DXR_RUST_TEMP_FOLDER") {
744 Some(val
) => PathBuf
::from(val
),
746 Some(val
) => val
.join("dxr"),
747 None
=> PathBuf
::from("dxr-temp"),
751 if let Err(e
) = fs
::create_dir_all(&root_path
) {
752 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
753 root_path
.display(), e
));
757 let disp
= root_path
.display();
758 info
!("Writing output to {}", disp
);
761 // Create output file.
762 let mut out_name
= cratename
.clone();
763 out_name
.push_str(".csv");
764 root_path
.push(&out_name
);
765 let output_file
= match File
::create(&root_path
) {
768 let disp
= root_path
.display();
769 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
774 let mut visitor
= dump_csv
::DumpCsvVisitor
::new(tcx
, analysis
, output_file
);
776 visitor
.dump_crate_info(&cratename
, krate
);
777 visit
::walk_crate(&mut visitor
, krate
);
780 // Utility functions for the module.
782 // Helper function to escape quotes in a string
783 fn escape(s
: String
) -> String
{
784 s
.replace("\"", "\"\"")
787 // If the expression is a macro expansion or other generated code, run screaming
789 pub fn generated_code(span
: Span
) -> bool
{
790 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP