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
::{hir, lowering}
;
21 use rustc
::front
::map
::NodeItem
;
22 use rustc
::session
::config
::CrateType
::CrateTypeExecutable
;
24 use syntax
::ast
::{self, NodeId}
;
26 use syntax
::codemap
::*;
27 use syntax
::parse
::token
::{self, keywords}
;
28 use syntax
::visit
::{self, Visitor}
;
29 use syntax
::print
::pprust
::ty_to_string
;
31 use self::span_utils
::SpanUtils
;
39 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
40 tcx
: &'l ty
::ctxt
<'tcx
>,
41 lcx
: &'l lowering
::LoweringContext
<'l
>,
42 span_utils
: SpanUtils
<'l
>,
45 pub struct CrateData
{
50 /// Data for any entity in the Rust language. The actual data contained varied
51 /// with the kind of entity being queried. See the nested structs for details.
54 /// Data for all kinds of functions and methods.
55 FunctionData(FunctionData
),
56 /// Data for local and global variables (consts and statics), and fields.
57 VariableData(VariableData
),
65 /// Data for the use of some variable (e.g., the use of a local variable, which
66 /// will refere to that variables declaration).
67 VariableRefData(VariableRefData
),
68 /// Data for a reference to a type or trait.
69 TypeRefData(TypeRefData
),
70 /// Data for a reference to a module.
71 ModRefData(ModRefData
),
72 /// Data about a function call.
73 FunctionCallData(FunctionCallData
),
74 /// Data about a method call.
75 MethodCallData(MethodCallData
),
78 /// Data for all kinds of functions and methods.
80 pub struct FunctionData
{
84 pub declaration
: Option
<DefId
>,
89 /// Data for local and global variables (consts and statics).
91 pub struct VariableData
{
98 pub type_value
: String
,
101 /// Data for modules.
106 pub qualname
: String
,
109 pub filename
: String
,
112 /// Data for enum declarations.
114 pub struct EnumData
{
117 pub qualname
: String
,
123 pub struct ImplData
{
127 // FIXME: I'm not really sure inline data is the best way to do this. Seems
128 // OK in this case, but generalising leads to returning chunks of AST, which
130 pub trait_ref
: Option
<TypeRefData
>,
131 pub self_ref
: Option
<TypeRefData
>,
134 /// Data for the use of some item (e.g., the use of a local variable, which
135 /// will refer to that variables declaration (by ref_id)).
137 pub struct VariableRefData
{
144 /// Data for a reference to a type or trait.
146 pub struct TypeRefData
{
152 /// Data for a reference to a module.
154 pub struct ModRefData
{
160 /// Data about a function call.
162 pub struct FunctionCallData
{
168 /// Data about a method call.
170 pub struct MethodCallData
{
173 pub ref_id
: Option
<DefId
>,
174 pub decl_id
: Option
<DefId
>,
179 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
180 pub fn new(tcx
: &'l ty
::ctxt
<'tcx
>,
181 lcx
: &'l lowering
::LoweringContext
<'l
>)
182 -> SaveContext
<'l
, 'tcx
> {
183 let span_utils
= SpanUtils
::new(&tcx
.sess
);
184 SaveContext
::from_span_utils(tcx
, lcx
, span_utils
)
187 pub fn from_span_utils(tcx
: &'l ty
::ctxt
<'tcx
>,
188 lcx
: &'l lowering
::LoweringContext
<'l
>,
189 span_utils
: SpanUtils
<'l
>)
190 -> SaveContext
<'l
, 'tcx
> {
194 span_utils
: span_utils
,
198 // List external crates used by the current crate.
199 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
200 let mut result
= Vec
::new();
202 for n
in self.tcx
.sess
.cstore
.crates() {
203 result
.push(CrateData
{
204 name
: self.tcx
.sess
.cstore
.crate_name(n
),
212 pub fn get_item_data(&self, item
: &ast
::Item
) -> Data
{
215 let name
= self.tcx
.map
.path_to_string(item
.id
);
216 let qualname
= format
!("::{}", name
);
217 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
219 Data
::FunctionData(FunctionData
{
224 span
: sub_span
.unwrap(),
225 scope
: self.enclosing_scope(item
.id
),
228 ast
::ItemStatic(ref typ
, mt
, ref expr
) => {
229 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
231 // If the variable is immutable, save the initialising expression.
232 let (value
, keyword
) = match mt
{
233 ast
::MutMutable
=> (String
::from("<mutable>"), keywords
::Mut
),
234 ast
::MutImmutable
=> (self.span_utils
.snippet(expr
.span
), keywords
::Static
),
237 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
239 Data
::VariableData(VariableData
{
241 name
: item
.ident
.to_string(),
243 span
: sub_span
.unwrap(),
244 scope
: self.enclosing_scope(item
.id
),
246 type_value
: ty_to_string(&typ
),
249 ast
::ItemConst(ref typ
, ref expr
) => {
250 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
251 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
253 Data
::VariableData(VariableData
{
255 name
: item
.ident
.to_string(),
257 span
: sub_span
.unwrap(),
258 scope
: self.enclosing_scope(item
.id
),
259 value
: self.span_utils
.snippet(expr
.span
),
260 type_value
: ty_to_string(&typ
),
263 ast
::ItemMod(ref m
) => {
264 let qualname
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
266 let cm
= self.tcx
.sess
.codemap();
267 let filename
= cm
.span_to_filename(m
.inner
);
269 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
271 Data
::ModData(ModData
{
273 name
: item
.ident
.to_string(),
275 span
: sub_span
.unwrap(),
276 scope
: self.enclosing_scope(item
.id
),
280 ast
::ItemEnum(..) => {
281 let enum_name
= format
!("::{}", self.tcx
.map
.path_to_string(item
.id
));
282 let val
= self.span_utils
.snippet(item
.span
);
283 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
285 Data
::EnumData(EnumData
{
288 span
: sub_span
.unwrap(),
290 scope
: self.enclosing_scope(item
.id
),
293 ast
::ItemImpl(_
, _
, _
, ref trait_ref
, ref typ
, _
) => {
294 let mut type_data
= None
;
297 let parent
= self.enclosing_scope(item
.id
);
300 // Common case impl for a struct or something basic.
301 ast
::TyPath(None
, ref path
) => {
302 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
).unwrap();
303 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| {
312 // Less useful case, impl for a compound type.
314 sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
318 let trait_data
= trait_ref
.as_ref()
319 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
321 Data
::ImplData(ImplData
{
325 trait_ref
: trait_data
,
336 pub fn get_field_data(&self, field
: &ast
::StructField
, scope
: NodeId
) -> Option
<VariableData
> {
337 match field
.node
.kind
{
338 ast
::NamedField(ident
, _
) => {
339 let qualname
= format
!("::{}::{}", self.tcx
.map
.path_to_string(scope
), ident
);
340 let typ
= self.tcx
.node_types().get(&field
.node
.id
).unwrap().to_string();
341 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
344 name
: ident
.to_string(),
346 span
: sub_span
.unwrap(),
348 value
: "".to_owned(),
356 // FIXME would be nice to take a MethodItem here, but the ast provides both
357 // trait and impl flavours, so the caller must do the disassembly.
358 pub fn get_method_data(&self, id
: ast
::NodeId
, name
: ast
::Name
, span
: Span
) -> FunctionData
{
359 // The qualname for a method is the trait name or name of the struct in an impl in
360 // which the method is declared in, followed by the method's name.
361 let qualname
= match self.tcx
.impl_of_method(self.tcx
.map
.local_def_id(id
)) {
362 Some(impl_id
) => match self.tcx
.map
.get_if_local(impl_id
) {
363 Some(NodeItem(item
)) => {
365 hir
::ItemImpl(_
, _
, _
, _
, ref ty
, _
) => {
366 let mut result
= String
::from("<");
367 result
.push_str(&rustc_front
::print
::pprust
::ty_to_string(&**ty
));
369 match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
371 result
.push_str(" as ");
372 result
.push_str(&self.tcx
.item_path_str(def_id
));
376 result
.push_str(">");
380 self.tcx
.sess
.span_bug(span
,
381 &format
!("Container {:?} for method {} not \
389 self.tcx
.sess
.span_bug(span
,
390 &format
!("Container {:?} for method {} is not a node \
397 None
=> match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
399 match self.tcx
.map
.get_if_local(def_id
) {
400 Some(NodeItem(_
)) => {
401 format
!("::{}", self.tcx
.item_path_str(def_id
))
404 self.tcx
.sess
.span_bug(span
,
405 &format
!("Could not find container {:?} for \
406 method {}, got {:?}",
414 self.tcx
.sess
.span_bug(span
,
415 &format
!("Could not find container for method {}", id
));
420 let qualname
= format
!("{}::{}", qualname
, name
);
422 let def_id
= self.tcx
.map
.local_def_id(id
);
423 let decl_id
= self.tcx
.trait_item_of_item(def_id
).and_then(|new_id
| {
424 let new_def_id
= new_id
.def_id();
425 if new_def_id
!= def_id
{
432 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
436 name
: name
.to_string(),
438 declaration
: decl_id
,
439 span
: sub_span
.unwrap(),
440 scope
: self.enclosing_scope(id
),
444 pub fn get_trait_ref_data(&self,
445 trait_ref
: &ast
::TraitRef
,
447 -> Option
<TypeRefData
> {
448 self.lookup_ref_id(trait_ref
.ref_id
).map(|def_id
| {
449 let span
= trait_ref
.path
.span
;
450 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).unwrap_or(span
);
459 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
461 ast
::ExprField(ref sub_ex
, ident
) => {
462 let hir_node
= lowering
::lower_expr(self.lcx
, sub_ex
);
463 let ty
= &self.tcx
.expr_ty_adjusted(&hir_node
).sty
;
465 ty
::TyStruct(def
, _
) => {
466 let f
= def
.struct_variant().field_named(ident
.node
.name
);
467 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
468 return Some(Data
::VariableRefData(VariableRefData
{
469 name
: ident
.node
.to_string(),
470 span
: sub_span
.unwrap(),
471 scope
: self.enclosing_scope(expr
.id
),
476 debug
!("Expected struct type, found {:?}", ty
);
481 ast
::ExprStruct(ref path
, _
, _
) => {
482 let hir_node
= lowering
::lower_expr(self.lcx
, expr
);
483 let ty
= &self.tcx
.expr_ty_adjusted(&hir_node
).sty
;
485 ty
::TyStruct(def
, _
) => {
486 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
487 Some(Data
::TypeRefData(TypeRefData
{
488 span
: sub_span
.unwrap(),
489 scope
: self.enclosing_scope(expr
.id
),
494 // FIXME ty could legitimately be a TyEnum, but then we will fail
495 // later if we try to look up the fields.
496 debug
!("expected TyStruct, found {:?}", ty
);
501 ast
::ExprMethodCall(..) => {
502 let method_call
= ty
::MethodCall
::expr(expr
.id
);
503 let method_id
= self.tcx
.tables
.borrow().method_map
[&method_call
].def_id
;
504 let (def_id
, decl_id
) = match self.tcx
.impl_or_trait_item(method_id
).container() {
505 ty
::ImplContainer(_
) => (Some(method_id
), None
),
506 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
508 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
509 let parent
= self.enclosing_scope(expr
.id
);
510 Some(Data
::MethodCallData(MethodCallData
{
511 span
: sub_span
.unwrap(),
517 ast
::ExprPath(_
, ref path
) => {
518 self.get_path_data(expr
.id
, path
)
527 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Data
> {
528 let def_map
= self.tcx
.def_map
.borrow();
529 if !def_map
.contains_key(&id
) {
530 self.tcx
.sess
.span_bug(path
.span
,
531 &format
!("def_map has no key for {} in visit_expr", id
));
533 let def
= def_map
.get(&id
).unwrap().full_def();
534 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
540 def
::DefAssociatedConst(..) |
541 def
::DefVariant(..) => {
542 Some(Data
::VariableRefData(VariableRefData
{
543 name
: self.span_utils
.snippet(sub_span
.unwrap()),
544 span
: sub_span
.unwrap(),
545 scope
: self.enclosing_scope(id
),
546 ref_id
: def
.def_id(),
549 def
::DefStruct(def_id
) |
550 def
::DefTy(def_id
, _
) |
551 def
::DefTrait(def_id
) |
552 def
::DefTyParam(_
, _
, def_id
, _
) => {
553 Some(Data
::TypeRefData(TypeRefData
{
554 span
: sub_span
.unwrap(),
556 scope
: self.enclosing_scope(id
),
559 def
::DefMethod(decl_id
) => {
560 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
561 let def_id
= if decl_id
.is_local() {
562 let ti
= self.tcx
.impl_or_trait_item(decl_id
);
563 match ti
.container() {
564 ty
::TraitContainer(def_id
) => {
568 .find(|mr
| mr
.name() == ti
.name() && self.trait_method_has_body(mr
))
569 .map(|mr
| mr
.def_id())
571 ty
::ImplContainer(def_id
) => {
572 let impl_items
= self.tcx
.impl_items
.borrow();
573 Some(impl_items
.get(&def_id
)
577 self.tcx
.impl_or_trait_item(mr
.def_id()).name() ==
587 Some(Data
::MethodCallData(MethodCallData
{
588 span
: sub_span
.unwrap(),
589 scope
: self.enclosing_scope(id
),
591 decl_id
: Some(decl_id
),
594 def
::DefFn(def_id
, _
) => {
595 Some(Data
::FunctionCallData(FunctionCallData
{
597 span
: sub_span
.unwrap(),
598 scope
: self.enclosing_scope(id
),
601 def
::DefMod(def_id
) => {
602 Some(Data
::ModRefData(ModRefData
{
604 span
: sub_span
.unwrap(),
605 scope
: self.enclosing_scope(id
),
612 fn trait_method_has_body(&self, mr
: &ty
::ImplOrTraitItem
) -> bool
{
613 let def_id
= mr
.def_id();
614 if let Some(node_id
) = self.tcx
.map
.as_local_node_id(def_id
) {
615 let trait_item
= self.tcx
.map
.expect_trait_item(node_id
);
616 if let hir
::TraitItem_
::MethodTraitItem(_
, Some(_
)) = trait_item
.node
{
626 pub fn get_field_ref_data(&self,
627 field_ref
: &ast
::Field
,
628 variant
: ty
::VariantDef
,
631 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
632 // We don't really need a sub-span here, but no harm done
633 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
635 name
: field_ref
.ident
.node
.to_string(),
636 span
: sub_span
.unwrap(),
642 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
647 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
648 if !self.tcx
.def_map
.borrow().contains_key(&ref_id
) {
649 self.tcx
.sess
.bug(&format
!("def_map has no key for {} in lookup_type_ref",
652 let def
= self.tcx
.def_map
.borrow().get(&ref_id
).unwrap().full_def();
654 def
::DefPrimTy(_
) | def
::DefSelfTy(..) => None
,
655 _
=> Some(def
.def_id()),
660 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
661 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(0)
665 // An AST visitor for collecting paths from patterns.
666 struct PathCollector
{
667 // The Row field identifies the kind of pattern.
668 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
672 fn new() -> PathCollector
{
673 PathCollector { collected_paths: vec![] }
677 impl<'v
> Visitor
<'v
> for PathCollector
{
678 fn visit_pat(&mut self, p
: &ast
::Pat
) {
679 if generated_code(p
.span
) {
684 ast
::PatStruct(ref path
, _
, _
) => {
685 self.collected_paths
.push((p
.id
, path
.clone(), ast
::MutMutable
, recorder
::TypeRef
));
687 ast
::PatEnum(ref path
, _
) |
688 ast
::PatQPath(_
, ref path
) => {
689 self.collected_paths
.push((p
.id
, path
.clone(), ast
::MutMutable
, recorder
::VarRef
));
691 ast
::PatIdent(bm
, ref path1
, _
) => {
692 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
696 let immut
= match bm
{
697 // Even if the ref is mut, you can't change the ref, only
698 // the data pointed at, so showing the initialising expression
699 // is still worthwhile.
700 ast
::BindByRef(_
) => ast
::MutImmutable
,
701 ast
::BindByValue(mt
) => mt
,
703 // collect path for either visit_local or visit_arm
704 let path
= ast_util
::ident_to_path(path1
.span
, path1
.node
);
705 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
709 visit
::walk_pat(self, p
);
713 pub fn process_crate
<'l
, 'tcx
>(tcx
: &'l ty
::ctxt
<'tcx
>,
714 lcx
: &'l lowering
::LoweringContext
<'l
>,
716 analysis
: &ty
::CrateAnalysis
,
718 odir
: Option
<&Path
>) {
719 if generated_code(krate
.span
) {
723 assert
!(analysis
.glob_map
.is_some());
725 info
!("Dumping crate {}", cratename
);
727 // find a path to dump our data to
728 let mut root_path
= match env
::var_os("DXR_RUST_TEMP_FOLDER") {
729 Some(val
) => PathBuf
::from(val
),
731 Some(val
) => val
.join("dxr"),
732 None
=> PathBuf
::from("dxr-temp"),
736 if let Err(e
) = fs
::create_dir_all(&root_path
) {
737 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
743 let disp
= root_path
.display();
744 info
!("Writing output to {}", disp
);
747 // Create output file.
748 let executable
= tcx
.sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
749 let mut out_name
= if executable
{
754 out_name
.push_str(&cratename
);
755 out_name
.push_str(&tcx
.sess
.opts
.cg
.extra_filename
);
756 out_name
.push_str(".csv");
757 root_path
.push(&out_name
);
758 let output_file
= match File
::create(&root_path
) {
761 let disp
= root_path
.display();
762 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
767 let mut visitor
= dump_csv
::DumpCsvVisitor
::new(tcx
, lcx
, analysis
, output_file
);
769 visitor
.dump_crate_info(cratename
, krate
);
770 visit
::walk_crate(&mut visitor
, krate
);
773 // Utility functions for the module.
775 // Helper function to escape quotes in a string
776 fn escape(s
: String
) -> String
{
777 s
.replace("\"", "\"\"")
780 // If the expression is a macro expansion or other generated code, run screaming
782 pub fn generated_code(span
: Span
) -> bool
{
783 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP