--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Output a CSV file containing the output from rustc's analysis. The data is
+//! primarily designed to be used as input to the DXR tool, specifically its
+//! Rust plugin. It could also be used by IDEs or other code browsing, search, or
+//! cross-referencing tools.
+//!
+//! Dumping the analysis is implemented by walking the AST and getting a bunch of
+//! info out from all over the place. We use Def IDs to identify objects. The
+//! tricky part is getting syntactic (span, source text) and semantic (reference
+//! Def IDs) information for parts of expressions which the compiler has discarded.
+//! E.g., in a path `foo::bar::baz`, the compiler only keeps a span for the whole
+//! path and a reference to `baz`, but we want spans and references for all three
+//! idents.
+//!
+//! SpanUtils is used to manipulate spans. In particular, to extract sub-spans
+//! from spans (e.g., the span for `bar` from the above example path).
+//! Recorder is used for recording the output in csv format. FmtStrs separates
+//! the format of the output away from extracting it from the compiler.
+//! DxrVisitor walks the AST and processes it.
+
+use driver::driver::CrateAnalysis;
+use driver::session::Session;
+
+use middle::def;
+use middle::ty;
+use middle::typeck;
+
+use std::cell::Cell;
+use std::gc::Gc;
+use std::io;
+use std::io::File;
+use std::io::fs;
+use std::os;
+
+use syntax::ast;
+use syntax::ast_util;
+use syntax::ast::{NodeId,DefId};
+use syntax::ast_map::NodeItem;
+use syntax::attr;
+use syntax::codemap::*;
+use syntax::parse::token;
+use syntax::parse::token::{get_ident,keywords};
+use syntax::visit;
+use syntax::visit::Visitor;
+use syntax::print::pprust::{path_to_str,ty_to_str};
+
+use middle::save::span_utils::SpanUtils;
+use middle::save::recorder::Recorder;
+use middle::save::recorder::FmtStrs;
+
+use util::ppaux;
+
+mod span_utils;
+mod recorder;
+
+// Helper function to escape quotes in a string
+fn escape(s: String) -> String {
+ s.replace("\"", "\"\"")
+}
+
+// If the expression is a macro expansion or other generated code, run screaming and don't index.
+fn generated_code(span: Span) -> bool {
+ span.expn_info.is_some() || span == DUMMY_SP
+}
+
+struct DxrVisitor<'l> {
+ sess: &'l Session,
+ analysis: &'l CrateAnalysis,
+
+ collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>,
+ collecting: bool,
+
+ span: SpanUtils<'l>,
+ fmt: FmtStrs<'l>,
+}
+
+impl <'l> DxrVisitor<'l> {
+ fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
+ // the current crate
+ self.fmt.crate_str(krate.span, name);
+
+ // dump info about all the external crates referenced from this crate
+ self.sess.cstore.iter_crate_data(|n, cmd| {
+ self.fmt.external_crate_str(krate.span, cmd.name.as_slice(), n);
+ });
+ self.fmt.recorder.record("end_external_crates\n");
+ }
+
+ // Return all non-empty prefixes of a path.
+ // For each prefix, we return the span for the last segment in the prefix and
+ // a str representation of the entire prefix.
+ fn process_path_prefixes(&self, path: &ast::Path) -> Vec<(Span, String)> {
+ let spans = self.span.spans_for_path_segments(path);
+
+ // Paths to enums seem to not match their spans - the span includes all the
+ // variants too. But they seem to always be at the end, so I hope we can cope with
+ // always using the first ones. So, only error out if we don't have enough spans.
+ // What could go wrong...?
+ if spans.len() < path.segments.len() {
+ error!("Mis-calculated spans for path '{}'. \
+ Found {} spans, expected {}. Found spans:",
+ path_to_str(path), spans.len(), path.segments.len());
+ for s in spans.iter() {
+ let loc = self.sess.codemap().lookup_char_pos(s.lo);
+ error!(" '{}' in {}, line {}",
+ self.span.snippet(*s), loc.file.name, loc.line);
+ }
+ return vec!();
+ }
+
+ let mut result: Vec<(Span, String)> = vec!();
+
+
+ let mut segs = vec!();
+ for (seg, span) in path.segments.iter().zip(spans.iter()) {
+ segs.push(seg.clone());
+ let sub_path = ast::Path{span: *span, // span for the last segment
+ global: path.global,
+ segments: segs};
+ let qualname = path_to_str(&sub_path);
+ result.push((*span, qualname));
+ segs = sub_path.segments;
+ }
+
+ result
+ }
+
+ fn write_sub_paths(&mut self, path: &ast::Path, scope_id: NodeId) {
+ let sub_paths = self.process_path_prefixes(path);
+ for &(ref span, ref qualname) in sub_paths.iter() {
+ self.fmt.sub_mod_ref_str(path.span,
+ *span,
+ qualname.as_slice(),
+ scope_id);
+ }
+ }
+
+ // As write_sub_paths, but does not process the last ident in the path (assuming it
+ // will be processed elsewhere).
+ fn write_sub_paths_truncated(&mut self, path: &ast::Path, scope_id: NodeId) {
+ let sub_paths = self.process_path_prefixes(path);
+ let len = sub_paths.len();
+ if len <= 1 {
+ return;
+ }
+
+ let sub_paths = sub_paths.slice(0, len-1);
+ for &(ref span, ref qualname) in sub_paths.iter() {
+ self.fmt.sub_mod_ref_str(path.span,
+ *span,
+ qualname.as_slice(),
+ scope_id);
+ }
+ }
+
+ // As write_sub_paths, but expects a path of the form module_path::trait::method
+ // Where trait could actually be a struct too.
+ fn write_sub_path_trait_truncated(&mut self, path: &ast::Path, scope_id: NodeId) {
+ let sub_paths = self.process_path_prefixes(path);
+ let len = sub_paths.len();
+ if len <= 1 {
+ return;
+ }
+ let sub_paths = sub_paths.slice_to(len-1);
+
+ // write the trait part of the sub-path
+ let (ref span, ref qualname) = sub_paths[len-2];
+ self.fmt.sub_type_ref_str(path.span,
+ *span,
+ qualname.as_slice());
+
+ // write the other sub-paths
+ if len <= 2 {
+ return;
+ }
+ let sub_paths = sub_paths.slice(0, len-2);
+ for &(ref span, ref qualname) in sub_paths.iter() {
+ self.fmt.sub_mod_ref_str(path.span,
+ *span,
+ qualname.as_slice(),
+ scope_id);
+ }
+ }
+
+ // looks up anything, not just a type
+ fn lookup_type_ref(&self, ref_id: NodeId) -> Option<DefId> {
+ if !self.analysis.ty_cx.def_map.borrow().contains_key(&ref_id) {
+ self.sess.bug(format!("def_map has no key for {} in lookup_type_ref",
+ ref_id).as_slice());
+ }
+ let def = *self.analysis.ty_cx.def_map.borrow().get(&ref_id);
+ match def {
+ def::DefPrimTy(_) => None,
+ _ => Some(def.def_id()),
+ }
+ }
+
+ fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option<recorder::Row> {
+ let def_map = self.analysis.ty_cx.def_map.borrow();
+ if !def_map.contains_key(&ref_id) {
+ self.sess.span_bug(span, format!("def_map has no key for {} in lookup_def_kind",
+ ref_id).as_slice());
+ }
+ let def = *def_map.get(&ref_id);
+ match def {
+ def::DefMod(_) |
+ def::DefForeignMod(_) => Some(recorder::ModRef),
+ def::DefStruct(_) => Some(recorder::StructRef),
+ def::DefTy(_) |
+ def::DefTrait(_) => Some(recorder::TypeRef),
+ def::DefStatic(_, _) |
+ def::DefBinding(_, _) |
+ def::DefArg(_, _) |
+ def::DefLocal(_, _) |
+ def::DefVariant(_, _, _) |
+ def::DefUpvar(_, _, _, _) => Some(recorder::VarRef),
+
+ def::DefFn(_, _) => Some(recorder::FnRef),
+
+ def::DefSelfTy(_) |
+ def::DefRegion(_) |
+ def::DefTyParamBinder(_) |
+ def::DefLabel(_) |
+ def::DefStaticMethod(_, _, _) |
+ def::DefTyParam(_, _) |
+ def::DefUse(_) |
+ def::DefMethod(_, _) |
+ def::DefPrimTy(_) => {
+ self.sess.span_bug(span, format!("lookup_def_kind for unexpected item: {:?}",
+ def).as_slice());
+ },
+ }
+ }
+
+ fn process_formals(&mut self, formals: &Vec<ast::Arg>, qualname: &str, e:DxrVisitorEnv) {
+ for arg in formals.iter() {
+ assert!(self.collected_paths.len() == 0 && !self.collecting);
+ self.collecting = true;
+ self.visit_pat(&*arg.pat, e);
+ self.collecting = false;
+ let span_utils = self.span;
+ for &(id, ref p, _, _) in self.collected_paths.iter() {
+ let typ = ppaux::ty_to_str(&self.analysis.ty_cx,
+ *self.analysis.ty_cx.node_types.borrow().get(&(id as uint)));
+ // get the span only for the name of the variable (I hope the path is only ever a
+ // variable name, but who knows?)
+ self.fmt.formal_str(p.span,
+ span_utils.span_for_last_ident(p.span),
+ id,
+ qualname,
+ path_to_str(p).as_slice(),
+ typ.as_slice());
+ }
+ self.collected_paths.clear();
+ }
+ }
+
+ fn process_method(&mut self, method: &ast::Method, e:DxrVisitorEnv) {
+ if generated_code(method.span) {
+ return;
+ }
+
+ let mut scope_id;
+ // The qualname for a method is the trait name or name of the struct in an impl in
+ // which the method is declared in followed by the method's name.
+ let mut qualname = match ty::impl_of_method(&self.analysis.ty_cx,
+ ast_util::local_def(method.id)) {
+ Some(impl_id) => match self.analysis.ty_cx.map.get(impl_id.node) {
+ NodeItem(item) => {
+ scope_id = item.id;
+ match item.node {
+ ast::ItemImpl(_, _, ty, _) => {
+ let mut result = String::from_str("<");
+ result.push_str(ty_to_str(&*ty).as_slice());
+
+ match ty::trait_of_method(&self.analysis.ty_cx,
+ ast_util::local_def(method.id)) {
+ Some(def_id) => {
+ result.push_str(" as ");
+ result.push_str(
+ ty::item_path_str(&self.analysis.ty_cx, def_id).as_slice());
+ },
+ None => {}
+ }
+ result.append(">::")
+ }
+ _ => {
+ self.sess.span_bug(method.span,
+ format!("Container {} for method {} not an impl?",
+ impl_id.node, method.id).as_slice());
+ },
+ }
+ },
+ _ => {
+ self.sess.span_bug(method.span,
+ format!("Container {} for method {} is not a node item {:?}",
+ impl_id.node,
+ method.id,
+ self.analysis.ty_cx.map.get(impl_id.node)
+ ).as_slice());
+ },
+ },
+ None => match ty::trait_of_method(&self.analysis.ty_cx,
+ ast_util::local_def(method.id)) {
+ Some(def_id) => {
+ scope_id = def_id.node;
+ match self.analysis.ty_cx.map.get(def_id.node) {
+ NodeItem(_) => {
+ let result = ty::item_path_str(&self.analysis.ty_cx, def_id);
+ result.append("::")
+ }
+ _ => {
+ self.sess.span_bug(method.span,
+ format!("Could not find container {} for method {}",
+ def_id.node, method.id).as_slice());
+ }
+ }
+ },
+ None => {
+ self.sess.span_bug(method.span,
+ format!("Could not find container for method {}",
+ method.id).as_slice());
+ },
+ },
+ };
+
+ qualname.push_str(get_ident(method.ident).get());
+ let qualname = qualname.as_slice();
+
+ // record the decl for this def (if it has one)
+ let decl_id = ty::trait_method_of_method(&self.analysis.ty_cx,
+ ast_util::local_def(method.id))
+ .filtered(|def_id| method.id != 0 && def_id.node == 0);
+
+ let sub_span = self.span.sub_span_after_keyword(method.span, keywords::Fn);
+ self.fmt.method_str(method.span,
+ sub_span,
+ method.id,
+ qualname,
+ decl_id,
+ scope_id);
+
+ self.process_formals(&method.decl.inputs, qualname, e);
+
+ // walk arg and return types
+ for arg in method.decl.inputs.iter() {
+ self.visit_ty(&*arg.ty, e);
+ }
+ self.visit_ty(&*method.decl.output, e);
+ // walk the fn body
+ self.visit_block(&*method.body, DxrVisitorEnv::new_nested(method.id));
+
+ self.process_generic_params(&method.generics,
+ method.span,
+ qualname,
+ method.id,
+ e);
+ }
+
+ fn process_trait_ref(&mut self,
+ trait_ref: &ast::TraitRef,
+ e: DxrVisitorEnv,
+ impl_id: Option<NodeId>) {
+ match self.lookup_type_ref(trait_ref.ref_id) {
+ Some(id) => {
+ let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
+ self.fmt.ref_str(recorder::TypeRef,
+ trait_ref.path.span,
+ sub_span,
+ id,
+ e.cur_scope);
+ match impl_id {
+ Some(impl_id) => self.fmt.impl_str(trait_ref.path.span,
+ sub_span,
+ impl_id,
+ id,
+ e.cur_scope),
+ None => (),
+ }
+ visit::walk_path(self, &trait_ref.path, e);
+ },
+ None => ()
+ }
+ }
+
+ fn process_struct_field_def(&mut self,
+ field: &ast::StructField,
+ qualname: &str,
+ scope_id: NodeId) {
+ match field.node.kind {
+ ast::NamedField(ident, _) => {
+ let name = get_ident(ident);
+ let qualname = format!("{}::{}", qualname, name);
+ let typ = ppaux::ty_to_str(&self.analysis.ty_cx,
+ *self.analysis.ty_cx.node_types.borrow().get(&(field.node.id as uint)));
+ match self.span.sub_span_before_token(field.span, token::COLON) {
+ Some(sub_span) => self.fmt.field_str(field.span,
+ Some(sub_span),
+ field.node.id,
+ name.get().as_slice(),
+ qualname.as_slice(),
+ typ.as_slice(),
+ scope_id),
+ None => self.sess.span_bug(field.span,
+ format!("Could not find sub-span for field {}",
+ qualname).as_slice()),
+ }
+ },
+ _ => (),
+ }
+ }
+
+ // Dump generic params bindings, then visit_generics
+ fn process_generic_params(&mut self, generics:&ast::Generics,
+ full_span: Span,
+ prefix: &str,
+ id: NodeId,
+ e: DxrVisitorEnv) {
+ // We can't only use visit_generics since we don't have spans for param
+ // bindings, so we reparse the full_span to get those sub spans.
+ // However full span is the entire enum/fn/struct block, so we only want
+ // the first few to match the number of generics we're looking for.
+ let param_sub_spans = self.span.spans_for_ty_params(full_span,
+ (generics.ty_params.len() as int));
+ for (param, param_ss) in generics.ty_params.iter().zip(param_sub_spans.iter()) {
+ // Append $id to name to make sure each one is unique
+ let name = format!("{}::{}${}",
+ prefix,
+ escape(self.span.snippet(*param_ss)),
+ id);
+ self.fmt.typedef_str(full_span,
+ Some(*param_ss),
+ param.id,
+ name.as_slice(),
+ "");
+ }
+ self.visit_generics(generics, e);
+ }
+
+ fn process_fn(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ decl: ast::P<ast::FnDecl>,
+ ty_params: &ast::Generics,
+ body: ast::P<ast::Block>) {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn);
+ self.fmt.fn_str(item.span,
+ sub_span,
+ item.id,
+ qualname.as_slice(),
+ e.cur_scope);
+
+ self.process_formals(&decl.inputs, qualname.as_slice(), e);
+
+ // walk arg and return types
+ for arg in decl.inputs.iter() {
+ self.visit_ty(&*arg.ty, e);
+ }
+ self.visit_ty(&*decl.output, e);
+
+ // walk the body
+ self.visit_block(&*body, DxrVisitorEnv::new_nested(item.id));
+
+ self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id, e);
+ }
+
+ fn process_static(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ typ: ast::P<ast::Ty>,
+ mt: ast::Mutability,
+ expr: &ast::Expr)
+ {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+
+ // If the variable is immutable, save the initialising expresion.
+ let value = match mt {
+ ast::MutMutable => String::from_str("<mutable>"),
+ ast::MutImmutable => self.span.snippet(expr.span),
+ };
+
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Static);
+ self.fmt.static_str(item.span,
+ sub_span,
+ item.id,
+ get_ident(item.ident).get(),
+ qualname.as_slice(),
+ value.as_slice(),
+ ty_to_str(&*typ).as_slice(),
+ e.cur_scope);
+
+ // walk type and init value
+ self.visit_ty(&*typ, e);
+ self.visit_expr(expr, e);
+ }
+
+ fn process_struct(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ def: &ast::StructDef,
+ ty_params: &ast::Generics) {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+
+ let ctor_id = match def.ctor_id {
+ Some(node_id) => node_id,
+ None => -1,
+ };
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Struct);
+ self.fmt.struct_str(item.span,
+ sub_span,
+ item.id,
+ ctor_id,
+ qualname.as_slice(),
+ e.cur_scope);
+
+ // fields
+ for field in def.fields.iter() {
+ self.process_struct_field_def(field, qualname.as_slice(), item.id);
+ self.visit_ty(&*field.node.ty, e);
+ }
+
+ self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id, e);
+ }
+
+ fn process_enum(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ enum_definition: &ast::EnumDef,
+ ty_params: &ast::Generics) {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+ match self.span.sub_span_after_keyword(item.span, keywords::Enum) {
+ Some(sub_span) => self.fmt.enum_str(item.span,
+ Some(sub_span),
+ item.id,
+ qualname.as_slice(),
+ e.cur_scope),
+ None => self.sess.span_bug(item.span,
+ format!("Could not find subspan for enum {}",
+ qualname).as_slice()),
+ }
+ for variant in enum_definition.variants.iter() {
+ let name = get_ident(variant.node.name);
+ let name = name.get();
+ let qualname = qualname.clone().append("::").append(name);
+ let val = self.span.snippet(variant.span);
+ match variant.node.kind {
+ ast::TupleVariantKind(ref args) => {
+ // first ident in span is the variant's name
+ self.fmt.tuple_variant_str(variant.span,
+ self.span.span_for_first_ident(variant.span),
+ variant.node.id,
+ name,
+ qualname.as_slice(),
+ val.as_slice(),
+ item.id);
+ for arg in args.iter() {
+ self.visit_ty(&*arg.ty, e);
+ }
+ }
+ ast::StructVariantKind(ref struct_def) => {
+ let ctor_id = match struct_def.ctor_id {
+ Some(node_id) => node_id,
+ None => -1,
+ };
+ self.fmt.struct_variant_str(
+ variant.span,
+ self.span.span_for_first_ident(variant.span),
+ variant.node.id,
+ ctor_id,
+ qualname.as_slice(),
+ val.as_slice(),
+ item.id);
+
+ for field in struct_def.fields.iter() {
+ self.process_struct_field_def(field, qualname.as_slice(), variant.node.id);
+ self.visit_ty(&*field.node.ty, e);
+ }
+ }
+ }
+ }
+
+ self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id, e);
+ }
+
+ fn process_impl(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ type_parameters: &ast::Generics,
+ trait_ref: &Option<ast::TraitRef>,
+ typ: ast::P<ast::Ty>,
+ methods: &Vec<Gc<ast::Method>>) {
+ match typ.node {
+ ast::TyPath(ref path, _, id) => {
+ match self.lookup_type_ref(id) {
+ Some(id) => {
+ let sub_span = self.span.sub_span_for_type_name(path.span);
+ self.fmt.ref_str(recorder::TypeRef,
+ path.span,
+ sub_span,
+ id,
+ e.cur_scope);
+ self.fmt.impl_str(path.span,
+ sub_span,
+ item.id,
+ id,
+ e.cur_scope);
+ },
+ None => ()
+ }
+ },
+ _ => self.visit_ty(&*typ, e),
+ }
+
+ match *trait_ref {
+ Some(ref trait_ref) => self.process_trait_ref(trait_ref, e, Some(item.id)),
+ None => (),
+ }
+
+ self.process_generic_params(type_parameters, item.span, "", item.id, e);
+ for method in methods.iter() {
+ visit::walk_method_helper(self, &**method, e)
+ }
+ }
+
+ fn process_trait(&mut self,
+ item: &ast::Item,
+ e: DxrVisitorEnv,
+ generics: &ast::Generics,
+ trait_refs: &Vec<ast::TraitRef>,
+ methods: &Vec<ast::TraitMethod>) {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait);
+ self.fmt.trait_str(item.span,
+ sub_span,
+ item.id,
+ qualname.as_slice(),
+ e.cur_scope);
+
+ // super-traits
+ for trait_ref in trait_refs.iter() {
+ match self.lookup_type_ref(trait_ref.ref_id) {
+ Some(id) => {
+ let sub_span = self.span.sub_span_for_type_name(trait_ref.path.span);
+ self.fmt.ref_str(recorder::TypeRef,
+ trait_ref.path.span,
+ sub_span,
+ id,
+ e.cur_scope);
+ self.fmt.inherit_str(trait_ref.path.span,
+ sub_span,
+ id,
+ item.id);
+ },
+ None => ()
+ }
+ }
+
+ // walk generics and methods
+ self.process_generic_params(generics, item.span, qualname.as_slice(), item.id, e);
+ for method in methods.iter() {
+ self.visit_trait_method(method, e)
+ }
+ }
+
+ fn process_mod(&mut self,
+ item: &ast::Item, // The module in question, represented as an item.
+ e: DxrVisitorEnv,
+ m: &ast::Mod) {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+
+ let cm = self.sess.codemap();
+ let filename = cm.span_to_filename(m.inner);
+
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Mod);
+ self.fmt.mod_str(item.span,
+ sub_span,
+ item.id,
+ qualname.as_slice(),
+ e.cur_scope,
+ filename.as_slice());
+
+ visit::walk_mod(self, m, DxrVisitorEnv::new_nested(item.id));
+ }
+
+ fn process_path(&mut self,
+ ex: &ast::Expr,
+ e: DxrVisitorEnv,
+ path: &ast::Path) {
+ if generated_code(path.span) {
+ return
+ }
+
+ let def_map = self.analysis.ty_cx.def_map.borrow();
+ if !def_map.contains_key(&ex.id) {
+ self.sess.span_bug(ex.span,
+ format!("def_map has no key for {} in visit_expr",
+ ex.id).as_slice());
+ }
+ let def = def_map.get(&ex.id);
+ let sub_span = self.span.span_for_last_ident(ex.span);
+ match *def {
+ def::DefLocal(id, _) |
+ def::DefArg(id, _) |
+ def::DefUpvar(id, _, _, _) |
+ def::DefBinding(id, _) => self.fmt.ref_str(recorder::VarRef,
+ ex.span,
+ sub_span,
+ ast_util::local_def(id),
+ e.cur_scope),
+ def::DefStatic(def_id,_) |
+ def::DefVariant(_, def_id, _) => self.fmt.ref_str(recorder::VarRef,
+ ex.span,
+ sub_span,
+ def_id,
+ e.cur_scope),
+ def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef,
+ ex.span,
+ sub_span,
+ def_id,
+ e.cur_scope),
+ def::DefStaticMethod(declid, provenence, _) => {
+ let sub_span = self.span.sub_span_for_meth_name(ex.span);
+ let defid = if declid.krate == ast::LOCAL_CRATE {
+ let m = ty::method(&self.analysis.ty_cx, declid);
+ match provenence {
+ def::FromTrait(def_id) =>
+ Some(ty::trait_methods(&self.analysis.ty_cx, def_id)
+ .iter().find(|mr| mr.ident.name == m.ident.name).unwrap().def_id),
+ def::FromImpl(def_id) => {
+ let impl_methods = self.analysis.ty_cx.impl_methods.borrow();
+ Some(*impl_methods.get(&def_id)
+ .iter().find(|mr|
+ ty::method(
+ &self.analysis.ty_cx, **mr).ident.name == m.ident.name)
+ .unwrap())
+ }
+ }
+ } else {
+ None
+ };
+ self.fmt.meth_call_str(ex.span,
+ sub_span,
+ defid,
+ Some(declid),
+ e.cur_scope);
+ },
+ def::DefFn(def_id, _) => self.fmt.fn_call_str(ex.span,
+ sub_span,
+ def_id,
+ e.cur_scope),
+ _ => self.sess.span_bug(ex.span,
+ format!("Unexpected def kind while looking up path in '{}'",
+ self.span.snippet(ex.span)).as_slice()),
+ }
+ // modules or types in the path prefix
+ match *def {
+ def::DefStaticMethod(_, _, _) => {
+ self.write_sub_path_trait_truncated(path, e.cur_scope);
+ },
+ def::DefLocal(_, _) |
+ def::DefArg(_, _) |
+ def::DefStatic(_,_) |
+ def::DefStruct(_) |
+ def::DefFn(_, _) => self.write_sub_paths_truncated(path, e.cur_scope),
+ _ => {},
+ }
+
+ visit::walk_path(self, path, e);
+ }
+
+ fn process_struct_lit(&mut self,
+ ex: &ast::Expr,
+ e: DxrVisitorEnv,
+ path: &ast::Path,
+ fields: &Vec<ast::Field>,
+ base: Option<Gc<ast::Expr>>) {
+ if generated_code(path.span) {
+ return
+ }
+
+ let mut struct_def: Option<DefId> = None;
+ match self.lookup_type_ref(ex.id) {
+ Some(id) => {
+ struct_def = Some(id);
+ let sub_span = self.span.span_for_last_ident(path.span);
+ self.fmt.ref_str(recorder::StructRef,
+ path.span,
+ sub_span,
+ id,
+ e.cur_scope);
+ },
+ None => ()
+ }
+
+ self.write_sub_paths_truncated(path, e.cur_scope);
+
+ for field in fields.iter() {
+ match struct_def {
+ Some(struct_def) => {
+ let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
+ for f in fields.iter() {
+ if generated_code(field.ident.span) {
+ continue;
+ }
+ if f.name == field.ident.node.name {
+ // We don't really need a sub-span here, but no harm done
+ let sub_span = self.span.span_for_last_ident(field.ident.span);
+ self.fmt.ref_str(recorder::VarRef,
+ field.ident.span,
+ sub_span,
+ f.id,
+ e.cur_scope);
+ }
+ }
+ }
+ None => {}
+ }
+
+ self.visit_expr(&*field.expr, e)
+ }
+ visit::walk_expr_opt(self, base, e)
+ }
+
+ fn process_method_call(&mut self,
+ ex: &ast::Expr,
+ e: DxrVisitorEnv,
+ args: &Vec<Gc<ast::Expr>>) {
+ let method_map = self.analysis.ty_cx.method_map.borrow();
+ let method_callee = method_map.get(&typeck::MethodCall::expr(ex.id));
+ let (def_id, decl_id) = match method_callee.origin {
+ typeck::MethodStatic(def_id) => {
+ // method invoked on an object with a concrete type (not a static method)
+ let decl_id = ty::trait_method_of_method(&self.analysis.ty_cx, def_id);
+
+ // This incantation is required if the method referenced is a trait's
+ // defailt implementation.
+ let def_id = ty::method(&self.analysis.ty_cx, def_id).provided_source
+ .unwrap_or(def_id);
+ (Some(def_id), decl_id)
+ }
+ typeck::MethodParam(mp) => {
+ // method invoked on a type parameter
+ let method = ty::trait_method(&self.analysis.ty_cx,
+ mp.trait_id,
+ mp.method_num);
+ (None, Some(method.def_id))
+ },
+ typeck::MethodObject(mo) => {
+ // method invoked on a trait instance
+ let method = ty::trait_method(&self.analysis.ty_cx,
+ mo.trait_id,
+ mo.method_num);
+ (None, Some(method.def_id))
+ },
+ };
+ let sub_span = self.span.sub_span_for_meth_name(ex.span);
+ self.fmt.meth_call_str(ex.span,
+ sub_span,
+ def_id,
+ decl_id,
+ e.cur_scope);
+
+ // walk receiver and args
+ visit::walk_exprs(self, args.as_slice(), e);
+ }
+
+ fn process_pat(&mut self, p:&ast::Pat, e: DxrVisitorEnv) {
+ if generated_code(p.span) {
+ return
+ }
+
+ match p.node {
+ ast::PatStruct(ref path, ref fields, _) => {
+ self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef));
+ visit::walk_path(self, path, e);
+ let struct_def = match self.lookup_type_ref(p.id) {
+ Some(sd) => sd,
+ None => {
+ self.sess.span_bug(p.span,
+ format!("Could not find struct_def for `{}`",
+ self.span.snippet(p.span)).as_slice());
+ }
+ };
+ // The AST doesn't give us a span for the struct field, so we have
+ // to figure out where it is by assuming it's the token before each colon.
+ let field_spans = self.span.sub_spans_before_tokens(p.span,
+ token::COMMA,
+ token::COLON);
+ if fields.len() != field_spans.len() {
+ self.sess.span_bug(p.span,
+ format!("Mismatched field count in '{}', found {}, expected {}",
+ self.span.snippet(p.span), field_spans.len(), fields.len()
+ ).as_slice());
+ }
+ for (field, &span) in fields.iter().zip(field_spans.iter()) {
+ self.visit_pat(&*field.pat, e);
+ if span.is_none() {
+ continue;
+ }
+ let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def);
+ for f in fields.iter() {
+ if f.name == field.ident.name {
+ self.fmt.ref_str(recorder::VarRef,
+ p.span,
+ span,
+ f.id,
+ e.cur_scope);
+ break;
+ }
+ }
+ }
+ }
+ ast::PatEnum(ref path, _) => {
+ self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef));
+ visit::walk_pat(self, p, e);
+ }
+ ast::PatIdent(bm, ref path, ref optional_subpattern) => {
+ let immut = match bm {
+ // Even if the ref is mut, you can't change the ref, only
+ // the data pointed at, so showing the initialising expression
+ // is still worthwhile.
+ ast::BindByRef(_) => true,
+ ast::BindByValue(mt) => {
+ match mt {
+ ast::MutMutable => false,
+ ast::MutImmutable => true,
+ }
+ }
+ };
+ // collect path for either visit_local or visit_arm
+ self.collected_paths.push((p.id, path.clone(), immut, recorder::VarRef));
+ match *optional_subpattern {
+ None => {}
+ Some(subpattern) => self.visit_pat(&*subpattern, e),
+ }
+ }
+ _ => visit::walk_pat(self, p, e)
+ }
+ }
+}
+
+impl<'l> Visitor<DxrVisitorEnv> for DxrVisitor<'l> {
+ fn visit_item(&mut self, item:&ast::Item, e: DxrVisitorEnv) {
+ if generated_code(item.span) {
+ return
+ }
+
+ match item.node {
+ ast::ItemFn(decl, _, _, ref ty_params, body) =>
+ self.process_fn(item, e, decl, ty_params, body),
+ ast::ItemStatic(typ, mt, expr) =>
+ self.process_static(item, e, typ, mt, &*expr),
+ ast::ItemStruct(def, ref ty_params) => self.process_struct(item, e, &*def, ty_params),
+ ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, e, def, ty_params),
+ ast::ItemImpl(ref ty_params, ref trait_ref, typ, ref methods) =>
+ self.process_impl(item, e, ty_params, trait_ref, typ, methods),
+ ast::ItemTrait(ref generics, _, ref trait_refs, ref methods) =>
+ self.process_trait(item, e, generics, trait_refs, methods),
+ ast::ItemMod(ref m) => self.process_mod(item, e, m),
+ ast::ItemTy(ty, ref ty_params) => {
+ let qualname = self.analysis.ty_cx.map.path_to_str(item.id);
+ let value = ty_to_str(&*ty);
+ let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Type);
+ self.fmt.typedef_str(item.span,
+ sub_span,
+ item.id,
+ qualname.as_slice(),
+ value.as_slice());
+
+ self.visit_ty(&*ty, e);
+ self.process_generic_params(ty_params, item.span, qualname.as_slice(), item.id, e);
+ },
+ ast::ItemMac(_) => (),
+ _ => visit::walk_item(self, item, e),
+ }
+ }
+
+ fn visit_generics(&mut self, generics: &ast::Generics, e: DxrVisitorEnv) {
+ for param in generics.ty_params.iter() {
+ for bound in param.bounds.iter() {
+ match *bound {
+ ast::TraitTyParamBound(ref trait_ref) => {
+ self.process_trait_ref(trait_ref, e, None);
+ }
+ _ => {}
+ }
+ }
+ match param.default {
+ Some(ty) => self.visit_ty(&*ty, e),
+ None => (),
+ }
+ }
+ }
+
+ // We don't actually index functions here, that is done in visit_item/ItemFn.
+ // Here we just visit methods.
+ fn visit_fn(&mut self,
+ fk: &visit::FnKind,
+ fd: &ast::FnDecl,
+ b: &ast::Block,
+ s: Span,
+ _: NodeId,
+ e: DxrVisitorEnv) {
+ if generated_code(s) {
+ return;
+ }
+
+ match *fk {
+ visit::FkMethod(_, _, method) => self.process_method(method, e),
+ _ => visit::walk_fn(self, fk, fd, b, s, e),
+ }
+ }
+
+ fn visit_trait_method(&mut self, tm: &ast::TraitMethod, e: DxrVisitorEnv) {
+ match *tm {
+ ast::Required(ref method_type) => {
+ if generated_code(method_type.span) {
+ return;
+ }
+
+ let mut scope_id ;
+ let mut qualname = match ty::trait_of_method(&self.analysis.ty_cx,
+ ast_util::local_def(method_type.id)) {
+ Some(def_id) => {
+ scope_id = def_id.node;
+ ty::item_path_str(&self.analysis.ty_cx, def_id).append("::")
+ },
+ None => {
+ self.sess.span_bug(method_type.span,
+ format!("Could not find trait for method {}",
+ method_type.id).as_slice());
+ },
+ };
+
+ qualname.push_str(get_ident(method_type.ident).get());
+ let qualname = qualname.as_slice();
+
+ let sub_span = self.span.sub_span_after_keyword(method_type.span, keywords::Fn);
+ self.fmt.method_decl_str(method_type.span,
+ sub_span,
+ method_type.id,
+ qualname,
+ scope_id);
+
+ // walk arg and return types
+ for arg in method_type.decl.inputs.iter() {
+ self.visit_ty(&*arg.ty, e);
+ }
+ self.visit_ty(&*method_type.decl.output, e);
+
+ self.process_generic_params(&method_type.generics,
+ method_type.span,
+ qualname,
+ method_type.id,
+ e);
+ }
+ ast::Provided(method) => self.process_method(&*method, e),
+ }
+ }
+
+ fn visit_view_item(&mut self, i:&ast::ViewItem, e:DxrVisitorEnv) {
+ if generated_code(i.span) {
+ return
+ }
+
+ match i.node {
+ ast::ViewItemUse(ref path) => {
+ match path.node {
+ ast::ViewPathSimple(ident, ref path, id) => {
+ let sub_span = self.span.span_for_last_ident(path.span);
+ let mod_id = match self.lookup_type_ref(id) {
+ Some(def_id) => {
+ match self.lookup_def_kind(id, path.span) {
+ Some(kind) => self.fmt.ref_str(kind,
+ path.span,
+ sub_span,
+ def_id,
+ e.cur_scope),
+ None => {},
+ }
+ Some(def_id)
+ },
+ None => None,
+ };
+
+ // 'use' always introduces an alias, if there is not an explicit
+ // one, there is an implicit one.
+ let sub_span =
+ match self.span.sub_span_before_token(path.span, token::EQ) {
+ Some(sub_span) => Some(sub_span),
+ None => sub_span,
+ };
+
+ self.fmt.use_alias_str(path.span,
+ sub_span,
+ id,
+ mod_id,
+ get_ident(ident).get(),
+ e.cur_scope);
+ self.write_sub_paths_truncated(path, e.cur_scope);
+ }
+ ast::ViewPathGlob(ref path, _) => {
+ self.write_sub_paths(path, e.cur_scope);
+ }
+ ast::ViewPathList(ref path, ref list, _) => {
+ for plid in list.iter() {
+ match self.lookup_type_ref(plid.node.id) {
+ Some(id) => match self.lookup_def_kind(plid.node.id, plid.span) {
+ Some(kind) => self.fmt.ref_str(kind,
+ plid.span,
+ Some(plid.span),
+ id,
+ e.cur_scope),
+ None => (),
+ },
+ None => ()
+ }
+ }
+
+ self.write_sub_paths(path, e.cur_scope);
+ }
+ }
+ },
+ ast::ViewItemExternCrate(ident, ref s, id) => {
+ let name = get_ident(ident).get().to_owned();
+ let s = match *s {
+ Some((ref s, _)) => s.get().to_owned(),
+ None => name.to_owned(),
+ };
+ let sub_span = self.span.sub_span_after_keyword(i.span, keywords::Crate);
+ let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(id) {
+ Some(cnum) => cnum,
+ None => 0,
+ };
+ self.fmt.extern_crate_str(i.span,
+ sub_span,
+ id,
+ cnum,
+ name.as_slice(),
+ s.as_slice(),
+ e.cur_scope);
+ },
+ }
+ }
+
+ fn visit_ty(&mut self, t: &ast::Ty, e: DxrVisitorEnv) {
+ if generated_code(t.span) {
+ return
+ }
+
+ match t.node {
+ ast::TyPath(ref path, _, id) => {
+ match self.lookup_type_ref(id) {
+ Some(id) => {
+ let sub_span = self.span.sub_span_for_type_name(t.span);
+ self.fmt.ref_str(recorder::TypeRef,
+ t.span,
+ sub_span,
+ id,
+ e.cur_scope);
+ },
+ None => ()
+ }
+
+ self.write_sub_paths_truncated(path, e.cur_scope);
+
+ visit::walk_path(self, path, e);
+ },
+ _ => visit::walk_ty(self, t, e),
+ }
+ }
+
+ fn visit_expr(&mut self, ex: &ast::Expr, e: DxrVisitorEnv) {
+ if generated_code(ex.span) {
+ return
+ }
+
+ match ex.node {
+ ast::ExprCall(_f, ref _args) => {
+ // Don't need to do anything for function calls,
+ // because just walking the callee path does what we want.
+ visit::walk_expr(self, ex, e);
+ },
+ ast::ExprPath(ref path) => self.process_path(ex, e, path),
+ ast::ExprStruct(ref path, ref fields, base) =>
+ self.process_struct_lit(ex, e, path, fields, base),
+ ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, e, args),
+ ast::ExprField(sub_ex, ident, _) => {
+ if generated_code(sub_ex.span) {
+ return
+ }
+
+ self.visit_expr(&*sub_ex, e);
+
+ let t = ty::expr_ty_adjusted(&self.analysis.ty_cx, &*sub_ex);
+ let t_box = ty::get(t);
+ match t_box.sty {
+ ty::ty_struct(def_id, _) => {
+ let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id);
+ for f in fields.iter() {
+ if f.name == ident.name {
+ let sub_span = self.span.span_for_last_ident(ex.span);
+ self.fmt.ref_str(recorder::VarRef,
+ ex.span,
+ sub_span,
+ f.id,
+ e.cur_scope);
+ break;
+ }
+ }
+ },
+ _ => self.sess.span_bug(ex.span,
+ "Expected struct type, but not ty_struct"),
+ }
+ },
+ ast::ExprFnBlock(decl, body) => {
+ if generated_code(body.span) {
+ return
+ }
+
+ let id = String::from_str("$").append(ex.id.to_str().as_slice());
+ self.process_formals(&decl.inputs, id.as_slice(), e);
+
+ // walk arg and return types
+ for arg in decl.inputs.iter() {
+ self.visit_ty(&*arg.ty, e);
+ }
+ self.visit_ty(&*decl.output, e);
+
+ // walk the body
+ self.visit_block(&*body, DxrVisitorEnv::new_nested(ex.id));
+ },
+ _ => {
+ visit::walk_expr(self, ex, e)
+ },
+ }
+ }
+
+ fn visit_mac(&mut self, _: &ast::Mac, _: DxrVisitorEnv) {
+ // Just stop, macros are poison to us.
+ }
+
+ fn visit_pat(&mut self, p: &ast::Pat, e: DxrVisitorEnv) {
+ self.process_pat(p, e);
+ if !self.collecting {
+ self.collected_paths.clear();
+ }
+ }
+
+ fn visit_arm(&mut self, arm: &ast::Arm, e: DxrVisitorEnv) {
+ assert!(self.collected_paths.len() == 0 && !self.collecting);
+ self.collecting = true;
+
+ for pattern in arm.pats.iter() {
+ // collect paths from the arm's patterns
+ self.visit_pat(&**pattern, e);
+ }
+ self.collecting = false;
+ // process collected paths
+ for &(id, ref p, ref immut, ref_kind) in self.collected_paths.iter() {
+ let value = if *immut {
+ self.span.snippet(p.span).into_owned()
+ } else {
+ "<mutable>".to_owned()
+ };
+ let sub_span = self.span.span_for_first_ident(p.span);
+ let def_map = self.analysis.ty_cx.def_map.borrow();
+ if !def_map.contains_key(&id) {
+ self.sess.span_bug(p.span,
+ format!("def_map has no key for {} in visit_arm",
+ id).as_slice());
+ }
+ let def = def_map.get(&id);
+ match *def {
+ def::DefBinding(id, _) => self.fmt.variable_str(p.span,
+ sub_span,
+ id,
+ path_to_str(p).as_slice(),
+ value.as_slice(),
+ ""),
+ def::DefVariant(_,id,_) => self.fmt.ref_str(ref_kind,
+ p.span,
+ sub_span,
+ id,
+ e.cur_scope),
+ // FIXME(nrc) what is this doing here?
+ def::DefStatic(_, _) => {}
+ _ => error!("unexpected defintion kind when processing collected paths: {:?}", *def)
+ }
+ }
+ self.collected_paths.clear();
+ visit::walk_expr_opt(self, arm.guard, e);
+ self.visit_expr(&*arm.body, e);
+ }
+
+ fn visit_stmt(&mut self, s:&ast::Stmt, e:DxrVisitorEnv) {
+ if generated_code(s.span) {
+ return
+ }
+
+ visit::walk_stmt(self, s, e)
+ }
+
+ fn visit_local(&mut self, l:&ast::Local, e: DxrVisitorEnv) {
+ if generated_code(l.span) {
+ return
+ }
+
+ // The local could declare multiple new vars, we must walk the
+ // pattern and collect them all.
+ assert!(self.collected_paths.len() == 0 && !self.collecting);
+ self.collecting = true;
+ self.visit_pat(&*l.pat, e);
+ self.collecting = false;
+
+ let value = self.span.snippet(l.span);
+
+ for &(id, ref p, ref immut, _) in self.collected_paths.iter() {
+ let value = if *immut { value.to_owned() } else { "<mutable>".to_owned() };
+ let types = self.analysis.ty_cx.node_types.borrow();
+ let typ = ppaux::ty_to_str(&self.analysis.ty_cx, *types.get(&(id as uint)));
+ // Get the span only for the name of the variable (I hope the path
+ // is only ever a variable name, but who knows?).
+ let sub_span = self.span.span_for_last_ident(p.span);
+ // Rust uses the id of the pattern for var lookups, so we'll use it too.
+ self.fmt.variable_str(p.span,
+ sub_span,
+ id,
+ path_to_str(p).as_slice(),
+ value.as_slice(),
+ typ.as_slice());
+ }
+ self.collected_paths.clear();
+
+ // Just walk the initialiser and type (don't want to walk the pattern again).
+ self.visit_ty(&*l.ty, e);
+ visit::walk_expr_opt(self, l.init, e);
+ }
+}
+
+#[deriving(Clone)]
+struct DxrVisitorEnv {
+ cur_scope: NodeId,
+}
+
+impl DxrVisitorEnv {
+ fn new() -> DxrVisitorEnv {
+ DxrVisitorEnv{cur_scope: 0}
+ }
+ fn new_nested(new_mod: NodeId) -> DxrVisitorEnv {
+ DxrVisitorEnv{cur_scope: new_mod}
+ }
+}
+
+pub fn process_crate(sess: &Session,
+ krate: &ast::Crate,
+ analysis: &CrateAnalysis,
+ odir: &Option<Path>) {
+ if generated_code(krate.span) {
+ return;
+ }
+
+ let (cratename, crateid) = match attr::find_crateid(krate.attrs.as_slice()) {
+ Some(crateid) => (crateid.name.clone(), crateid.to_str()),
+ None => {
+ info!("Could not find crate name, using 'unknown_crate'");
+ (String::from_str("unknown_crate"),"unknown_crate".to_owned())
+ },
+ };
+
+ info!("Dumping crate {} ({})", cratename, crateid);
+
+ // find a path to dump our data to
+ let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") {
+ Some(val) => Path::new(val),
+ None => match *odir {
+ Some(ref val) => val.join("dxr"),
+ None => Path::new("dxr-temp"),
+ },
+ };
+
+ match fs::mkdir_recursive(&root_path, io::UserRWX) {
+ Err(e) => sess.err(format!("Could not create directory {}: {}",
+ root_path.display(), e).as_slice()),
+ _ => (),
+ }
+
+ {
+ let disp = root_path.display();
+ info!("Writing output to {}", disp);
+ }
+
+ // Create ouput file.
+ let mut out_name = cratename.clone();
+ out_name.push_str(".csv");
+ root_path.push(out_name);
+ let output_file = match File::create(&root_path) {
+ Ok(f) => box f,
+ Err(e) => {
+ let disp = root_path.display();
+ sess.fatal(format!("Could not open {}: {}", disp, e).as_slice());
+ }
+ };
+ root_path.pop();
+
+ let mut visitor = DxrVisitor{ sess: sess,
+ analysis: analysis,
+ collected_paths: vec!(),
+ collecting: false,
+ fmt: FmtStrs::new(box Recorder {
+ out: output_file as Box<Writer>,
+ dump_spans: false,
+ },
+ SpanUtils {
+ sess: sess,
+ err_count: Cell::new(0)
+ },
+ cratename.clone()),
+ span: SpanUtils {
+ sess: sess,
+ err_count: Cell::new(0)
+ }};
+
+ visitor.dump_crate_info(cratename.as_slice(), krate);
+
+ visit::walk_crate(&mut visitor, krate, DxrVisitorEnv::new());
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use middle::save::escape;
+use middle::save::span_utils::SpanUtils;
+
+use std::vec::Vec;
+
+use syntax::ast;
+use syntax::ast::{NodeId,DefId};
+use syntax::codemap::*;
+
+pub struct Recorder {
+ // output file
+ pub out: Box<Writer>,
+ pub dump_spans: bool,
+}
+
+impl Recorder {
+ pub fn record(&mut self, info: &str) {
+ match write!(self.out, "{}", info) {
+ Err(_) => error!("Error writing output '{}'", info),
+ _ => (),
+ }
+ }
+
+ pub fn dump_span(&mut self,
+ su: SpanUtils,
+ kind: &str,
+ span: Span,
+ _sub_span: Option<Span>) {
+ assert!(self.dump_spans);
+ let result = format!("span,kind,{},{},text,\"{}\"\n",
+ kind, su.extent_str(span), escape(su.snippet(span)));
+ self.record(result.as_slice());
+ }
+}
+
+pub struct FmtStrs<'a> {
+ pub recorder: Box<Recorder>,
+ span: SpanUtils<'a>,
+ krate: String,
+}
+
+macro_rules! s { ($e:expr) => { format!("{}", $e) }}
+macro_rules! svec {
+ ($($e:expr),*) => ({
+ // leading _ to allow empty construction without a warning.
+ let mut _temp = ::std::vec::Vec::new();
+ $(_temp.push(s!($e));)*
+ _temp
+ })
+}
+
+pub enum Row {
+ Variable,
+ Enum,
+ Variant,
+ VariantStruct,
+ Function,
+ MethodDecl,
+ Struct,
+ Trait,
+ Impl,
+ Module,
+ UseAlias,
+ ExternCrate,
+ Inheritance,
+ MethodCall,
+ Typedef,
+ ExternalCrate,
+ Crate,
+ FnCall,
+ ModRef,
+ VarRef,
+ TypeRef,
+ StructRef,
+ FnRef,
+}
+
+impl<'a> FmtStrs<'a> {
+ pub fn new(rec: Box<Recorder>, span: SpanUtils<'a>, krate: String) -> FmtStrs<'a> {
+ FmtStrs {
+ recorder: rec,
+ span: span,
+ krate: krate,
+ }
+ }
+
+ // A map from kind of item to a tuple of
+ // a string representation of the name
+ // a vector of field names
+ // whether this kind requires a span
+ // whether dump_spans should dump for this kind
+ fn lookup_row(r: Row) -> (&'static str, Vec<&'static str>, bool, bool) {
+ match r {
+ Variable => ("variable",
+ vec!("id","name","qualname","value","type","scopeid"),
+ true, true),
+ Enum => ("enum", vec!("id","qualname","scopeid"), true, true),
+ Variant => ("variant", vec!("id","name","qualname","value","scopeid"), true, true),
+ VariantStruct => ("variant_struct",
+ vec!("id","ctor_id","qualname","value","scopeid"), true, true),
+ Function => ("function", vec!("id","qualname","declid","declidcrate","scopeid"),
+ true, true),
+ MethodDecl => ("method_decl", vec!("id","qualname","scopeid"), true, true),
+ Struct => ("struct", vec!("id","ctor_id","qualname","scopeid"), true, true),
+ Trait => ("trait", vec!("id","qualname","scopeid"), true, true),
+ Impl => ("impl", vec!("id","refid","refidcrate","scopeid"), true, true),
+ Module => ("module", vec!("id","qualname","scopeid","def_file"), true, false),
+ UseAlias => ("use_alias",
+ vec!("id","refid","refidcrate","name","scopeid"),
+ true, true),
+ ExternCrate => ("extern_crate",
+ vec!("id","name","location","crate","scopeid"),
+ true, true),
+ Inheritance => ("inheritance",
+ vec!("base","basecrate","derived","derivedcrate"),
+ true, false),
+ MethodCall => ("method_call",
+ vec!("refid","refidcrate","declid","declidcrate","scopeid"),
+ true, true),
+ Typedef => ("typedef", vec!("id","qualname","value"), true, true),
+ ExternalCrate => ("external_crate", vec!("name","crate","file_name"), false, false),
+ Crate => ("crate", vec!("name"), true, false),
+ FnCall => ("fn_call", vec!("refid","refidcrate","qualname","scopeid"), true, true),
+ ModRef => ("mod_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true),
+ VarRef => ("var_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true),
+ TypeRef => ("type_ref",
+ vec!("refid","refidcrate","qualname","scopeid"),
+ true, true),
+ StructRef => ("struct_ref",
+ vec!("refid","refidcrate","qualname","scopeid"),
+ true, true),
+ FnRef => ("fn_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true)
+ }
+ }
+
+ pub fn make_values_str(&self,
+ kind: &'static str,
+ fields: &Vec<&'static str>,
+ values: Vec<String>,
+ span: Span) -> Option<String> {
+ if values.len() != fields.len() {
+ self.span.sess.span_bug(span, format!(
+ "Mismatch between length of fields for '{}', expected '{}', found '{}'",
+ kind, fields.len(), values.len()).as_slice());
+ }
+
+ let values = values.iter().map(|s| {
+ if s.len() > 1020 {
+ s.as_slice().slice_to(1020)
+ } else {
+ s.as_slice()
+ }
+ });
+
+ let pairs = fields.iter().zip(values);
+ let mut strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape(
+ if *f == "qualname" {
+ self.krate.clone().append("::").append(v)
+ } else {
+ String::from_str(v)
+ }
+ )));
+ Some(strs.fold(String::new(), |s, ss| s.append(ss.as_slice()))).map(|s| s.into_owned())
+ }
+
+ pub fn record_without_span(&mut self,
+ kind: Row,
+ values: Vec<String>,
+ span: Span) {
+ let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
+
+ if needs_span {
+ self.span.sess.span_bug(span, format!(
+ "Called record_without_span for '{}' which does requires a span",
+ label).as_slice());
+ }
+ assert!(!dump_spans);
+
+ if self.recorder.dump_spans {
+ return;
+ }
+
+ let values_str = match self.make_values_str(label, fields, values, span) {
+ Some(vs) => vs,
+ None => return,
+ };
+
+ let result = String::from_str(label);
+ self.recorder.record(result.append(values_str.as_slice()).append("\n").as_slice());
+ }
+
+ pub fn record_with_span(&mut self,
+ kind: Row,
+ span: Span,
+ sub_span: Span,
+ values: Vec<String>) {
+ let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
+
+ if self.recorder.dump_spans {
+ if dump_spans {
+ self.recorder.dump_span(self.span, label, span, Some(sub_span));
+ }
+ return;
+ }
+
+ if !needs_span {
+ self.span.sess.span_bug(span,
+ format!("Called record_with_span for '{}' \
+ which does not require a span", label).as_slice());
+ }
+
+ let values_str = match self.make_values_str(label, fields, values, span) {
+ Some(vs) => vs,
+ None => return,
+ };
+ let result = format!("{},{}{}\n", label, self.span.extent_str(sub_span), values_str);
+ self.recorder.record(result.as_slice());
+ }
+
+ pub fn check_and_record(&mut self,
+ kind: Row,
+ span: Span,
+ sub_span: Option<Span>,
+ values: Vec<String>) {
+ match sub_span {
+ Some(sub_span) => self.record_with_span(kind, span, sub_span, values),
+ None => {
+ let (label, _, _, _) = FmtStrs::lookup_row(kind);
+ self.span.report_span_err(label, span);
+ }
+ }
+ }
+
+ pub fn variable_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ value: &str,
+ typ: &str) {
+ // Getting a fully qualified name for a variable is hard because in
+ // the local case they can be overridden in one block and there is no nice way
+ // to refer to such a scope in english, so we just hack it by appending the
+ // variable def's node id
+ let qualname = String::from_str(name).append("$").append(id.to_str().as_slice());
+ self.check_and_record(Variable,
+ span,
+ sub_span,
+ svec!(id, name, qualname, value, typ, 0));
+ }
+
+ // formal parameters
+ pub fn formal_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ fn_name: &str,
+ name: &str,
+ typ: &str) {
+ let qualname = String::from_str(fn_name).append("::").append(name);
+ self.check_and_record(Variable,
+ span,
+ sub_span,
+ svec!(id, name, qualname, "", typ, 0));
+ }
+
+ // value is the initialising expression of the static if it is not mut, otherwise "".
+ pub fn static_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ qualname: &str,
+ value: &str,
+ typ: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Variable,
+ span,
+ sub_span,
+ svec!(id, name, qualname, value, typ, scope_id));
+ }
+
+ pub fn field_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ qualname: &str,
+ typ: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Variable,
+ span,
+ sub_span,
+ svec!(id, name, qualname, "", typ, scope_id));
+ }
+
+ pub fn enum_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Enum,
+ span,
+ sub_span,
+ svec!(id, name, scope_id));
+ }
+
+ pub fn tuple_variant_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ qualname: &str,
+ val: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Variant,
+ span,
+ sub_span,
+ svec!(id, name, qualname, val, scope_id));
+ }
+
+ pub fn struct_variant_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ ctor_id: NodeId,
+ name: &str,
+ val: &str,
+ scope_id: NodeId) {
+ self.check_and_record(VariantStruct,
+ span,
+ sub_span,
+ svec!(id, ctor_id, name, val, scope_id));
+ }
+
+ pub fn fn_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Function,
+ span,
+ sub_span,
+ svec!(id, name, "", "", scope_id));
+ }
+
+ pub fn method_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ decl_id: Option<DefId>,
+ scope_id: NodeId) {
+ let values = match decl_id {
+ Some(decl_id) => svec!(id, name, decl_id.node, decl_id.krate, scope_id),
+ None => svec!(id, name, "", "", scope_id)
+ };
+ self.check_and_record(Function,
+ span,
+ sub_span,
+ values);
+ }
+
+ pub fn method_decl_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ scope_id: NodeId) {
+ self.check_and_record(MethodDecl,
+ span,
+ sub_span,
+ svec!(id, name, scope_id));
+ }
+
+ pub fn struct_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ ctor_id: NodeId,
+ name: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Struct,
+ span,
+ sub_span,
+ svec!(id, ctor_id, name, scope_id));
+ }
+
+ pub fn trait_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ scope_id: NodeId) {
+ self.check_and_record(Trait,
+ span,
+ sub_span,
+ svec!(id, name, scope_id));
+ }
+
+ pub fn impl_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ ref_id: DefId,
+ scope_id: NodeId) {
+ self.check_and_record(Impl,
+ span,
+ sub_span,
+ svec!(id, ref_id.node, ref_id.krate, scope_id));
+ }
+
+ pub fn mod_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ name: &str,
+ parent: NodeId,
+ filename: &str) {
+ self.check_and_record(Module,
+ span,
+ sub_span,
+ svec!(id, name, parent, filename));
+ }
+
+ pub fn use_alias_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ mod_id: Option<DefId>,
+ name: &str,
+ parent: NodeId) {
+ let (mod_node, mod_crate) = match mod_id {
+ Some(mod_id) => (mod_id.node, mod_id.krate),
+ None => (0, 0)
+ };
+ self.check_and_record(UseAlias,
+ span,
+ sub_span,
+ svec!(id, mod_node, mod_crate, name, parent));
+ }
+
+ pub fn extern_crate_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ cnum: ast::CrateNum,
+ name: &str,
+ loc: &str,
+ parent: NodeId) {
+ self.check_and_record(ExternCrate,
+ span,
+ sub_span,
+ svec!(id, name, loc, cnum, parent));
+ }
+
+ pub fn inherit_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ base_id: DefId,
+ deriv_id: NodeId) {
+ self.check_and_record(Inheritance,
+ span,
+ sub_span,
+ svec!(base_id.node, base_id.krate, deriv_id, 0));
+ }
+
+ pub fn fn_call_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: DefId,
+ scope_id:NodeId) {
+ self.check_and_record(FnCall,
+ span,
+ sub_span,
+ svec!(id.node, id.krate, "", scope_id));
+ }
+
+ pub fn meth_call_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ defid: Option<DefId>,
+ declid: Option<DefId>,
+ scope_id: NodeId) {
+ let (dfn, dfk) = match defid {
+ Some(defid) => (defid.node, defid.krate),
+ None => (0, 0)
+ };
+ let (dcn, dck) = match declid {
+ Some(declid) => (s!(declid.node), s!(declid.krate)),
+ None => ("".to_owned(), "".to_owned())
+ };
+ self.check_and_record(MethodCall,
+ span,
+ sub_span,
+ svec!(dfn, dfk, dcn, dck, scope_id));
+ }
+
+ pub fn sub_mod_ref_str(&mut self,
+ span: Span,
+ sub_span: Span,
+ qualname: &str,
+ parent:NodeId) {
+ self.record_with_span(ModRef,
+ span,
+ sub_span,
+ svec!(0, 0, qualname, parent));
+ }
+
+ pub fn typedef_str(&mut self,
+ span: Span,
+ sub_span: Option<Span>,
+ id: NodeId,
+ qualname: &str,
+ value: &str) {
+ self.check_and_record(Typedef,
+ span,
+ sub_span,
+ svec!(id, qualname, value));
+ }
+
+ pub fn crate_str(&mut self,
+ span: Span,
+ name: &str) {
+ self.record_with_span(Crate,
+ span,
+ span,
+ svec!(name));
+ }
+
+ pub fn external_crate_str(&mut self,
+ span: Span,
+ name: &str,
+ num: ast::CrateNum) {
+ let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo);
+ self.record_without_span(ExternalCrate,
+ svec!(name, num, lo_loc.file.name),
+ span);
+ }
+
+ pub fn sub_type_ref_str(&mut self,
+ span: Span,
+ sub_span: Span,
+ qualname: &str) {
+ self.record_with_span(TypeRef,
+ span,
+ sub_span,
+ svec!(0, 0, qualname, 0));
+ }
+
+ // A slightly generic function for a reference to an item of any kind.
+ pub fn ref_str(&mut self,
+ kind: Row,
+ span: Span,
+ sub_span: Option<Span>,
+ id: DefId,
+ scope_id: NodeId) {
+ self.check_and_record(kind,
+ span,
+ sub_span,
+ svec!(id.node, id.krate, "", scope_id));
+ }
+}
--- /dev/null
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use driver::session::Session;
+
+use middle::save::generated_code;
+
+use std::cell::Cell;
+
+use syntax::ast;
+use syntax::codemap::*;
+use syntax::parse::lexer;
+use syntax::parse::lexer::{Reader,StringReader};
+use syntax::parse::token;
+use syntax::parse::token::{is_keyword,keywords,is_ident,Token};
+
+pub struct SpanUtils<'a> {
+ pub sess: &'a Session,
+ pub err_count: Cell<int>,
+}
+
+impl<'a> SpanUtils<'a> {
+ // Standard string for extents/location.
+ pub fn extent_str(&self, span: Span) -> String {
+ let lo_loc = self.sess.codemap().lookup_char_pos(span.lo);
+ let hi_loc = self.sess.codemap().lookup_char_pos(span.hi);
+ let lo_pos = self.sess.codemap().lookup_byte_offset(span.lo).pos;
+ let hi_pos = self.sess.codemap().lookup_byte_offset(span.hi).pos;
+
+ format!("file_name,{},file_line,{},file_col,{},extent_start,{},\
+ file_line_end,{},file_col_end,{},extent_end,{}",
+ lo_loc.file.name, lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(),
+ hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint())
+ }
+
+ // sub_span starts at span.lo, so we need to adjust the positions etc.
+ // If sub_span is None, we don't need to adjust.
+ pub fn make_sub_span(&self, span: Span, sub_span: Option<Span>) -> Option<Span> {
+ let loc = self.sess.codemap().lookup_char_pos(span.lo);
+ assert!(!generated_code(span),
+ "generated code; we should not be processing this `{}` in {}, line {}",
+ self.snippet(span), loc.file.name, loc.line);
+
+ match sub_span {
+ None => None,
+ Some(sub) => {
+ let FileMapAndBytePos {fm, pos} =
+ self.sess.codemap().lookup_byte_offset(span.lo);
+ let base = pos + fm.start_pos;
+ Some(Span {
+ lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos,
+ hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos,
+ expn_info: None,
+ })
+ }
+ }
+ }
+
+ pub fn snippet(&self, span: Span) -> String {
+ match self.sess.codemap().span_to_snippet(span) {
+ Some(s) => s,
+ None => String::new(),
+ }
+ }
+
+ pub fn retokenise_span(&self, span: Span) -> StringReader<'a> {
+ // sadness - we don't have spans for sub-expressions nor access to the tokens
+ // so in order to get extents for the function name itself (which dxr expects)
+ // we need to re-tokenise the fn definition
+
+ // Note: this is a bit awful - it adds the contents of span to the end of
+ // the codemap as a new filemap. This is mostly OK, but means we should
+ // not iterate over the codemap. Also, any spans over the new filemap
+ // are incompatible with spans over other filemaps.
+ let filemap = self.sess.codemap().new_filemap(String::from_str("<anon-dxr>"),
+ self.snippet(span));
+ let s = self.sess;
+ lexer::StringReader::new(s.diagnostic(), filemap)
+ }
+
+ // Re-parses a path and returns the span for the last identifier in the path
+ pub fn span_for_last_ident(&self, span: Span) -> Option<Span> {
+ let mut result = None;
+
+ let mut toks = self.retokenise_span(span);
+ let mut bracket_count = 0;
+ loop {
+ let ts = toks.next_token();
+ if ts.tok == token::EOF {
+ return self.make_sub_span(span, result)
+ }
+ if bracket_count == 0 &&
+ (is_ident(&ts.tok) || is_keyword(keywords::Self, &ts.tok)) {
+ result = Some(ts.sp);
+ }
+
+ bracket_count += match ts.tok {
+ token::LT => 1,
+ token::GT => -1,
+ token::BINOP(token::SHR) => -2,
+ _ => 0
+ }
+ }
+ }
+
+ // Return the span for the first identifier in the path.
+ pub fn span_for_first_ident(&self, span: Span) -> Option<Span> {
+ let mut toks = self.retokenise_span(span);
+ let mut bracket_count = 0;
+ loop {
+ let ts = toks.next_token();
+ if ts.tok == token::EOF {
+ return None;
+ }
+ if bracket_count == 0 &&
+ (is_ident(&ts.tok) || is_keyword(keywords::Self, &ts.tok)) {
+ return self.make_sub_span(span, Some(ts.sp));
+ }
+
+ bracket_count += match ts.tok {
+ token::LT => 1,
+ token::GT => -1,
+ token::BINOP(token::SHR) => -2,
+ _ => 0
+ }
+ }
+ }
+
+ // Return the span for the last ident before a `(` or `<` or '::<' and outside any
+ // any brackets, or the last span.
+ pub fn sub_span_for_meth_name(&self, span: Span) -> Option<Span> {
+ let mut toks = self.retokenise_span(span);
+ let mut prev = toks.next_token();
+ let mut result = None;
+ let mut bracket_count = 0;
+ let mut last_span = None;
+ while prev.tok != token::EOF {
+ last_span = None;
+ let mut next = toks.next_token();
+
+ if (next.tok == token::LPAREN ||
+ next.tok == token::LT) &&
+ bracket_count == 0 &&
+ is_ident(&prev.tok) {
+ result = Some(prev.sp);
+ }
+
+ if bracket_count == 0 &&
+ next.tok == token::MOD_SEP {
+ let old = prev;
+ prev = next;
+ next = toks.next_token();
+ if next.tok == token::LT &&
+ is_ident(&old.tok) {
+ result = Some(old.sp);
+ }
+ }
+
+ bracket_count += match prev.tok {
+ token::LPAREN | token::LT => 1,
+ token::RPAREN | token::GT => -1,
+ token::BINOP(token::SHR) => -2,
+ _ => 0
+ };
+
+ if is_ident(&prev.tok) && bracket_count == 0 {
+ last_span = Some(prev.sp);
+ }
+ prev = next;
+ }
+ if result.is_none() && last_span.is_some() {
+ return self.make_sub_span(span, last_span);
+ }
+ return self.make_sub_span(span, result);
+ }
+
+ // Return the span for the last ident before a `<` and outside any
+ // brackets, or the last span.
+ pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> {
+ let mut toks = self.retokenise_span(span);
+ let mut prev = toks.next_token();
+ let mut result = None;
+ let mut bracket_count = 0;
+ loop {
+ let next = toks.next_token();
+
+ if (next.tok == token::LT ||
+ next.tok == token::COLON) &&
+ bracket_count == 0 &&
+ is_ident(&prev.tok) {
+ result = Some(prev.sp);
+ }
+
+ bracket_count += match prev.tok {
+ token::LT => 1,
+ token::GT => -1,
+ token::BINOP(token::SHR) => -2,
+ _ => 0
+ };
+
+ if next.tok == token::EOF {
+ break;
+ }
+ prev = next;
+ }
+ if bracket_count != 0 {
+ let loc = self.sess.codemap().lookup_char_pos(span.lo);
+ self.sess.span_bug(span,
+ format!("Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
+ self.snippet(span), loc.file.name, loc.line).as_slice());
+ }
+ if result.is_none() && is_ident(&prev.tok) && bracket_count == 0 {
+ return self.make_sub_span(span, Some(prev.sp));
+ }
+ self.make_sub_span(span, result)
+ }
+
+ // Reparse span and return an owned vector of sub spans of the first limit
+ // identifier tokens in the given nesting level.
+ // example with Foo<Bar<T,V>, Bar<T,V>>
+ // Nesting = 0: all idents outside of brackets: ~[Foo]
+ // Nesting = 1: idents within one level of brackets: ~[Bar, Bar]
+ pub fn spans_with_brackets(&self, span: Span, nesting: int, limit: int) -> Vec<Span> {
+ let mut result: Vec<Span> = vec!();
+
+ let mut toks = self.retokenise_span(span);
+ // We keep track of how many brackets we're nested in
+ let mut bracket_count = 0;
+ loop {
+ let ts = toks.next_token();
+ if ts.tok == token::EOF {
+ if bracket_count != 0 {
+ let loc = self.sess.codemap().lookup_char_pos(span.lo);
+ self.sess.span_bug(span, format!(
+ "Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
+ self.snippet(span), loc.file.name, loc.line).as_slice());
+ }
+ return result
+ }
+ if (result.len() as int) == limit {
+ return result;
+ }
+ bracket_count += match ts.tok {
+ token::LT => 1,
+ token::GT => -1,
+ token::BINOP(token::SHL) => 2,
+ token::BINOP(token::SHR) => -2,
+ _ => 0
+ };
+ if is_ident(&ts.tok) &&
+ bracket_count == nesting {
+ result.push(self.make_sub_span(span, Some(ts.sp)).unwrap());
+ }
+ }
+ }
+
+ pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> {
+ let mut toks = self.retokenise_span(span);
+ let mut prev = toks.next_token();
+ loop {
+ if prev.tok == token::EOF {
+ return None;
+ }
+ let next = toks.next_token();
+ if next.tok == tok {
+ return self.make_sub_span(span, Some(prev.sp));
+ }
+ prev = next;
+ }
+ }
+
+ // Return an owned vector of the subspans of the tokens that come before tok2
+ // which is before tok1. If there is no instance of tok2 before tok1, then that
+ // place in the result is None.
+ // Everything returned must be inside a set of (non-angle) brackets, but no
+ // more deeply nested than that.
+ pub fn sub_spans_before_tokens(&self,
+ span: Span,
+ tok1: Token,
+ tok2: Token) -> Vec<Option<Span>> {
+ let mut sub_spans : Vec<Option<Span>> = vec!();
+ let mut toks = self.retokenise_span(span);
+ let mut prev = toks.next_token();
+ let mut next = toks.next_token();
+ let mut stored_val = false;
+ let mut found_val = false;
+ let mut bracket_count = 0;
+ while next.tok != token::EOF {
+ if bracket_count == 1 {
+ if next.tok == tok2 {
+ sub_spans.push(self.make_sub_span(span, Some(prev.sp)));
+ stored_val = true;
+ found_val = false;
+ }
+ if next.tok == tok1 {
+ if !stored_val {
+ sub_spans.push(None);
+ } else {
+ stored_val = false;
+ }
+ found_val = false;
+ }
+ if !stored_val &&
+ is_ident(&next.tok) {
+ found_val = true;
+ }
+ }
+
+ bracket_count += match next.tok {
+ token::LPAREN | token::LBRACE => 1,
+ token::RPAREN | token::RBRACE => -1,
+ _ => 0
+ };
+
+ prev = next;
+ next = toks.next_token();
+ }
+ if found_val {
+ sub_spans.push(None);
+ }
+ return sub_spans;
+ }
+
+ pub fn sub_span_after_keyword(&self,
+ span: Span,
+ keyword: keywords::Keyword) -> Option<Span> {
+ let mut toks = self.retokenise_span(span);
+ loop {
+ let ts = toks.next_token();
+ if ts.tok == token::EOF {
+ return None;
+ }
+ if is_keyword(keyword, &ts.tok) {
+ let ts = toks.next_token();
+ if ts.tok == token::EOF {
+ return None
+ } else {
+ return self.make_sub_span(span, Some(ts.sp));
+ }
+ }
+ }
+ }
+
+ // Returns a list of the spans of idents in a patch.
+ // E.g., For foo::bar<x,t>::baz, we return [foo, bar, baz] (well, their spans)
+ pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec<Span> {
+ if generated_code(path.span) {
+ return vec!();
+ }
+
+ self.spans_with_brackets(path.span, 0, -1)
+ }
+
+ // Return an owned vector of the subspans of the param identifier
+ // tokens found in span.
+ pub fn spans_for_ty_params(&self, span: Span, number: int) -> Vec<Span> {
+ if generated_code(span) {
+ return vec!();
+ }
+ // Type params are nested within one level of brackets:
+ // i.e. we want ~[A, B] from Foo<A, B<T,U>>
+ self.spans_with_brackets(span, 1, number)
+ }
+
+ pub fn report_span_err(&self, kind: &str, span: Span) {
+ let loc = self.sess.codemap().lookup_char_pos(span.lo);
+ info!("({}) Could not find sub_span in `{}` in {}, line {}",
+ kind, self.snippet(span), loc.file.name, loc.line);
+ self.err_count.set(self.err_count.get()+1);
+ if self.err_count.get() > 1000 {
+ self.sess.bug("span errors reached 1000, giving up");
+ }
+ }
+}