1 // Copyright 2012-2014 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 //! Output a CSV file containing the output from rustc's analysis. The data is
12 //! primarily designed to be used as input to the DXR tool, specifically its
13 //! Rust plugin. It could also be used by IDEs or other code browsing, search, or
14 //! cross-referencing tools.
16 //! Dumping the analysis is implemented by walking the AST and getting a bunch of
17 //! info out from all over the place. We use Def IDs to identify objects. The
18 //! tricky part is getting syntactic (span, source text) and semantic (reference
19 //! Def IDs) information for parts of expressions which the compiler has discarded.
20 //! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole
21 //! path and a reference to `baz`, but we want spans and references for all three
24 //! SpanUtils is used to manipulate spans. In particular, to extract sub-spans
25 //! from spans (e.g., the span for `bar` from the above example path).
26 //! Recorder is used for recording the output in csv format. FmtStrs separates
27 //! the format of the output away from extracting it from the compiler.
28 //! DxrVisitor walks the AST and processes it.
33 use middle::ty::{self, Ty};
37 use std::fs::{self, File};
38 use std::path::{Path, PathBuf};
41 use syntax::ast::{self, NodeId, DefId};
42 use syntax::ast_map::NodeItem;
44 use syntax::codemap::*;
45 use syntax::parse::token::{self, get_ident, keywords};
46 use syntax::owned_slice::OwnedSlice;
47 use syntax::visit::{self, Visitor};
48 use syntax::print::pprust::{path_to_string, ty_to_string};
51 use self::span_utils::SpanUtils;
52 use self::recorder::{Recorder, FmtStrs};
59 // Helper function to escape quotes in a string
60 fn escape(s: String) -> String {
61 s.replace("\"", "\"\"")
64 // If the expression is a macro expansion or other generated code, run screaming and don't index.
65 fn generated_code(span: Span) -> bool {
66 span.expn_id != NO_EXPANSION || span == DUMMY_SP
69 struct DxrVisitor<'l, 'tcx: 'l> {
71 analysis: &'l ty::CrateAnalysis<'tcx>,
73 collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>,
82 impl <'l, 'tcx> DxrVisitor<'l, 'tcx> {
83 fn nest<F>(&mut self, scope_id: NodeId, f: F) where
84 F: FnOnce(&mut DxrVisitor<'l, 'tcx>),
86 let parent_scope = self.cur_scope;
87 self.cur_scope = scope_id;
89 self.cur_scope = parent_scope;
92 fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
94 self.fmt.crate_str(krate.span, name);
96 // dump info about all the external crates referenced from this crate
97 self.sess.cstore.iter_crate_data(|n, cmd| {
98 self.fmt.external_crate_str(krate.span, &cmd.name, n);
100 self.fmt.recorder.record("end_external_crates\n");
103 // Return all non-empty prefixes of a path.
104 // For each prefix, we return the span for the last segment in the prefix and
105 // a str representation of the entire prefix.
106 fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> {
107 let spans = self.span.spans_for_path_segments(path);
109 // Paths to enums seem to not match their spans - the span includes all the
110 // variants too. But they seem to always be at the end, so I hope we can cope with
111 // always using the first ones. So, only error out if we don't have enough spans.
112 // What could go wrong...?
113 if spans.len() < path.segments.len() {
114 error!("Mis-calculated spans for path '{}'. \
115 Found {} spans, expected {}. Found spans:",
116 path_to_string(path), spans.len(), path.segments.len());
118 let loc = self.sess.codemap().lookup_char_pos(s.lo);
119 error!(" '{}' in {}, line {}",
120 self.span.snippet(*s), loc.file.name, loc.line);
125 let mut result: Vec<(Span, String)> = vec!();
127 let mut segs = vec!();
128 for (i, (seg, span)) in path.segments.iter().zip(spans.iter()).enumerate() {
129 segs.push(seg.clone());
130 let sub_path = ast::Path{span: *span, // span for the last segment
133 let qualname = if i == 0 && path.global {
134 format!("::{}", path_to_string(&sub_path))
136 path_to_string(&sub_path)
138 result.push((*span, qualname));
139 segs = sub_path.segments;
145 // The global arg allows us to override the global-ness of the path (which
146 // actually means 'does the path start with `::`', rather than 'is the path
147 // semantically global). We use the override for `use` imports (etc.) where
148 // the syntax is non-global, but the semantics are global.
149 fn write_sub_paths(&mut self, path: &ast::Path, global: bool) {
150 let sub_paths = self.process_path_prefixes(path);
151 for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
152 let qualname = if i == 0 && global && !path.global {
153 format!("::{}", qualname)
157 self.fmt.sub_mod_ref_str(path.span,
164 // As write_sub_paths, but does not process the last ident in the path (assuming it
165 // will be processed elsewhere). See note on write_sub_paths about global.
166 fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) {
167 let sub_paths = self.process_path_prefixes(path);
168 let len = sub_paths.len();
173 let sub_paths = &sub_paths[..len-1];
174 for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
175 let qualname = if i == 0 && global && !path.global {
176 format!("::{}", qualname)
180 self.fmt.sub_mod_ref_str(path.span,
187 // As write_sub_paths, but expects a path of the form module_path::trait::method
188 // Where trait could actually be a struct too.
189 fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) {
190 let sub_paths = self.process_path_prefixes(path);
191 let len = sub_paths.len();
195 let sub_paths = &sub_paths[.. (len-1)];
197 // write the trait part of the sub-path
198 let (ref span, ref qualname) = sub_paths[len-2];
199 self.fmt.sub_type_ref_str(path.span,
203 // write the other sub-paths
207 let sub_paths = &sub_paths[..len-2];
208 for &(ref span, ref qualname) in sub_paths {
209 self.fmt.sub_mod_ref_str(path.span,
216 // looks up anything, not just a type
217 fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
218 if !self.analysis.ty_cx.def_map.borrow().contains_key(&ref_id) {
219 self.sess.bug(&format!("def_map has no key for {} in lookup_type_ref",
222 let def = self.analysis.ty_cx.def_map.borrow().get(&ref_id).unwrap().full_def();
224 def::DefPrimTy(_) => None,
225 _ => Some(def.def_id()),
229 fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option<recorder::Row> {
230 let def_map = self.analysis.ty_cx.def_map.borrow();
231 if !def_map.contains_key(&ref_id) {
232 self.sess.span_bug(span, &format!("def_map has no key for {} in lookup_def_kind",
235 let def = def_map.get(&ref_id).unwrap().full_def();
238 def::DefForeignMod(_) => Some(recorder::ModRef),
239 def::DefStruct(_) => Some(recorder::StructRef),
241 def::DefAssociatedTy(..) |
242 def::DefTrait(_) => Some(recorder::TypeRef),
243 def::DefStatic(_, _) |
246 def::DefVariant(_, _, _) |
247 def::DefUpvar(..) => Some(recorder::VarRef),
249 def::DefFn(..) => Some(recorder::FnRef),
254 def::DefTyParam(..) |
257 def::DefPrimTy(_) => {
258 self.sess.span_bug(span, &format!("lookup_def_kind for unexpected item: {:?}",
264 fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str) {
266 assert!(self.collected_paths.len() == 0 && !self.collecting);
267 self.collecting = true;
268 self.visit_pat(&*arg.pat);
269 self.collecting = false;
270 let span_utils = self.span.clone();
271 for &(id, ref p, _, _) in &self.collected_paths {
274 &self.analysis.ty_cx,
275 *self.analysis.ty_cx.node_types.borrow().get(&id).unwrap());
276 // get the span only for the name of the variable (I hope the path is only ever a
277 // variable name, but who knows?)
278 self.fmt.formal_str(p.span,
279 span_utils.span_for_last_ident(p.span),
285 self.collected_paths.clear();
289 fn process_method(&mut self, sig: &ast::MethodSig,
290 body: Option<&ast::Block>,
291 id: ast::NodeId, ident: ast::Ident,
293 if generated_code(span) {
298 // The qualname for a method is the trait name or name of the struct in an impl in
299 // which the method is declared in, followed by the method's name.
300 let qualname = match ty::impl_of_method(&self.analysis.ty_cx,
301 ast_util::local_def(id)) {
302 Some(impl_id) => match self.analysis.ty_cx.map.get(impl_id.node) {
306 ast::ItemImpl(_, _, _, _, ref ty, _) => {
307 let mut result = String::from_str("<");
308 result.push_str(&ty_to_string(&**ty));
310 match ty::trait_of_item(&self.analysis.ty_cx,
311 ast_util::local_def(id)) {
313 result.push_str(" as ");
315 &ty::item_path_str(&self.analysis.ty_cx, def_id));
319 result.push_str(">");
323 self.sess.span_bug(span,
324 &format!("Container {} for method {} not an impl?",
330 self.sess.span_bug(span,
331 &format!("Container {} for method {} is not a node item {:?}",
332 impl_id.node, id, self.analysis.ty_cx.map.get(impl_id.node)));
335 None => match ty::trait_of_item(&self.analysis.ty_cx,
336 ast_util::local_def(id)) {
338 scope_id = def_id.node;
339 match self.analysis.ty_cx.map.get(def_id.node) {
341 format!("::{}", ty::item_path_str(&self.analysis.ty_cx, def_id))
344 self.sess.span_bug(span,
345 &format!("Could not find container {} for method {}",
351 self.sess.span_bug(span,
352 &format!("Could not find container for method {}", id));
357 let qualname = &format!("{}::{}", qualname, &get_ident(ident));
359 // record the decl for this def (if it has one)
360 let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx,
361 ast_util::local_def(id))
364 ty::MethodTraitItemId(def_id) => {
365 def_id.node != 0 && def_id != ast_util::local_def(id)
367 ty::TypeTraitItemId(_) => false,
369 Some(def_id.def_id())
375 let sub_span = self.span.sub_span_after_keyword(span, keywords::Fn);
377 self.fmt.method_str(span,
383 self.process_formals(&sig.decl.inputs, qualname);
385 self.fmt.method_decl_str(span,
392 // walk arg and return types
393 for arg in &sig.decl.inputs {
394 self.visit_ty(&arg.ty);
397 if let ast::Return(ref ret_ty) = sig.decl.output {
398 self.visit_ty(ret_ty);
402 if let Some(body) = body {
403 self.nest(id, |v| v.visit_block(body));
406 self.process_generic_params(&sig.generics,
412 fn process_trait_ref(&mut self,
413 trait_ref: &ast::TraitRef) {
414 match self.lookup_type_ref(trait_ref.ref_id) {
416 let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
417 self.fmt.ref_str(recorder::TypeRef,
422 visit::walk_path(self, &trait_ref.path);
428 fn process_struct_field_def(&mut self,
429 field: &ast::StructField,
432 match field.node.kind {
433 ast::NamedField(ident, _) => {
434 let name = get_ident(ident);
435 let qualname = format!("{}::{}", qualname, name);
438 &self.analysis.ty_cx,
439 *self.analysis.ty_cx.node_types.borrow().get(&field.node.id).unwrap());
440 match self.span.sub_span_before_token(field.span, token::Colon) {
441 Some(sub_span) => self.fmt.field_str(field.span,
448 None => self.sess.span_bug(field.span,
449 &format!("Could not find sub-span for field {}",
457 // Dump generic params bindings, then visit_generics
458 fn process_generic_params(&mut self,
459 generics:&ast::Generics,
463 // We can't only use visit_generics since we don't have spans for param
464 // bindings, so we reparse the full_span to get those sub spans.
465 // However full span is the entire enum/fn/struct block, so we only want
466 // the first few to match the number of generics we're looking for.
467 let param_sub_spans = self.span.spans_for_ty_params(full_span,
468 (generics.ty_params.len() as int));
469 for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans.iter()) {
470 // Append $id to name to make sure each one is unique
471 let name = format!("{}::{}${}",
473 escape(self.span.snippet(*param_ss)),
475 self.fmt.typedef_str(full_span,
481 self.visit_generics(generics);
484 fn process_fn(&mut self,
487 ty_params: &ast::Generics,
489 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
491 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn);
492 self.fmt.fn_str(item.span,
498 self.process_formals(&decl.inputs, &qualname[..]);
500 // walk arg and return types
501 for arg in &decl.inputs {
502 self.visit_ty(&*arg.ty);
505 if let ast::Return(ref ret_ty) = decl.output {
506 self.visit_ty(&**ret_ty);
510 self.nest(item.id, |v| v.visit_block(&*body));
512 self.process_generic_params(ty_params, item.span, &qualname[..], item.id);
515 fn process_static(&mut self,
521 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
523 // If the variable is immutable, save the initialising expression.
524 let value = match mt {
525 ast::MutMutable => String::from_str("<mutable>"),
526 ast::MutImmutable => self.span.snippet(expr.span),
529 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Static);
530 self.fmt.static_str(item.span,
533 &get_ident(item.ident),
536 &ty_to_string(&*typ),
539 // walk type and init value
540 self.visit_ty(&*typ);
541 self.visit_expr(expr);
544 fn process_const(&mut self,
549 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
551 let sub_span = self.span.sub_span_after_keyword(item.span,
553 self.fmt.static_str(item.span,
556 &get_ident(item.ident),
559 &ty_to_string(&*typ),
562 // walk type and init value
563 self.visit_ty(&*typ);
564 self.visit_expr(expr);
567 fn process_struct(&mut self,
569 def: &ast::StructDef,
570 ty_params: &ast::Generics) {
571 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
573 let ctor_id = match def.ctor_id {
574 Some(node_id) => node_id,
577 let val = self.span.snippet(item.span);
578 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
579 self.fmt.struct_str(item.span,
588 for field in &def.fields {
589 self.process_struct_field_def(field, &qualname[..], item.id);
590 self.visit_ty(&*field.node.ty);
593 self.process_generic_params(ty_params, item.span, &qualname[..], item.id);
596 fn process_enum(&mut self,
598 enum_definition: &ast::EnumDef,
599 ty_params: &ast::Generics) {
600 let enum_name = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
601 let val = self.span.snippet(item.span);
602 match self.span.sub_span_after_keyword(item.span, keywords::Enum) {
603 Some(sub_span) => self.fmt.enum_str(item.span,
609 None => self.sess.span_bug(item.span,
610 &format!("Could not find subspan for enum {}",
613 for variant in &enum_definition.variants {
614 let name = get_ident(variant.node.name);
616 let mut qualname = enum_name.clone();
617 qualname.push_str("::");
618 qualname.push_str(name);
619 let val = self.span.snippet(variant.span);
620 match variant.node.kind {
621 ast::TupleVariantKind(ref args) => {
622 // first ident in span is the variant's name
623 self.fmt.tuple_variant_str(variant.span,
624 self.span.span_for_first_ident(variant.span),
632 self.visit_ty(&*arg.ty);
635 ast::StructVariantKind(ref struct_def) => {
636 let ctor_id = match struct_def.ctor_id {
637 Some(node_id) => node_id,
640 self.fmt.struct_variant_str(
642 self.span.span_for_first_ident(variant.span),
650 for field in &struct_def.fields {
651 self.process_struct_field_def(field, &qualname, variant.node.id);
652 self.visit_ty(&*field.node.ty);
658 self.process_generic_params(ty_params, item.span, &enum_name[..], item.id);
661 fn process_impl(&mut self,
663 type_parameters: &ast::Generics,
664 trait_ref: &Option<ast::TraitRef>,
666 impl_items: &[P<ast::ImplItem>]) {
667 let trait_id = trait_ref.as_ref().and_then(|tr| self.lookup_type_ref(tr.ref_id));
669 // Common case impl for a struct or something basic.
670 ast::TyPath(None, ref path) => {
671 let sub_span = self.span.sub_span_for_type_name(path.span);
672 let self_id = self.lookup_type_ref(typ.id).map(|id| {
673 self.fmt.ref_str(recorder::TypeRef,
680 self.fmt.impl_str(path.span,
688 // Less useful case, impl for a compound type.
689 self.visit_ty(&*typ);
691 let sub_span = self.span.sub_span_for_type_name(typ.span);
692 self.fmt.impl_str(typ.span,
702 Some(ref trait_ref) => self.process_trait_ref(trait_ref),
706 self.process_generic_params(type_parameters, item.span, "", item.id);
707 for impl_item in impl_items {
708 visit::walk_impl_item(self, impl_item);
712 fn process_trait(&mut self,
714 generics: &ast::Generics,
715 trait_refs: &OwnedSlice<ast::TyParamBound>,
716 methods: &[P<ast::TraitItem>]) {
717 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
718 let val = self.span.snippet(item.span);
719 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait);
720 self.fmt.trait_str(item.span,
728 for super_bound in &**trait_refs {
729 let trait_ref = match *super_bound {
730 ast::TraitTyParamBound(ref trait_ref, _) => {
733 ast::RegionTyParamBound(..) => {
738 let trait_ref = &trait_ref.trait_ref;
739 match self.lookup_type_ref(trait_ref.ref_id) {
741 let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
742 self.fmt.ref_str(recorder::TypeRef,
747 self.fmt.inherit_str(trait_ref.path.span,
756 // walk generics and methods
757 self.process_generic_params(generics, item.span, &qualname[..], item.id);
758 for method in methods {
759 self.visit_trait_item(method)
763 fn process_mod(&mut self,
764 item: &ast::Item, // The module in question, represented as an item.
766 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
768 let cm = self.sess.codemap();
769 let filename = cm.span_to_filename(m.inner);
771 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Mod);
772 self.fmt.mod_str(item.span,
779 self.nest(item.id, |v| visit::walk_mod(v, m));
782 fn process_path(&mut self,
786 ref_kind: Option<recorder::Row>) {
787 if generated_code(span) {
791 let def_map = self.analysis.ty_cx.def_map.borrow();
792 if !def_map.contains_key(&id) {
793 self.sess.span_bug(span,
794 &format!("def_map has no key for {} in visit_expr", id));
796 let def = def_map.get(&id).unwrap().full_def();
797 let sub_span = self.span.span_for_last_ident(span);
803 def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef),
808 def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef,
813 def::DefTy(def_id, _) => self.fmt.ref_str(recorder::TypeRef,
818 def::DefMethod(declid, provenence) => {
819 let sub_span = self.span.sub_span_for_meth_name(span);
820 let defid = if declid.krate == ast::LOCAL_CRATE {
821 let ti = ty::impl_or_trait_item(&self.analysis.ty_cx,
824 def::FromTrait(def_id) => {
825 Some(ty::trait_items(&self.analysis.ty_cx,
829 mr.name() == ti.name()
834 def::FromImpl(def_id) => {
835 let impl_items = self.analysis
839 Some(impl_items.get(&def_id)
843 ty::impl_or_trait_item(
844 &self.analysis.ty_cx,
846 ).name() == ti.name()
855 self.fmt.meth_call_str(span,
861 def::DefFn(def_id, _) => {
862 self.fmt.fn_call_str(span,
867 _ => self.sess.span_bug(span,
868 &format!("Unexpected def kind while looking \
869 up path in `{}`: `{:?}`",
870 self.span.snippet(span),
873 // modules or types in the path prefix
875 def::DefMethod(did, _) => {
876 let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, did);
877 if let ty::MethodTraitItem(m) = ti {
878 if m.explicit_self == ty::StaticExplicitSelfCategory {
879 self.write_sub_path_trait_truncated(path);
884 def::DefStatic(_,_) |
887 def::DefVariant(..) |
888 def::DefFn(..) => self.write_sub_paths_truncated(path, false),
893 fn process_struct_lit(&mut self,
896 fields: &Vec<ast::Field>,
897 base: &Option<P<ast::Expr>>) {
898 if generated_code(path.span) {
902 self.write_sub_paths_truncated(path, false);
904 let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, ex).sty;
905 let struct_def = match *ty {
906 ty::ty_struct(def_id, _) => {
907 let sub_span = self.span.span_for_last_ident(path.span);
908 self.fmt.ref_str(recorder::StructRef,
918 for field in fields {
920 Some(struct_def) => {
921 let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
923 if generated_code(field.ident.span) {
926 if f.name == field.ident.node.name {
927 // We don't really need a sub-span here, but no harm done
928 let sub_span = self.span.span_for_last_ident(field.ident.span);
929 self.fmt.ref_str(recorder::VarRef,
940 self.visit_expr(&*field.expr)
942 visit::walk_expr_opt(self, base)
945 fn process_method_call(&mut self,
947 args: &Vec<P<ast::Expr>>) {
948 let method_map = self.analysis.ty_cx.method_map.borrow();
949 let method_callee = method_map.get(&ty::MethodCall::expr(ex.id)).unwrap();
950 let (def_id, decl_id) = match method_callee.origin {
951 ty::MethodStatic(def_id) |
952 ty::MethodStaticClosure(def_id) => {
953 // method invoked on an object with a concrete type (not a static method)
955 match ty::trait_item_of_item(&self.analysis.ty_cx,
958 Some(decl_id) => Some(decl_id.def_id()),
961 // This incantation is required if the method referenced is a
962 // trait's default implementation.
963 let def_id = match ty::impl_or_trait_item(&self.analysis
966 ty::MethodTraitItem(method) => {
967 method.provided_source.unwrap_or(def_id)
969 ty::TypeTraitItem(_) => def_id,
971 (Some(def_id), decl_id)
973 ty::MethodTypeParam(ref mp) => {
974 // method invoked on a type parameter
975 let trait_item = ty::trait_item(&self.analysis.ty_cx,
978 (None, Some(trait_item.def_id()))
980 ty::MethodTraitObject(ref mo) => {
981 // method invoked on a trait instance
982 let trait_item = ty::trait_item(&self.analysis.ty_cx,
985 (None, Some(trait_item.def_id()))
988 let sub_span = self.span.sub_span_for_meth_name(ex.span);
989 self.fmt.meth_call_str(ex.span,
995 // walk receiver and args
996 visit::walk_exprs(self, &args[..]);
999 fn process_pat(&mut self, p:&ast::Pat) {
1000 if generated_code(p.span) {
1005 ast::PatStruct(ref path, ref fields, _) => {
1006 self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef));
1007 visit::walk_path(self, path);
1009 let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def();
1010 let struct_def = match def {
1011 def::DefConst(..) => None,
1012 def::DefVariant(_, variant_id, _) => Some(variant_id),
1014 match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) {
1016 self.sess.span_bug(p.span,
1017 &format!("Could not find struct_def for `{}`",
1018 self.span.snippet(p.span)));
1020 Some(def_id) => Some(def_id),
1025 if let Some(struct_def) = struct_def {
1026 let struct_fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
1027 for &Spanned { node: ref field, span } in fields {
1028 let sub_span = self.span.span_for_first_ident(span);
1029 for f in &struct_fields {
1030 if f.name == field.ident.name {
1031 self.fmt.ref_str(recorder::VarRef,
1039 self.visit_pat(&*field.pat);
1043 ast::PatEnum(ref path, _) => {
1044 self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
1045 visit::walk_pat(self, p);
1047 ast::PatIdent(bm, ref path1, ref optional_subpattern) => {
1048 let immut = match bm {
1049 // Even if the ref is mut, you can't change the ref, only
1050 // the data pointed at, so showing the initialising expression
1051 // is still worthwhile.
1052 ast::BindByRef(_) => true,
1053 ast::BindByValue(mt) => {
1055 ast::MutMutable => false,
1056 ast::MutImmutable => true,
1060 // collect path for either visit_local or visit_arm
1061 let path = ast_util::ident_to_path(path1.span,path1.node);
1062 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
1063 match *optional_subpattern {
1065 Some(ref subpattern) => self.visit_pat(&**subpattern)
1068 _ => visit::walk_pat(self, p)
1073 impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
1074 fn visit_item(&mut self, item: &ast::Item) {
1075 if generated_code(item.span) {
1080 ast::ItemUse(ref use_item) => {
1081 match use_item.node {
1082 ast::ViewPathSimple(ident, ref path) => {
1083 let sub_span = self.span.span_for_last_ident(path.span);
1084 let mod_id = match self.lookup_type_ref(item.id) {
1086 match self.lookup_def_kind(item.id, path.span) {
1087 Some(kind) => self.fmt.ref_str(kind,
1099 // 'use' always introduces an alias, if there is not an explicit
1100 // one, there is an implicit one.
1102 match self.span.sub_span_after_keyword(use_item.span, keywords::As) {
1103 Some(sub_span) => Some(sub_span),
1107 self.fmt.use_alias_str(path.span,
1113 self.write_sub_paths_truncated(path, true);
1115 ast::ViewPathGlob(ref path) => {
1116 // Make a comma-separated list of names of imported modules.
1117 let mut name_string = String::new();
1118 let glob_map = &self.analysis.glob_map;
1119 let glob_map = glob_map.as_ref().unwrap();
1120 if glob_map.contains_key(&item.id) {
1121 for n in glob_map.get(&item.id).unwrap() {
1122 if name_string.len() > 0 {
1123 name_string.push_str(", ");
1125 name_string.push_str(n.as_str());
1129 let sub_span = self.span.sub_span_of_token(path.span,
1130 token::BinOp(token::Star));
1131 self.fmt.use_glob_str(path.span,
1136 self.write_sub_paths(path, true);
1138 ast::ViewPathList(ref path, ref list) => {
1141 ast::PathListIdent { id, .. } => {
1142 match self.lookup_type_ref(id) {
1144 match self.lookup_def_kind(id, plid.span) {
1149 def_id, self.cur_scope);
1156 ast::PathListMod { .. } => ()
1160 self.write_sub_paths(path, true);
1164 ast::ItemExternCrate(ref s) => {
1165 let name = get_ident(item.ident);
1167 let location = match *s {
1168 Some(s) => s.to_string(),
1169 None => name.to_string(),
1171 let alias_span = self.span.span_for_last_ident(item.span);
1172 let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(item.id) {
1176 self.fmt.extern_crate_str(item.span,
1184 ast::ItemFn(ref decl, _, _, ref ty_params, ref body) =>
1185 self.process_fn(item, &**decl, ty_params, &**body),
1186 ast::ItemStatic(ref typ, mt, ref expr) =>
1187 self.process_static(item, &**typ, mt, &**expr),
1188 ast::ItemConst(ref typ, ref expr) =>
1189 self.process_const(item, &**typ, &**expr),
1190 ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params),
1191 ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
1196 ref impl_items) => {
1197 self.process_impl(item,
1203 ast::ItemTrait(_, ref generics, ref trait_refs, ref methods) =>
1204 self.process_trait(item, generics, trait_refs, methods),
1205 ast::ItemMod(ref m) => self.process_mod(item, m),
1206 ast::ItemTy(ref ty, ref ty_params) => {
1207 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
1208 let value = ty_to_string(&**ty);
1209 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type);
1210 self.fmt.typedef_str(item.span,
1216 self.visit_ty(&**ty);
1217 self.process_generic_params(ty_params, item.span, &qualname, item.id);
1219 ast::ItemMac(_) => (),
1220 _ => visit::walk_item(self, item),
1224 fn visit_generics(&mut self, generics: &ast::Generics) {
1225 for param in &*generics.ty_params {
1226 for bound in &*param.bounds {
1227 if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
1228 self.process_trait_ref(&trait_ref.trait_ref);
1231 if let Some(ref ty) = param.default {
1232 self.visit_ty(&**ty);
1237 fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
1238 match trait_item.node {
1239 ast::MethodTraitItem(ref sig, ref body) => {
1240 self.process_method(sig, body.as_ref().map(|x| &**x),
1241 trait_item.id, trait_item.ident, trait_item.span);
1243 ast::TypeTraitItem(..) => {}
1247 fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
1248 match impl_item.node {
1249 ast::MethodImplItem(ref sig, ref body) => {
1250 self.process_method(sig, Some(body), impl_item.id,
1251 impl_item.ident, impl_item.span);
1253 ast::TypeImplItem(_) |
1254 ast::MacImplItem(_) => {}
1258 fn visit_ty(&mut self, t: &ast::Ty) {
1259 if generated_code(t.span) {
1264 ast::TyPath(_, ref path) => {
1265 match self.lookup_type_ref(t.id) {
1267 let sub_span = self.span.sub_span_for_type_name(t.span);
1268 self.fmt.ref_str(recorder::TypeRef,
1277 self.write_sub_paths_truncated(path, false);
1279 visit::walk_path(self, path);
1281 _ => visit::walk_ty(self, t),
1285 fn visit_expr(&mut self, ex: &ast::Expr) {
1286 if generated_code(ex.span) {
1291 ast::ExprCall(ref _f, ref _args) => {
1292 // Don't need to do anything for function calls,
1293 // because just walking the callee path does what we want.
1294 visit::walk_expr(self, ex);
1296 ast::ExprPath(_, ref path) => {
1297 self.process_path(ex.id, path.span, path, None);
1298 visit::walk_expr(self, ex);
1300 ast::ExprStruct(ref path, ref fields, ref base) =>
1301 self.process_struct_lit(ex, path, fields, base),
1302 ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args),
1303 ast::ExprField(ref sub_ex, ident) => {
1304 if generated_code(sub_ex.span) {
1308 self.visit_expr(&**sub_ex);
1309 let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty;
1311 ty::ty_struct(def_id, _) => {
1312 let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id);
1314 if f.name == ident.node.name {
1315 let sub_span = self.span.span_for_last_ident(ex.span);
1316 self.fmt.ref_str(recorder::VarRef,
1325 _ => self.sess.span_bug(ex.span,
1326 &format!("Expected struct type, found {:?}", ty)),
1329 ast::ExprTupField(ref sub_ex, idx) => {
1330 if generated_code(sub_ex.span) {
1334 self.visit_expr(&**sub_ex);
1336 let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty;
1338 ty::ty_struct(def_id, _) => {
1339 let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id);
1340 for (i, f) in fields.iter().enumerate() {
1342 let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
1343 self.fmt.ref_str(recorder::VarRef,
1353 _ => self.sess.span_bug(ex.span,
1354 &format!("Expected struct or tuple \
1355 type, found {:?}", ty)),
1358 ast::ExprClosure(_, ref decl, ref body) => {
1359 if generated_code(body.span) {
1363 let mut id = String::from_str("$");
1364 id.push_str(&ex.id.to_string());
1365 self.process_formals(&decl.inputs, &id[..]);
1367 // walk arg and return types
1368 for arg in &decl.inputs {
1369 self.visit_ty(&*arg.ty);
1372 if let ast::Return(ref ret_ty) = decl.output {
1373 self.visit_ty(&**ret_ty);
1377 self.nest(ex.id, |v| v.visit_block(&**body));
1380 visit::walk_expr(self, ex)
1385 fn visit_mac(&mut self, _: &ast::Mac) {
1386 // Just stop, macros are poison to us.
1389 fn visit_pat(&mut self, p: &ast::Pat) {
1390 self.process_pat(p);
1391 if !self.collecting {
1392 self.collected_paths.clear();
1396 fn visit_arm(&mut self, arm: &ast::Arm) {
1397 assert!(self.collected_paths.len() == 0 && !self.collecting);
1398 self.collecting = true;
1399 for pattern in &arm.pats {
1400 // collect paths from the arm's patterns
1401 self.visit_pat(&**pattern);
1404 // This is to get around borrow checking, because we need mut self to call process_path.
1405 let mut paths_to_process = vec![];
1406 // process collected paths
1407 for &(id, ref p, ref immut, ref_kind) in &self.collected_paths {
1408 let def_map = self.analysis.ty_cx.def_map.borrow();
1409 if !def_map.contains_key(&id) {
1410 self.sess.span_bug(p.span,
1411 &format!("def_map has no key for {} in visit_arm",
1414 let def = def_map.get(&id).unwrap().full_def();
1416 def::DefLocal(id) => {
1417 let value = if *immut {
1418 self.span.snippet(p.span).to_string()
1420 "<mutable>".to_string()
1423 assert!(p.segments.len() == 1, "qualified path for local variable def in arm");
1424 self.fmt.variable_str(p.span,
1431 def::DefVariant(..) | def::DefTy(..) | def::DefStruct(..) => {
1432 paths_to_process.push((id, p.clone(), Some(ref_kind)))
1434 // FIXME(nrc) what are these doing here?
1435 def::DefStatic(_, _) => {}
1436 def::DefConst(..) => {}
1437 _ => error!("unexpected definition kind when processing collected paths: {:?}",
1441 for &(id, ref path, ref_kind) in &paths_to_process {
1442 self.process_path(id, path.span, path, ref_kind);
1444 self.collecting = false;
1445 self.collected_paths.clear();
1446 visit::walk_expr_opt(self, &arm.guard);
1447 self.visit_expr(&*arm.body);
1450 fn visit_stmt(&mut self, s: &ast::Stmt) {
1451 if generated_code(s.span) {
1455 visit::walk_stmt(self, s)
1458 fn visit_local(&mut self, l: &ast::Local) {
1459 if generated_code(l.span) {
1463 // The local could declare multiple new vars, we must walk the
1464 // pattern and collect them all.
1465 assert!(self.collected_paths.len() == 0 && !self.collecting);
1466 self.collecting = true;
1467 self.visit_pat(&*l.pat);
1468 self.collecting = false;
1470 let value = self.span.snippet(l.span);
1472 for &(id, ref p, ref immut, _) in &self.collected_paths {
1473 let value = if *immut { value.to_string() } else { "<mutable>".to_string() };
1474 let types = self.analysis.ty_cx.node_types.borrow();
1475 let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap());
1476 // Get the span only for the name of the variable (I hope the path
1477 // is only ever a variable name, but who knows?).
1478 let sub_span = self.span.span_for_last_ident(p.span);
1479 // Rust uses the id of the pattern for var lookups, so we'll use it too.
1480 self.fmt.variable_str(p.span,
1487 self.collected_paths.clear();
1489 // Just walk the initialiser and type (don't want to walk the pattern again).
1490 visit::walk_ty_opt(self, &l.ty);
1491 visit::walk_expr_opt(self, &l.init);
1495 #[allow(deprecated)]
1496 pub fn process_crate(sess: &Session,
1498 analysis: &ty::CrateAnalysis,
1499 odir: Option<&Path>) {
1500 if generated_code(krate.span) {
1504 assert!(analysis.glob_map.is_some());
1505 let cratename = match attr::find_crate_name(&krate.attrs) {
1506 Some(name) => name.to_string(),
1508 info!("Could not find crate name, using 'unknown_crate'");
1509 String::from_str("unknown_crate")
1513 info!("Dumping crate {}", cratename);
1515 // find a path to dump our data to
1516 let mut root_path = match env::var_os("DXR_RUST_TEMP_FOLDER") {
1517 Some(val) => PathBuf::from(val),
1518 None => match odir {
1519 Some(val) => val.join("dxr"),
1520 None => PathBuf::from("dxr-temp"),
1524 match fs::create_dir_all(&root_path) {
1525 Err(e) => sess.err(&format!("Could not create directory {}: {}",
1526 root_path.display(), e)),
1531 let disp = root_path.display();
1532 info!("Writing output to {}", disp);
1535 // Create output file.
1536 let mut out_name = cratename.clone();
1537 out_name.push_str(".csv");
1538 root_path.push(&out_name);
1539 let output_file = match File::create(&root_path) {
1542 let disp = root_path.display();
1543 sess.fatal(&format!("Could not open {}: {}", disp, e));
1548 let mut visitor = DxrVisitor {
1551 collected_paths: vec!(),
1553 fmt: FmtStrs::new(box Recorder {
1559 err_count: Cell::new(0)
1563 err_count: Cell::new(0)
1568 visitor.dump_crate_info(&cratename[..], krate);
1570 visit::walk_crate(&mut visitor, krate);