]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/save/dump_csv.rs
d3493251e2f42c3d33deb8c68e120def99fc83c4
[rust.git] / src / librustc_trans / save / dump_csv.rs
1 // Copyright 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.
4 //
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.
10
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.
15 //!
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
22 //! idents.
23 //!
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 //! DumpCsvVisitor walks the AST and processes it.
29
30 use super::{escape, generated_code, recorder, SaveContext};
31
32 use session::Session;
33
34 use middle::def;
35 use middle::ty::{self, Ty};
36
37 use std::cell::Cell;
38 use std::fs::File;
39 use std::path::Path;
40
41 use syntax::ast_util;
42 use syntax::ast::{self, NodeId, DefId};
43 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};
49 use syntax::ptr::P;
50
51 use super::span_utils::SpanUtils;
52 use super::recorder::{Recorder, FmtStrs};
53
54 use util::ppaux;
55
56
57 pub struct DumpCsvVisitor<'l, 'tcx: 'l> {
58     save_ctxt: SaveContext<'l, 'tcx>,
59     sess: &'l Session,
60     analysis: &'l ty::CrateAnalysis<'tcx>,
61
62     collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>,
63     collecting: bool,
64
65     span: SpanUtils<'l>,
66     fmt: FmtStrs<'l>,
67
68     cur_scope: NodeId
69 }
70
71 impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
72     pub fn new(sess: &'l Session,
73                analysis: &'l ty::CrateAnalysis<'tcx>,
74                output_file: Box<File>) -> DumpCsvVisitor<'l, 'tcx> {
75         DumpCsvVisitor {
76             sess: sess,
77             save_ctxt: SaveContext::new(sess, analysis, SpanUtils {
78                 sess: sess,
79                 err_count: Cell::new(0)
80             }),
81             analysis: analysis,
82             collected_paths: vec![],
83             collecting: false,
84             span: SpanUtils {
85                 sess: sess,
86                 err_count: Cell::new(0)
87             },
88             fmt: FmtStrs::new(box Recorder {
89                                 out: output_file,
90                                 dump_spans: false,
91                             },
92                             SpanUtils {
93                                 sess: sess,
94                                 err_count: Cell::new(0)
95                             }),
96             cur_scope: 0            
97         }
98     }
99
100     fn nest<F>(&mut self, scope_id: NodeId, f: F) where
101         F: FnOnce(&mut DumpCsvVisitor<'l, 'tcx>),
102     {
103         let parent_scope = self.cur_scope;
104         self.cur_scope = scope_id;
105         f(self);
106         self.cur_scope = parent_scope;
107     }
108
109     pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
110         // The current crate.
111         self.fmt.crate_str(krate.span, name);
112
113         // Dump info about all the external crates referenced from this crate.
114         for c in &self.save_ctxt.get_external_crates() {
115             self.fmt.external_crate_str(krate.span, &c.name, c.number);            
116         }
117         self.fmt.recorder.record("end_external_crates\n");
118     }
119
120     // Return all non-empty prefixes of a path.
121     // For each prefix, we return the span for the last segment in the prefix and
122     // a str representation of the entire prefix.
123     fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> {
124         let spans = self.span.spans_for_path_segments(path);
125
126         // Paths to enums seem to not match their spans - the span includes all the
127         // variants too. But they seem to always be at the end, so I hope we can cope with
128         // always using the first ones. So, only error out if we don't have enough spans.
129         // What could go wrong...?
130         if spans.len() < path.segments.len() {
131             error!("Mis-calculated spans for path '{}'. \
132                     Found {} spans, expected {}. Found spans:",
133                    path_to_string(path), spans.len(), path.segments.len());
134             for s in &spans {
135                 let loc = self.sess.codemap().lookup_char_pos(s.lo);
136                 error!("    '{}' in {}, line {}",
137                        self.span.snippet(*s), loc.file.name, loc.line);
138             }
139             return vec!();
140         }
141
142         let mut result: Vec<(Span, String)> = vec!();
143
144         let mut segs = vec!();
145         for (i, (seg, span)) in path.segments.iter().zip(spans.iter()).enumerate() {
146             segs.push(seg.clone());
147             let sub_path = ast::Path{span: *span, // span for the last segment
148                                      global: path.global,
149                                      segments: segs};
150             let qualname = if i == 0 && path.global {
151                 format!("::{}", path_to_string(&sub_path))
152             } else {
153                 path_to_string(&sub_path)
154             };
155             result.push((*span, qualname));
156             segs = sub_path.segments;
157         }
158
159         result
160     }
161
162     // The global arg allows us to override the global-ness of the path (which
163     // actually means 'does the path start with `::`', rather than 'is the path
164     // semantically global). We use the override for `use` imports (etc.) where
165     // the syntax is non-global, but the semantics are global.
166     fn write_sub_paths(&mut self, path: &ast::Path, global: bool) {
167         let sub_paths = self.process_path_prefixes(path);
168         for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
169             let qualname = if i == 0 && global && !path.global {
170                 format!("::{}", qualname)
171             } else {
172                 qualname.clone()
173             };
174             self.fmt.sub_mod_ref_str(path.span,
175                                      *span,
176                                      &qualname[..],
177                                      self.cur_scope);
178         }
179     }
180
181     // As write_sub_paths, but does not process the last ident in the path (assuming it
182     // will be processed elsewhere). See note on write_sub_paths about global.
183     fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) {
184         let sub_paths = self.process_path_prefixes(path);
185         let len = sub_paths.len();
186         if len <= 1 {
187             return;
188         }
189
190         let sub_paths = &sub_paths[..len-1];
191         for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() {
192             let qualname = if i == 0 && global && !path.global {
193                 format!("::{}", qualname)
194             } else {
195                 qualname.clone()
196             };
197             self.fmt.sub_mod_ref_str(path.span,
198                                      *span,
199                                      &qualname[..],
200                                      self.cur_scope);
201         }
202     }
203
204     // As write_sub_paths, but expects a path of the form module_path::trait::method
205     // Where trait could actually be a struct too.
206     fn write_sub_path_trait_truncated(&mut self, path: &ast::Path) {
207         let sub_paths = self.process_path_prefixes(path);
208         let len = sub_paths.len();
209         if len <= 1 {
210             return;
211         }
212         let sub_paths = &sub_paths[.. (len-1)];
213
214         // write the trait part of the sub-path
215         let (ref span, ref qualname) = sub_paths[len-2];
216         self.fmt.sub_type_ref_str(path.span,
217                                   *span,
218                                   &qualname[..]);
219
220         // write the other sub-paths
221         if len <= 2 {
222             return;
223         }
224         let sub_paths = &sub_paths[..len-2];
225         for &(ref span, ref qualname) in sub_paths {
226             self.fmt.sub_mod_ref_str(path.span,
227                                      *span,
228                                      &qualname[..],
229                                      self.cur_scope);
230         }
231     }
232
233     // looks up anything, not just a type
234     fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
235         if !self.analysis.ty_cx.def_map.borrow().contains_key(&ref_id) {
236             self.sess.bug(&format!("def_map has no key for {} in lookup_type_ref",
237                                   ref_id));
238         }
239         let def = self.analysis.ty_cx.def_map.borrow().get(&ref_id).unwrap().full_def();
240         match def {
241             def::DefPrimTy(_) => None,
242             _ => Some(def.def_id()),
243         }
244     }
245
246     fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option<recorder::Row> {
247         let def_map = self.analysis.ty_cx.def_map.borrow();
248         if !def_map.contains_key(&ref_id) {
249             self.sess.span_bug(span, &format!("def_map has no key for {} in lookup_def_kind",
250                                              ref_id));
251         }
252         let def = def_map.get(&ref_id).unwrap().full_def();
253         match def {
254             def::DefMod(_) |
255             def::DefForeignMod(_) => Some(recorder::ModRef),
256             def::DefStruct(_) => Some(recorder::StructRef),
257             def::DefTy(..) |
258             def::DefAssociatedTy(..) |
259             def::DefTrait(_) => Some(recorder::TypeRef),
260             def::DefStatic(_, _) |
261             def::DefConst(_) |
262             def::DefAssociatedConst(..) |
263             def::DefLocal(_) |
264             def::DefVariant(_, _, _) |
265             def::DefUpvar(..) => Some(recorder::VarRef),
266
267             def::DefFn(..) => Some(recorder::FnRef),
268
269             def::DefSelfTy(..) |
270             def::DefRegion(_) |
271             def::DefLabel(_) |
272             def::DefTyParam(..) |
273             def::DefUse(_) |
274             def::DefMethod(..) |
275             def::DefPrimTy(_) => {
276                 self.sess.span_bug(span, &format!("lookup_def_kind for unexpected item: {:?}",
277                                                  def));
278             },
279         }
280     }
281
282     fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str) {
283         for arg in formals {
284             assert!(self.collected_paths.is_empty() && !self.collecting);
285             self.collecting = true;
286             self.visit_pat(&*arg.pat);
287             self.collecting = false;
288             let span_utils = self.span.clone();
289             for &(id, ref p, _, _) in &self.collected_paths {
290                 let typ =
291                     ppaux::ty_to_string(
292                         &self.analysis.ty_cx,
293                         *self.analysis.ty_cx.node_types().get(&id).unwrap());
294                 // get the span only for the name of the variable (I hope the path is only ever a
295                 // variable name, but who knows?)
296                 self.fmt.formal_str(p.span,
297                                     span_utils.span_for_last_ident(p.span),
298                                     id,
299                                     qualname,
300                                     &path_to_string(p),
301                                     &typ[..]);
302             }
303             self.collected_paths.clear();
304         }
305     }
306
307     fn process_method(&mut self, sig: &ast::MethodSig,
308                       body: Option<&ast::Block>,
309                       id: ast::NodeId, name: ast::Name,
310                       span: Span) {
311         if generated_code(span) {
312             return;
313         }
314
315         debug!("process_method: {}:{}", id, token::get_name(name));
316
317         let mut scope_id;
318         // The qualname for a method is the trait name or name of the struct in an impl in
319         // which the method is declared in, followed by the method's name.
320         let qualname = match ty::impl_of_method(&self.analysis.ty_cx,
321                                                 ast_util::local_def(id)) {
322             Some(impl_id) => match self.analysis.ty_cx.map.get(impl_id.node) {
323                 NodeItem(item) => {
324                     scope_id = item.id;
325                     match item.node {
326                         ast::ItemImpl(_, _, _, _, ref ty, _) => {
327                             let mut result = String::from_str("<");
328                             result.push_str(&ty_to_string(&**ty));
329
330                             match ty::trait_of_item(&self.analysis.ty_cx,
331                                                     ast_util::local_def(id)) {
332                                 Some(def_id) => {
333                                     result.push_str(" as ");
334                                     result.push_str(
335                                         &ty::item_path_str(&self.analysis.ty_cx, def_id));
336                                 },
337                                 None => {}
338                             }
339                             result.push_str(">");
340                             result
341                         }
342                         _ => {
343                             self.sess.span_bug(span,
344                                 &format!("Container {} for method {} not an impl?",
345                                          impl_id.node, id));
346                         },
347                     }
348                 },
349                 _ => {
350                     self.sess.span_bug(span,
351                         &format!("Container {} for method {} is not a node item {:?}",
352                                  impl_id.node, id, self.analysis.ty_cx.map.get(impl_id.node)));
353                 },
354             },
355             None => match ty::trait_of_item(&self.analysis.ty_cx,
356                                             ast_util::local_def(id)) {
357                 Some(def_id) => {
358                     scope_id = def_id.node;
359                     match self.analysis.ty_cx.map.get(def_id.node) {
360                         NodeItem(_) => {
361                             format!("::{}", ty::item_path_str(&self.analysis.ty_cx, def_id))
362                         }
363                         _ => {
364                             self.sess.span_bug(span,
365                                 &format!("Could not find container {} for method {}",
366                                          def_id.node, id));
367                         }
368                     }
369                 },
370                 None => {
371                     self.sess.span_bug(span,
372                         &format!("Could not find container for method {}", id));
373                 },
374             },
375         };
376
377         let qualname = &format!("{}::{}", qualname, &token::get_name(name));
378
379         // record the decl for this def (if it has one)
380         let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx,
381                                              ast_util::local_def(id))
382             .and_then(|new_id| {
383                 let def_id = new_id.def_id();
384                 if def_id.node != 0 && def_id != ast_util::local_def(id) {
385                     Some(def_id)
386                 } else {
387                     None
388                 }
389             });
390
391         let sub_span = self.span.sub_span_after_keyword(span, keywords::Fn);
392         if body.is_some() {
393             self.fmt.method_str(span,
394                                 sub_span,
395                                 id,
396                                 qualname,
397                                 decl_id,
398                                 scope_id);
399             self.process_formals(&sig.decl.inputs, qualname);
400         } else {
401             self.fmt.method_decl_str(span,
402                                      sub_span,
403                                      id,
404                                      qualname,
405                                      scope_id);
406         }
407
408         // walk arg and return types
409         for arg in &sig.decl.inputs {
410             self.visit_ty(&arg.ty);
411         }
412
413         if let ast::Return(ref ret_ty) = sig.decl.output {
414             self.visit_ty(ret_ty);
415         }
416
417         // walk the fn body
418         if let Some(body) = body {
419             self.nest(id, |v| v.visit_block(body));
420         }
421
422         self.process_generic_params(&sig.generics,
423                                     span,
424                                     qualname,
425                                     id);
426     }
427
428     fn process_trait_ref(&mut self,
429                          trait_ref: &ast::TraitRef) {
430         match self.lookup_type_ref(trait_ref.ref_id) {
431             Some(id) => {
432                 let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
433                 self.fmt.ref_str(recorder::TypeRef,
434                                  trait_ref.path.span,
435                                  sub_span,
436                                  id,
437                                  self.cur_scope);
438                 visit::walk_path(self, &trait_ref.path);
439             },
440             None => ()
441         }
442     }
443
444     fn process_struct_field_def(&mut self,
445                                 field: &ast::StructField,
446                                 qualname: &str,
447                                 scope_id: NodeId) {
448         match field.node.kind {
449             ast::NamedField(ident, _) => {
450                 let name = get_ident(ident);
451                 let qualname = format!("{}::{}", qualname, name);
452                 let typ =
453                     ppaux::ty_to_string(
454                         &self.analysis.ty_cx,
455                         *self.analysis.ty_cx.node_types().get(&field.node.id).unwrap());
456                 match self.span.sub_span_before_token(field.span, token::Colon) {
457                     Some(sub_span) => self.fmt.field_str(field.span,
458                                                          Some(sub_span),
459                                                          field.node.id,
460                                                          &name[..],
461                                                          &qualname[..],
462                                                          &typ[..],
463                                                          scope_id),
464                     None => self.sess.span_bug(field.span,
465                                                &format!("Could not find sub-span for field {}",
466                                                        qualname)),
467                 }
468             },
469             _ => (),
470         }
471     }
472
473     // Dump generic params bindings, then visit_generics
474     fn process_generic_params(&mut self,
475                               generics:&ast::Generics,
476                               full_span: Span,
477                               prefix: &str,
478                               id: NodeId) {
479         // We can't only use visit_generics since we don't have spans for param
480         // bindings, so we reparse the full_span to get those sub spans.
481         // However full span is the entire enum/fn/struct block, so we only want
482         // the first few to match the number of generics we're looking for.
483         let param_sub_spans = self.span.spans_for_ty_params(full_span,
484                                                            (generics.ty_params.len() as isize));
485         for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans.iter()) {
486             // Append $id to name to make sure each one is unique
487             let name = format!("{}::{}${}",
488                                prefix,
489                                escape(self.span.snippet(*param_ss)),
490                                id);
491             self.fmt.typedef_str(full_span,
492                                  Some(*param_ss),
493                                  param.id,
494                                  &name[..],
495                                  "");
496         }
497         self.visit_generics(generics);
498     }
499
500     fn process_fn(&mut self,
501                   item: &ast::Item,
502                   decl: &ast::FnDecl,
503                   ty_params: &ast::Generics,
504                   body: &ast::Block) {
505         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
506
507         let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn);
508         self.fmt.fn_str(item.span,
509                         sub_span,
510                         item.id,
511                         &qualname[..],
512                         self.cur_scope);
513
514         self.process_formals(&decl.inputs, &qualname[..]);
515
516         // walk arg and return types
517         for arg in &decl.inputs {
518             self.visit_ty(&*arg.ty);
519         }
520
521         if let ast::Return(ref ret_ty) = decl.output {
522             self.visit_ty(&**ret_ty);
523         }
524
525         // walk the body
526         self.nest(item.id, |v| v.visit_block(&*body));
527
528         self.process_generic_params(ty_params, item.span, &qualname[..], item.id);
529     }
530
531     fn process_static(&mut self,
532                       item: &ast::Item,
533                       typ: &ast::Ty,
534                       mt: ast::Mutability,
535                       expr: &ast::Expr)
536     {
537         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
538
539         // If the variable is immutable, save the initialising expression.
540         let (value, keyword) = match mt {
541             ast::MutMutable => (String::from_str("<mutable>"), keywords::Mut),
542             ast::MutImmutable => (self.span.snippet(expr.span), keywords::Static),
543         };
544
545         let sub_span = self.span.sub_span_after_keyword(item.span, keyword);
546         self.fmt.static_str(item.span,
547                             sub_span,
548                             item.id,
549                             &get_ident(item.ident),
550                             &qualname[..],
551                             &value[..],
552                             &ty_to_string(&*typ),
553                             self.cur_scope);
554
555         // walk type and init value
556         self.visit_ty(&*typ);
557         self.visit_expr(expr);
558     }
559
560     fn process_const(&mut self,
561                      id: ast::NodeId,
562                      ident: &ast::Ident,
563                      span: Span,
564                      typ: &ast::Ty,
565                      expr: &ast::Expr)
566     {
567         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(id));
568
569         let sub_span = self.span.sub_span_after_keyword(span,
570                                                         keywords::Const);
571         self.fmt.static_str(span,
572                             sub_span,
573                             id,
574                             &get_ident((*ident).clone()),
575                             &qualname[..],
576                             "",
577                             &ty_to_string(&*typ),
578                             self.cur_scope);
579
580         // walk type and init value
581         self.visit_ty(typ);
582         self.visit_expr(expr);
583     }
584
585     fn process_struct(&mut self,
586                       item: &ast::Item,
587                       def: &ast::StructDef,
588                       ty_params: &ast::Generics) {
589         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
590
591         let ctor_id = match def.ctor_id {
592             Some(node_id) => node_id,
593             None => -1,
594         };
595         let val = self.span.snippet(item.span);
596         let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
597         self.fmt.struct_str(item.span,
598                             sub_span,
599                             item.id,
600                             ctor_id,
601                             &qualname[..],
602                             self.cur_scope,
603                             &val[..]);
604
605         // fields
606         for field in &def.fields {
607             self.process_struct_field_def(field, &qualname[..], item.id);
608             self.visit_ty(&*field.node.ty);
609         }
610
611         self.process_generic_params(ty_params, item.span, &qualname[..], item.id);
612     }
613
614     fn process_enum(&mut self,
615                     item: &ast::Item,
616                     enum_definition: &ast::EnumDef,
617                     ty_params: &ast::Generics) {
618         let enum_name = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
619         let val = self.span.snippet(item.span);
620         match self.span.sub_span_after_keyword(item.span, keywords::Enum) {
621             Some(sub_span) => self.fmt.enum_str(item.span,
622                                                 Some(sub_span),
623                                                 item.id,
624                                                 &enum_name[..],
625                                                 self.cur_scope,
626                                                 &val[..]),
627             None => self.sess.span_bug(item.span,
628                                        &format!("Could not find subspan for enum {}",
629                                                enum_name)),
630         }
631         for variant in &enum_definition.variants {
632             let name = get_ident(variant.node.name);
633             let name = &name;
634             let mut qualname = enum_name.clone();
635             qualname.push_str("::");
636             qualname.push_str(name);
637             let val = self.span.snippet(variant.span);
638             match variant.node.kind {
639                 ast::TupleVariantKind(ref args) => {
640                     // first ident in span is the variant's name
641                     self.fmt.tuple_variant_str(variant.span,
642                                                self.span.span_for_first_ident(variant.span),
643                                                variant.node.id,
644                                                name,
645                                                &qualname[..],
646                                                &enum_name[..],
647                                                &val[..],
648                                                item.id);
649                     for arg in args {
650                         self.visit_ty(&*arg.ty);
651                     }
652                 }
653                 ast::StructVariantKind(ref struct_def) => {
654                     let ctor_id = match struct_def.ctor_id {
655                         Some(node_id) => node_id,
656                         None => -1,
657                     };
658                     self.fmt.struct_variant_str(
659                         variant.span,
660                         self.span.span_for_first_ident(variant.span),
661                         variant.node.id,
662                         ctor_id,
663                         &qualname[..],
664                         &enum_name[..],
665                         &val[..],
666                         item.id);
667
668                     for field in &struct_def.fields {
669                         self.process_struct_field_def(field, &qualname, variant.node.id);
670                         self.visit_ty(&*field.node.ty);
671                     }
672                 }
673             }
674         }
675
676         self.process_generic_params(ty_params, item.span, &enum_name[..], item.id);
677     }
678
679     fn process_impl(&mut self,
680                     item: &ast::Item,
681                     type_parameters: &ast::Generics,
682                     trait_ref: &Option<ast::TraitRef>,
683                     typ: &ast::Ty,
684                     impl_items: &[P<ast::ImplItem>]) {
685         let trait_id = trait_ref.as_ref().and_then(|tr| self.lookup_type_ref(tr.ref_id));
686         match typ.node {
687             // Common case impl for a struct or something basic.
688             ast::TyPath(None, ref path) => {
689                 let sub_span = self.span.sub_span_for_type_name(path.span);
690                 let self_id = self.lookup_type_ref(typ.id).map(|id| {
691                     self.fmt.ref_str(recorder::TypeRef,
692                                      path.span,
693                                      sub_span,
694                                      id,
695                                      self.cur_scope);
696                     id
697                 });
698                 self.fmt.impl_str(path.span,
699                                   sub_span,
700                                   item.id,
701                                   self_id,
702                                   trait_id,
703                                   self.cur_scope);
704             },
705             _ => {
706                 // Less useful case, impl for a compound type.
707                 self.visit_ty(&*typ);
708
709                 let sub_span = self.span.sub_span_for_type_name(typ.span);
710                 self.fmt.impl_str(typ.span,
711                                   sub_span,
712                                   item.id,
713                                   None,
714                                   trait_id,
715                                   self.cur_scope);
716             }
717         }
718
719         match *trait_ref {
720             Some(ref trait_ref) => self.process_trait_ref(trait_ref),
721             None => (),
722         }
723
724         self.process_generic_params(type_parameters, item.span, "", item.id);
725         for impl_item in impl_items {
726             self.visit_impl_item(impl_item);
727         }
728     }
729
730     fn process_trait(&mut self,
731                      item: &ast::Item,
732                      generics: &ast::Generics,
733                      trait_refs: &OwnedSlice<ast::TyParamBound>,
734                      methods: &[P<ast::TraitItem>]) {
735         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
736         let val = self.span.snippet(item.span);
737         let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait);
738         self.fmt.trait_str(item.span,
739                            sub_span,
740                            item.id,
741                            &qualname[..],
742                            self.cur_scope,
743                            &val[..]);
744
745         // super-traits
746         for super_bound in &**trait_refs {
747             let trait_ref = match *super_bound {
748                 ast::TraitTyParamBound(ref trait_ref, _) => {
749                     trait_ref
750                 }
751                 ast::RegionTyParamBound(..) => {
752                     continue;
753                 }
754             };
755
756             let trait_ref = &trait_ref.trait_ref;
757             match self.lookup_type_ref(trait_ref.ref_id) {
758                 Some(id) => {
759                     let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
760                     self.fmt.ref_str(recorder::TypeRef,
761                                      trait_ref.path.span,
762                                      sub_span,
763                                      id,
764                                      self.cur_scope);
765                     self.fmt.inherit_str(trait_ref.path.span,
766                                          sub_span,
767                                          id,
768                                          item.id);
769                 },
770                 None => ()
771             }
772         }
773
774         // walk generics and methods
775         self.process_generic_params(generics, item.span, &qualname[..], item.id);
776         for method in methods {
777             self.visit_trait_item(method)
778         }
779     }
780
781     fn process_mod(&mut self,
782                    item: &ast::Item,  // The module in question, represented as an item.
783                    m: &ast::Mod) {
784         let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
785
786         let cm = self.sess.codemap();
787         let filename = cm.span_to_filename(m.inner);
788
789         let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Mod);
790         self.fmt.mod_str(item.span,
791                          sub_span,
792                          item.id,
793                          &qualname[..],
794                          self.cur_scope,
795                          &filename[..]);
796
797         self.nest(item.id, |v| visit::walk_mod(v, m));
798     }
799
800     fn process_path(&mut self,
801                     id: NodeId,
802                     span: Span,
803                     path: &ast::Path,
804                     ref_kind: Option<recorder::Row>) {
805         if generated_code(span) {
806             return
807         }
808
809         let def_map = self.analysis.ty_cx.def_map.borrow();
810         if !def_map.contains_key(&id) {
811             self.sess.span_bug(span,
812                                &format!("def_map has no key for {} in visit_expr", id));
813         }
814         let def = def_map.get(&id).unwrap().full_def();
815         let sub_span = self.span.span_for_last_ident(span);
816         match def {
817             def::DefUpvar(..) |
818             def::DefLocal(..) |
819             def::DefStatic(..) |
820             def::DefConst(..) |
821             def::DefAssociatedConst(..) |
822             def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef),
823                                                     span,
824                                                     sub_span,
825                                                     def.def_id(),
826                                                     self.cur_scope),
827             def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef,
828                                                        span,
829                                                        sub_span,
830                                                        def_id,
831                                                        self.cur_scope),
832             def::DefTy(def_id, _) => self.fmt.ref_str(recorder::TypeRef,
833                                                       span,
834                                                       sub_span,
835                                                       def_id,
836                                                       self.cur_scope),
837             def::DefMethod(declid, provenence) => {
838                 let sub_span = self.span.sub_span_for_meth_name(span);
839                 let defid = if declid.krate == ast::LOCAL_CRATE {
840                     let ti = ty::impl_or_trait_item(&self.analysis.ty_cx,
841                                                     declid);
842                     match provenence {
843                         def::FromTrait(def_id) => {
844                             Some(ty::trait_items(&self.analysis.ty_cx,
845                                                  def_id)
846                                     .iter()
847                                     .find(|mr| {
848                                         mr.name() == ti.name()
849                                     })
850                                     .unwrap()
851                                     .def_id())
852                         }
853                         def::FromImpl(def_id) => {
854                             let impl_items = self.analysis
855                                                  .ty_cx
856                                                  .impl_items
857                                                  .borrow();
858                             Some(impl_items.get(&def_id)
859                                            .unwrap()
860                                            .iter()
861                                            .find(|mr| {
862                                                 ty::impl_or_trait_item(
863                                                     &self.analysis.ty_cx,
864                                                     mr.def_id()
865                                                 ).name() == ti.name()
866                                             })
867                                            .unwrap()
868                                            .def_id())
869                         }
870                     }
871                 } else {
872                     None
873                 };
874                 self.fmt.meth_call_str(span,
875                                        sub_span,
876                                        defid,
877                                        Some(declid),
878                                        self.cur_scope);
879             },
880             def::DefFn(def_id, _) => {
881                 self.fmt.fn_call_str(span,
882                                      sub_span,
883                                      def_id,
884                                      self.cur_scope)
885             }
886             _ => self.sess.span_bug(span,
887                                     &format!("Unexpected def kind while looking \
888                                               up path in `{}`: `{:?}`",
889                                              self.span.snippet(span),
890                                              def)),
891         }
892         // modules or types in the path prefix
893         match def {
894             def::DefMethod(did, _) => {
895                 let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, did);
896                 if let ty::MethodTraitItem(m) = ti {
897                     if m.explicit_self == ty::StaticExplicitSelfCategory {
898                         self.write_sub_path_trait_truncated(path);
899                     }
900                 }
901             }
902             def::DefLocal(_) |
903             def::DefStatic(_,_) |
904             def::DefConst(..) |
905             def::DefAssociatedConst(..) |
906             def::DefStruct(_) |
907             def::DefVariant(..) |
908             def::DefFn(..) => self.write_sub_paths_truncated(path, false),
909             _ => {},
910         }
911     }
912
913     fn process_struct_lit(&mut self,
914                           ex: &ast::Expr,
915                           path: &ast::Path,
916                           fields: &Vec<ast::Field>,
917                           base: &Option<P<ast::Expr>>) {
918         if generated_code(path.span) {
919             return
920         }
921
922         self.write_sub_paths_truncated(path, false);
923
924         let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, ex).sty;
925         let struct_def = match *ty {
926             ty::ty_struct(def_id, _) => {
927                 let sub_span = self.span.span_for_last_ident(path.span);
928                 self.fmt.ref_str(recorder::StructRef,
929                                  path.span,
930                                  sub_span,
931                                  def_id,
932                                  self.cur_scope);
933                 Some(def_id)
934             }
935             _ => None
936         };
937
938         for field in fields {
939             match struct_def {
940                 Some(struct_def) => {
941                     let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
942                     for f in &fields {
943                         if generated_code(field.ident.span) {
944                             continue;
945                         }
946                         if f.name == field.ident.node.name {
947                             // We don't really need a sub-span here, but no harm done
948                             let sub_span = self.span.span_for_last_ident(field.ident.span);
949                             self.fmt.ref_str(recorder::VarRef,
950                                              field.ident.span,
951                                              sub_span,
952                                              f.id,
953                                              self.cur_scope);
954                         }
955                     }
956                 }
957                 None => {}
958             }
959
960             self.visit_expr(&*field.expr)
961         }
962         visit::walk_expr_opt(self, base)
963     }
964
965     fn process_method_call(&mut self,
966                            ex: &ast::Expr,
967                            args: &Vec<P<ast::Expr>>) {
968         let method_map = self.analysis.ty_cx.method_map.borrow();
969         let method_callee = method_map.get(&ty::MethodCall::expr(ex.id)).unwrap();
970         let (def_id, decl_id) = match method_callee.origin {
971             ty::MethodStatic(def_id) |
972             ty::MethodStaticClosure(def_id) => {
973                 // method invoked on an object with a concrete type (not a static method)
974                 let decl_id =
975                     match ty::trait_item_of_item(&self.analysis.ty_cx,
976                                                  def_id) {
977                         None => None,
978                         Some(decl_id) => Some(decl_id.def_id()),
979                     };
980
981                 // This incantation is required if the method referenced is a
982                 // trait's default implementation.
983                 let def_id = match ty::impl_or_trait_item(&self.analysis
984                                                                .ty_cx,
985                                                           def_id) {
986                     ty::MethodTraitItem(method) => {
987                         method.provided_source.unwrap_or(def_id)
988                     }
989                     _ => self.sess
990                              .span_bug(ex.span,
991                                        "save::process_method_call: non-method \
992                                         DefId in MethodStatic or MethodStaticClosure"),
993                 };
994                 (Some(def_id), decl_id)
995             }
996             ty::MethodTypeParam(ref mp) => {
997                 // method invoked on a type parameter
998                 let trait_item = ty::trait_item(&self.analysis.ty_cx,
999                                                 mp.trait_ref.def_id,
1000                                                 mp.method_num);
1001                 (None, Some(trait_item.def_id()))
1002             }
1003             ty::MethodTraitObject(ref mo) => {
1004                 // method invoked on a trait instance
1005                 let trait_item = ty::trait_item(&self.analysis.ty_cx,
1006                                                 mo.trait_ref.def_id,
1007                                                 mo.method_num);
1008                 (None, Some(trait_item.def_id()))
1009             }
1010         };
1011         let sub_span = self.span.sub_span_for_meth_name(ex.span);
1012         self.fmt.meth_call_str(ex.span,
1013                                sub_span,
1014                                def_id,
1015                                decl_id,
1016                                self.cur_scope);
1017
1018         // walk receiver and args
1019         visit::walk_exprs(self, &args[..]);
1020     }
1021
1022     fn process_pat(&mut self, p:&ast::Pat) {
1023         if generated_code(p.span) {
1024             return
1025         }
1026
1027         match p.node {
1028             ast::PatStruct(ref path, ref fields, _) => {
1029                 self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef));
1030                 visit::walk_path(self, path);
1031
1032                 let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def();
1033                 let struct_def = match def {
1034                     def::DefConst(..) | def::DefAssociatedConst(..) => None,
1035                     def::DefVariant(_, variant_id, _) => Some(variant_id),
1036                     _ => {
1037                         match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) {
1038                             None => {
1039                                 self.sess.span_bug(p.span,
1040                                                    &format!("Could not find struct_def for `{}`",
1041                                                             self.span.snippet(p.span)));
1042                             }
1043                             Some(def_id) => Some(def_id),
1044                         }
1045                     }
1046                 };
1047
1048                 if let Some(struct_def) = struct_def {
1049                     let struct_fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
1050                     for &Spanned { node: ref field, span } in fields {
1051                         let sub_span = self.span.span_for_first_ident(span);
1052                         for f in &struct_fields {
1053                             if f.name == field.ident.name {
1054                                 self.fmt.ref_str(recorder::VarRef,
1055                                                  span,
1056                                                  sub_span,
1057                                                  f.id,
1058                                                  self.cur_scope);
1059                                 break;
1060                             }
1061                         }
1062                         self.visit_pat(&*field.pat);
1063                     }
1064                 }
1065             }
1066             ast::PatEnum(ref path, _) |
1067             ast::PatQPath(_, ref path) => {
1068                 self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
1069                 visit::walk_pat(self, p);
1070             }
1071             ast::PatIdent(bm, ref path1, ref optional_subpattern) => {
1072                 let immut = match bm {
1073                     // Even if the ref is mut, you can't change the ref, only
1074                     // the data pointed at, so showing the initialising expression
1075                     // is still worthwhile.
1076                     ast::BindByRef(_) => true,
1077                     ast::BindByValue(mt) => {
1078                         match mt {
1079                             ast::MutMutable => false,
1080                             ast::MutImmutable => true,
1081                         }
1082                     }
1083                 };
1084                 // collect path for either visit_local or visit_arm
1085                 let path = ast_util::ident_to_path(path1.span,path1.node);
1086                 self.collected_paths.push((p.id, path, immut, recorder::VarRef));
1087                 match *optional_subpattern {
1088                     None => {}
1089                     Some(ref subpattern) => self.visit_pat(&**subpattern)
1090                 }
1091             }
1092             _ => visit::walk_pat(self, p)
1093         }
1094     }
1095 }
1096
1097 impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
1098     fn visit_item(&mut self, item: &ast::Item) {
1099         if generated_code(item.span) {
1100             return
1101         }
1102
1103         match item.node {
1104             ast::ItemUse(ref use_item) => {
1105                 match use_item.node {
1106                     ast::ViewPathSimple(ident, ref path) => {
1107                         let sub_span = self.span.span_for_last_ident(path.span);
1108                         let mod_id = match self.lookup_type_ref(item.id) {
1109                             Some(def_id) => {
1110                                 match self.lookup_def_kind(item.id, path.span) {
1111                                     Some(kind) => self.fmt.ref_str(kind,
1112                                                                    path.span,
1113                                                                    sub_span,
1114                                                                    def_id,
1115                                                                    self.cur_scope),
1116                                     None => {},
1117                                 }
1118                                 Some(def_id)
1119                             },
1120                             None => None,
1121                         };
1122
1123                         // 'use' always introduces an alias, if there is not an explicit
1124                         // one, there is an implicit one.
1125                         let sub_span =
1126                             match self.span.sub_span_after_keyword(use_item.span, keywords::As) {
1127                                 Some(sub_span) => Some(sub_span),
1128                                 None => sub_span,
1129                             };
1130
1131                         self.fmt.use_alias_str(path.span,
1132                                                sub_span,
1133                                                item.id,
1134                                                mod_id,
1135                                                &get_ident(ident),
1136                                                self.cur_scope);
1137                         self.write_sub_paths_truncated(path, true);
1138                     }
1139                     ast::ViewPathGlob(ref path) => {
1140                         // Make a comma-separated list of names of imported modules.
1141                         let mut name_string = String::new();
1142                         let glob_map = &self.analysis.glob_map;
1143                         let glob_map = glob_map.as_ref().unwrap();
1144                         if glob_map.contains_key(&item.id) {
1145                             for n in glob_map.get(&item.id).unwrap() {
1146                                 if !name_string.is_empty() {
1147                                     name_string.push_str(", ");
1148                                 }
1149                                 name_string.push_str(n.as_str());
1150                             }
1151                         }
1152
1153                         let sub_span = self.span.sub_span_of_token(path.span,
1154                                                                    token::BinOp(token::Star));
1155                         self.fmt.use_glob_str(path.span,
1156                                               sub_span,
1157                                               item.id,
1158                                               &name_string,
1159                                               self.cur_scope);
1160                         self.write_sub_paths(path, true);
1161                     }
1162                     ast::ViewPathList(ref path, ref list) => {
1163                         for plid in list {
1164                             match plid.node {
1165                                 ast::PathListIdent { id, .. } => {
1166                                     match self.lookup_type_ref(id) {
1167                                         Some(def_id) =>
1168                                             match self.lookup_def_kind(id, plid.span) {
1169                                                 Some(kind) => {
1170                                                     self.fmt.ref_str(
1171                                                         kind, plid.span,
1172                                                         Some(plid.span),
1173                                                         def_id, self.cur_scope);
1174                                                 }
1175                                                 None => ()
1176                                             },
1177                                         None => ()
1178                                     }
1179                                 },
1180                                 ast::PathListMod { .. } => ()
1181                             }
1182                         }
1183
1184                         self.write_sub_paths(path, true);
1185                     }
1186                 }
1187             }
1188             ast::ItemExternCrate(ref s) => {
1189                 let name = get_ident(item.ident);
1190                 let name = &name;
1191                 let location = match *s {
1192                     Some(s) => s.to_string(),
1193                     None => name.to_string(),
1194                 };
1195                 let alias_span = self.span.span_for_last_ident(item.span);
1196                 let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(item.id) {
1197                     Some(cnum) => cnum,
1198                     None => 0,
1199                 };
1200                 self.fmt.extern_crate_str(item.span,
1201                                           alias_span,
1202                                           item.id,
1203                                           cnum,
1204                                           name,
1205                                           &location[..],
1206                                           self.cur_scope);
1207             }
1208             ast::ItemFn(ref decl, _, _, ref ty_params, ref body) =>
1209                 self.process_fn(item, &**decl, ty_params, &**body),
1210             ast::ItemStatic(ref typ, mt, ref expr) =>
1211                 self.process_static(item, &**typ, mt, &**expr),
1212             ast::ItemConst(ref typ, ref expr) =>
1213                 self.process_const(item.id, &item.ident, item.span, &*typ, &*expr),
1214             ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params),
1215             ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params),
1216             ast::ItemImpl(_, _,
1217                           ref ty_params,
1218                           ref trait_ref,
1219                           ref typ,
1220                           ref impl_items) => {
1221                 self.process_impl(item,
1222                                   ty_params,
1223                                   trait_ref,
1224                                   &**typ,
1225                                   impl_items)
1226             }
1227             ast::ItemTrait(_, ref generics, ref trait_refs, ref methods) =>
1228                 self.process_trait(item, generics, trait_refs, methods),
1229             ast::ItemMod(ref m) => self.process_mod(item, m),
1230             ast::ItemTy(ref ty, ref ty_params) => {
1231                 let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id));
1232                 let value = ty_to_string(&**ty);
1233                 let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type);
1234                 self.fmt.typedef_str(item.span,
1235                                      sub_span,
1236                                      item.id,
1237                                      &qualname[..],
1238                                      &value[..]);
1239
1240                 self.visit_ty(&**ty);
1241                 self.process_generic_params(ty_params, item.span, &qualname, item.id);
1242             },
1243             ast::ItemMac(_) => (),
1244             _ => visit::walk_item(self, item),
1245         }
1246     }
1247
1248     fn visit_generics(&mut self, generics: &ast::Generics) {
1249         for param in &*generics.ty_params {
1250             for bound in &*param.bounds {
1251                 if let ast::TraitTyParamBound(ref trait_ref, _) = *bound {
1252                     self.process_trait_ref(&trait_ref.trait_ref);
1253                 }
1254             }
1255             if let Some(ref ty) = param.default {
1256                 self.visit_ty(&**ty);
1257             }
1258         }
1259     }
1260
1261     fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
1262         match trait_item.node {
1263             ast::ConstTraitItem(ref ty, Some(ref expr)) => {
1264                 self.process_const(trait_item.id, &trait_item.ident,
1265                                    trait_item.span, &*ty, &*expr);
1266             }
1267             ast::MethodTraitItem(ref sig, ref body) => {
1268                 self.process_method(sig, body.as_ref().map(|x| &**x),
1269                                     trait_item.id, trait_item.ident.name, trait_item.span);
1270             }
1271             ast::ConstTraitItem(_, None) |
1272             ast::TypeTraitItem(..) => {}
1273         }
1274     }
1275
1276     fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
1277         match impl_item.node {
1278             ast::ConstImplItem(ref ty, ref expr) => {
1279                 self.process_const(impl_item.id, &impl_item.ident,
1280                                    impl_item.span, &ty, &expr);
1281             }
1282             ast::MethodImplItem(ref sig, ref body) => {
1283                 self.process_method(sig, Some(body), impl_item.id,
1284                                     impl_item.ident.name, impl_item.span);
1285             }
1286             ast::TypeImplItem(_) |
1287             ast::MacImplItem(_) => {}
1288         }
1289     }
1290
1291     fn visit_ty(&mut self, t: &ast::Ty) {
1292         if generated_code(t.span) {
1293             return
1294         }
1295
1296         match t.node {
1297             ast::TyPath(_, ref path) => {
1298                 match self.lookup_type_ref(t.id) {
1299                     Some(id) => {
1300                         let sub_span = self.span.sub_span_for_type_name(t.span);
1301                         self.fmt.ref_str(recorder::TypeRef,
1302                                          t.span,
1303                                          sub_span,
1304                                          id,
1305                                          self.cur_scope);
1306                     },
1307                     None => ()
1308                 }
1309
1310                 self.write_sub_paths_truncated(path, false);
1311
1312                 visit::walk_path(self, path);
1313             },
1314             _ => visit::walk_ty(self, t),
1315         }
1316     }
1317
1318     fn visit_expr(&mut self, ex: &ast::Expr) {
1319         if generated_code(ex.span) {
1320             return
1321         }
1322
1323         match ex.node {
1324             ast::ExprCall(ref _f, ref _args) => {
1325                 // Don't need to do anything for function calls,
1326                 // because just walking the callee path does what we want.
1327                 visit::walk_expr(self, ex);
1328             }
1329             ast::ExprPath(_, ref path) => {
1330                 self.process_path(ex.id, path.span, path, None);
1331                 visit::walk_expr(self, ex);
1332             }
1333             ast::ExprStruct(ref path, ref fields, ref base) =>
1334                 self.process_struct_lit(ex, path, fields, base),
1335             ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args),
1336             ast::ExprField(ref sub_ex, ident) => {
1337                 if generated_code(sub_ex.span) {
1338                     return
1339                 }
1340
1341                 self.visit_expr(&**sub_ex);
1342                 let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty;
1343                 match *ty {
1344                     ty::ty_struct(def_id, _) => {
1345                         let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id);
1346                         for f in &fields {
1347                             if f.name == ident.node.name {
1348                                 let sub_span = self.span.span_for_last_ident(ex.span);
1349                                 self.fmt.ref_str(recorder::VarRef,
1350                                                  ex.span,
1351                                                  sub_span,
1352                                                  f.id,
1353                                                  self.cur_scope);
1354                                 break;
1355                             }
1356                         }
1357                     }
1358                     _ => self.sess.span_bug(ex.span,
1359                                             &format!("Expected struct type, found {:?}", ty)),
1360                 }
1361             },
1362             ast::ExprTupField(ref sub_ex, idx) => {
1363                 if generated_code(sub_ex.span) {
1364                     return
1365                 }
1366
1367                 self.visit_expr(&**sub_ex);
1368
1369                 let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty;
1370                 match *ty {
1371                     ty::ty_struct(def_id, _) => {
1372                         let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id);
1373                         for (i, f) in fields.iter().enumerate() {
1374                             if i == idx.node {
1375                                 let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
1376                                 self.fmt.ref_str(recorder::VarRef,
1377                                                  ex.span,
1378                                                  sub_span,
1379                                                  f.id,
1380                                                  self.cur_scope);
1381                                 break;
1382                             }
1383                         }
1384                     }
1385                     ty::ty_tup(_) => {}
1386                     _ => self.sess.span_bug(ex.span,
1387                                             &format!("Expected struct or tuple \
1388                                                       type, found {:?}", ty)),
1389                 }
1390             },
1391             ast::ExprClosure(_, ref decl, ref body) => {
1392                 if generated_code(body.span) {
1393                     return
1394                 }
1395
1396                 let mut id = String::from_str("$");
1397                 id.push_str(&ex.id.to_string());
1398                 self.process_formals(&decl.inputs, &id[..]);
1399
1400                 // walk arg and return types
1401                 for arg in &decl.inputs {
1402                     self.visit_ty(&*arg.ty);
1403                 }
1404
1405                 if let ast::Return(ref ret_ty) = decl.output {
1406                     self.visit_ty(&**ret_ty);
1407                 }
1408
1409                 // walk the body
1410                 self.nest(ex.id, |v| v.visit_block(&**body));
1411             },
1412             _ => {
1413                 visit::walk_expr(self, ex)
1414             },
1415         }
1416     }
1417
1418     fn visit_mac(&mut self, _: &ast::Mac) {
1419         // Just stop, macros are poison to us.
1420     }
1421
1422     fn visit_pat(&mut self, p: &ast::Pat) {
1423         self.process_pat(p);
1424         if !self.collecting {
1425             self.collected_paths.clear();
1426         }
1427     }
1428
1429     fn visit_arm(&mut self, arm: &ast::Arm) {
1430         assert!(self.collected_paths.is_empty() && !self.collecting);
1431         self.collecting = true;
1432         for pattern in &arm.pats {
1433             // collect paths from the arm's patterns
1434             self.visit_pat(&**pattern);
1435         }
1436
1437         // This is to get around borrow checking, because we need mut self to call process_path.
1438         let mut paths_to_process = vec![];
1439         // process collected paths
1440         for &(id, ref p, ref immut, ref_kind) in &self.collected_paths {
1441             let def_map = self.analysis.ty_cx.def_map.borrow();
1442             if !def_map.contains_key(&id) {
1443                 self.sess.span_bug(p.span,
1444                                    &format!("def_map has no key for {} in visit_arm",
1445                                            id));
1446             }
1447             let def = def_map.get(&id).unwrap().full_def();
1448             match def {
1449                 def::DefLocal(id)  => {
1450                     let value = if *immut {
1451                         self.span.snippet(p.span).to_string()
1452                     } else {
1453                         "<mutable>".to_string()
1454                     };
1455
1456                     assert!(p.segments.len() == 1, "qualified path for local variable def in arm");
1457                     self.fmt.variable_str(p.span,
1458                                           Some(p.span),
1459                                           id,
1460                                           &path_to_string(p),
1461                                           &value[..],
1462                                           "")
1463                 }
1464                 def::DefVariant(..) | def::DefTy(..) | def::DefStruct(..) => {
1465                     paths_to_process.push((id, p.clone(), Some(ref_kind)))
1466                 }
1467                 // FIXME(nrc) what are these doing here?
1468                 def::DefStatic(_, _) |
1469                 def::DefConst(..) |
1470                 def::DefAssociatedConst(..) => {}
1471                 _ => error!("unexpected definition kind when processing collected paths: {:?}",
1472                             def)
1473             }
1474         }
1475         for &(id, ref path, ref_kind) in &paths_to_process {
1476             self.process_path(id, path.span, path, ref_kind);
1477         }
1478         self.collecting = false;
1479         self.collected_paths.clear();
1480         visit::walk_expr_opt(self, &arm.guard);
1481         self.visit_expr(&*arm.body);
1482     }
1483
1484     fn visit_stmt(&mut self, s: &ast::Stmt) {
1485         if generated_code(s.span) {
1486             return
1487         }
1488
1489         visit::walk_stmt(self, s)
1490     }
1491
1492     fn visit_local(&mut self, l: &ast::Local) {
1493         if generated_code(l.span) {
1494             return
1495         }
1496
1497         // The local could declare multiple new vars, we must walk the
1498         // pattern and collect them all.
1499         assert!(self.collected_paths.is_empty() && !self.collecting);
1500         self.collecting = true;
1501         self.visit_pat(&*l.pat);
1502         self.collecting = false;
1503
1504         let value = self.span.snippet(l.span);
1505
1506         for &(id, ref p, ref immut, _) in &self.collected_paths {
1507             let value = if *immut { value.to_string() } else { "<mutable>".to_string() };
1508             let types = self.analysis.ty_cx.node_types();
1509             let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap());
1510             // Get the span only for the name of the variable (I hope the path
1511             // is only ever a variable name, but who knows?).
1512             let sub_span = self.span.span_for_last_ident(p.span);
1513             // Rust uses the id of the pattern for var lookups, so we'll use it too.
1514             self.fmt.variable_str(p.span,
1515                                   sub_span,
1516                                   id,
1517                                   &path_to_string(p),
1518                                   &value[..],
1519                                   &typ[..]);
1520         }
1521         self.collected_paths.clear();
1522
1523         // Just walk the initialiser and type (don't want to walk the pattern again).
1524         visit::walk_ty_opt(self, &l.ty);
1525         visit::walk_expr_opt(self, &l.init);
1526     }
1527 }