From 4f9b04bf9e14bb8cb718e5dd9564ac0b698a8395 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 4 May 2015 22:33:07 +1200 Subject: [PATCH 1/8] save-analysis: move csv dumping stuff to its own module and rename --- src/librustc_trans/save/dump_csv.rs | 1522 ++++++++++++++++++++++++++ src/librustc_trans/save/mod.rs | 1528 +-------------------------- 2 files changed, 1540 insertions(+), 1510 deletions(-) create mode 100644 src/librustc_trans/save/dump_csv.rs diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs new file mode 100644 index 0000000000000..ab66123c4d739 --- /dev/null +++ b/src/librustc_trans/save/dump_csv.rs @@ -0,0 +1,1522 @@ +// Copyright 2015 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 or the MIT license +// , 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. +//! DumpCsvVisitor walks the AST and processes it. + +use super::{escape, generated_code, recorder}; + +use session::Session; + +use middle::def; +use middle::ty::{self, Ty}; + +use std::cell::Cell; +use std::fs::File; +use std::path::Path; + +use syntax::ast_util; +use syntax::ast::{self, NodeId, DefId}; +use syntax::ast_map::NodeItem; +use syntax::codemap::*; +use syntax::parse::token::{self, get_ident, keywords}; +use syntax::owned_slice::OwnedSlice; +use syntax::visit::{self, Visitor}; +use syntax::print::pprust::{path_to_string, ty_to_string}; +use syntax::ptr::P; + +use super::span_utils::SpanUtils; +use super::recorder::{Recorder, FmtStrs}; + +use util::ppaux; + + +pub struct DumpCsvVisitor<'l, 'tcx: 'l> { + sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + + collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, + collecting: bool, + + span: SpanUtils<'l>, + fmt: FmtStrs<'l>, + + cur_scope: NodeId +} + +impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { + fn nest(&mut self, scope_id: NodeId, f: F) where + F: FnOnce(&mut DumpCsvVisitor<'l, 'tcx>), + { + let parent_scope = self.cur_scope; + self.cur_scope = scope_id; + f(self); + self.cur_scope = parent_scope; + } + + pub fn new(sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + output_file: Box) -> DumpCsvVisitor<'l, 'tcx> { + DumpCsvVisitor { + sess: sess, + analysis: analysis, + collected_paths: vec![], + collecting: false, + span: SpanUtils { + sess: sess, + err_count: Cell::new(0) + }, + fmt: FmtStrs::new(box Recorder { + out: output_file, + dump_spans: false, + }, + SpanUtils { + sess: sess, + err_count: Cell::new(0) + }), + cur_scope: 0 + } + } + + pub 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, 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_string(path), spans.len(), path.segments.len()); + for s in &spans { + 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 (i, (seg, span)) in path.segments.iter().zip(spans.iter()).enumerate() { + segs.push(seg.clone()); + let sub_path = ast::Path{span: *span, // span for the last segment + global: path.global, + segments: segs}; + let qualname = if i == 0 && path.global { + format!("::{}", path_to_string(&sub_path)) + } else { + path_to_string(&sub_path) + }; + result.push((*span, qualname)); + segs = sub_path.segments; + } + + result + } + + // The global arg allows us to override the global-ness of the path (which + // actually means 'does the path start with `::`', rather than 'is the path + // semantically global). We use the override for `use` imports (etc.) where + // the syntax is non-global, but the semantics are global. + fn write_sub_paths(&mut self, path: &ast::Path, global: bool) { + let sub_paths = self.process_path_prefixes(path); + for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { + let qualname = if i == 0 && global && !path.global { + format!("::{}", qualname) + } else { + qualname.clone() + }; + self.fmt.sub_mod_ref_str(path.span, + *span, + &qualname[..], + self.cur_scope); + } + } + + // As write_sub_paths, but does not process the last ident in the path (assuming it + // will be processed elsewhere). See note on write_sub_paths about global. + fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) { + let sub_paths = self.process_path_prefixes(path); + let len = sub_paths.len(); + if len <= 1 { + return; + } + + let sub_paths = &sub_paths[..len-1]; + for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { + let qualname = if i == 0 && global && !path.global { + format!("::{}", qualname) + } else { + qualname.clone() + }; + self.fmt.sub_mod_ref_str(path.span, + *span, + &qualname[..], + self.cur_scope); + } + } + + // 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) { + let sub_paths = self.process_path_prefixes(path); + let len = sub_paths.len(); + if len <= 1 { + return; + } + let sub_paths = &sub_paths[.. (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[..]); + + // write the other sub-paths + if len <= 2 { + return; + } + let sub_paths = &sub_paths[..len-2]; + for &(ref span, ref qualname) in sub_paths { + self.fmt.sub_mod_ref_str(path.span, + *span, + &qualname[..], + self.cur_scope); + } + } + + // looks up anything, not just a type + fn lookup_type_ref(&self, ref_id: NodeId) -> Option { + 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)); + } + let def = self.analysis.ty_cx.def_map.borrow().get(&ref_id).unwrap().full_def(); + match def { + def::DefPrimTy(_) => None, + _ => Some(def.def_id()), + } + } + + fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option { + 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)); + } + let def = def_map.get(&ref_id).unwrap().full_def(); + match def { + def::DefMod(_) | + def::DefForeignMod(_) => Some(recorder::ModRef), + def::DefStruct(_) => Some(recorder::StructRef), + def::DefTy(..) | + def::DefAssociatedTy(..) | + def::DefTrait(_) => Some(recorder::TypeRef), + def::DefStatic(_, _) | + def::DefConst(_) | + def::DefAssociatedConst(..) | + def::DefLocal(_) | + def::DefVariant(_, _, _) | + def::DefUpvar(..) => Some(recorder::VarRef), + + def::DefFn(..) => Some(recorder::FnRef), + + def::DefSelfTy(..) | + def::DefRegion(_) | + def::DefLabel(_) | + def::DefTyParam(..) | + def::DefUse(_) | + def::DefMethod(..) | + def::DefPrimTy(_) => { + self.sess.span_bug(span, &format!("lookup_def_kind for unexpected item: {:?}", + def)); + }, + } + } + + fn process_formals(&mut self, formals: &Vec, qualname: &str) { + for arg in formals { + assert!(self.collected_paths.is_empty() && !self.collecting); + self.collecting = true; + self.visit_pat(&*arg.pat); + self.collecting = false; + let span_utils = self.span.clone(); + for &(id, ref p, _, _) in &self.collected_paths { + let typ = + ppaux::ty_to_string( + &self.analysis.ty_cx, + *self.analysis.ty_cx.node_types().get(&id).unwrap()); + // 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_string(p), + &typ[..]); + } + self.collected_paths.clear(); + } + } + + fn process_method(&mut self, sig: &ast::MethodSig, + body: Option<&ast::Block>, + id: ast::NodeId, name: ast::Name, + span: Span) { + if generated_code(span) { + return; + } + + debug!("process_method: {}:{}", id, token::get_name(name)); + + 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 qualname = match ty::impl_of_method(&self.analysis.ty_cx, + ast_util::local_def(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(_, _, _, _, ref ty, _) => { + let mut result = String::from_str("<"); + result.push_str(&ty_to_string(&**ty)); + + match ty::trait_of_item(&self.analysis.ty_cx, + ast_util::local_def(id)) { + Some(def_id) => { + result.push_str(" as "); + result.push_str( + &ty::item_path_str(&self.analysis.ty_cx, def_id)); + }, + None => {} + } + result.push_str(">"); + result + } + _ => { + self.sess.span_bug(span, + &format!("Container {} for method {} not an impl?", + impl_id.node, id)); + }, + } + }, + _ => { + self.sess.span_bug(span, + &format!("Container {} for method {} is not a node item {:?}", + impl_id.node, id, self.analysis.ty_cx.map.get(impl_id.node))); + }, + }, + None => match ty::trait_of_item(&self.analysis.ty_cx, + ast_util::local_def(id)) { + Some(def_id) => { + scope_id = def_id.node; + match self.analysis.ty_cx.map.get(def_id.node) { + NodeItem(_) => { + format!("::{}", ty::item_path_str(&self.analysis.ty_cx, def_id)) + } + _ => { + self.sess.span_bug(span, + &format!("Could not find container {} for method {}", + def_id.node, id)); + } + } + }, + None => { + self.sess.span_bug(span, + &format!("Could not find container for method {}", id)); + }, + }, + }; + + let qualname = &format!("{}::{}", qualname, &token::get_name(name)); + + // record the decl for this def (if it has one) + let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx, + ast_util::local_def(id)) + .and_then(|new_id| { + let def_id = new_id.def_id(); + if def_id.node != 0 && def_id != ast_util::local_def(id) { + Some(def_id) + } else { + None + } + }); + + let sub_span = self.span.sub_span_after_keyword(span, keywords::Fn); + if body.is_some() { + self.fmt.method_str(span, + sub_span, + id, + qualname, + decl_id, + scope_id); + self.process_formals(&sig.decl.inputs, qualname); + } else { + self.fmt.method_decl_str(span, + sub_span, + id, + qualname, + scope_id); + } + + // walk arg and return types + for arg in &sig.decl.inputs { + self.visit_ty(&arg.ty); + } + + if let ast::Return(ref ret_ty) = sig.decl.output { + self.visit_ty(ret_ty); + } + + // walk the fn body + if let Some(body) = body { + self.nest(id, |v| v.visit_block(body)); + } + + self.process_generic_params(&sig.generics, + span, + qualname, + id); + } + + fn process_trait_ref(&mut self, + trait_ref: &ast::TraitRef) { + 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, + self.cur_scope); + visit::walk_path(self, &trait_ref.path); + }, + 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_string( + &self.analysis.ty_cx, + *self.analysis.ty_cx.node_types().get(&field.node.id).unwrap()); + 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[..], + &qualname[..], + &typ[..], + scope_id), + None => self.sess.span_bug(field.span, + &format!("Could not find sub-span for field {}", + qualname)), + } + }, + _ => (), + } + } + + // Dump generic params bindings, then visit_generics + fn process_generic_params(&mut self, + generics:&ast::Generics, + full_span: Span, + prefix: &str, + id: NodeId) { + // 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 isize)); + 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[..], + ""); + } + self.visit_generics(generics); + } + + fn process_fn(&mut self, + item: &ast::Item, + decl: &ast::FnDecl, + ty_params: &ast::Generics, + body: &ast::Block) { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(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[..], + self.cur_scope); + + self.process_formals(&decl.inputs, &qualname[..]); + + // walk arg and return types + for arg in &decl.inputs { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = decl.output { + self.visit_ty(&**ret_ty); + } + + // walk the body + self.nest(item.id, |v| v.visit_block(&*body)); + + self.process_generic_params(ty_params, item.span, &qualname[..], item.id); + } + + fn process_static(&mut self, + item: &ast::Item, + typ: &ast::Ty, + mt: ast::Mutability, + expr: &ast::Expr) + { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + + // If the variable is immutable, save the initialising expression. + let (value, keyword) = match mt { + ast::MutMutable => (String::from_str(""), keywords::Mut), + ast::MutImmutable => (self.span.snippet(expr.span), keywords::Static), + }; + + let sub_span = self.span.sub_span_after_keyword(item.span, keyword); + self.fmt.static_str(item.span, + sub_span, + item.id, + &get_ident(item.ident), + &qualname[..], + &value[..], + &ty_to_string(&*typ), + self.cur_scope); + + // walk type and init value + self.visit_ty(&*typ); + self.visit_expr(expr); + } + + fn process_const(&mut self, + id: ast::NodeId, + ident: &ast::Ident, + span: Span, + typ: &ast::Ty, + expr: &ast::Expr) + { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(id)); + + let sub_span = self.span.sub_span_after_keyword(span, + keywords::Const); + self.fmt.static_str(span, + sub_span, + id, + &get_ident((*ident).clone()), + &qualname[..], + "", + &ty_to_string(&*typ), + self.cur_scope); + + // walk type and init value + self.visit_ty(typ); + self.visit_expr(expr); + } + + fn process_struct(&mut self, + item: &ast::Item, + def: &ast::StructDef, + ty_params: &ast::Generics) { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + + let ctor_id = match def.ctor_id { + Some(node_id) => node_id, + None => -1, + }; + let val = self.span.snippet(item.span); + 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[..], + self.cur_scope, + &val[..]); + + // fields + for field in &def.fields { + self.process_struct_field_def(field, &qualname[..], item.id); + self.visit_ty(&*field.node.ty); + } + + self.process_generic_params(ty_params, item.span, &qualname[..], item.id); + } + + fn process_enum(&mut self, + item: &ast::Item, + enum_definition: &ast::EnumDef, + ty_params: &ast::Generics) { + let enum_name = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let val = self.span.snippet(item.span); + 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, + &enum_name[..], + self.cur_scope, + &val[..]), + None => self.sess.span_bug(item.span, + &format!("Could not find subspan for enum {}", + enum_name)), + } + for variant in &enum_definition.variants { + let name = get_ident(variant.node.name); + let name = &name; + let mut qualname = enum_name.clone(); + qualname.push_str("::"); + qualname.push_str(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[..], + &enum_name[..], + &val[..], + item.id); + for arg in args { + self.visit_ty(&*arg.ty); + } + } + 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[..], + &enum_name[..], + &val[..], + item.id); + + for field in &struct_def.fields { + self.process_struct_field_def(field, &qualname, variant.node.id); + self.visit_ty(&*field.node.ty); + } + } + } + } + + self.process_generic_params(ty_params, item.span, &enum_name[..], item.id); + } + + fn process_impl(&mut self, + item: &ast::Item, + type_parameters: &ast::Generics, + trait_ref: &Option, + typ: &ast::Ty, + impl_items: &[P]) { + let trait_id = trait_ref.as_ref().and_then(|tr| self.lookup_type_ref(tr.ref_id)); + match typ.node { + // Common case impl for a struct or something basic. + ast::TyPath(None, ref path) => { + let sub_span = self.span.sub_span_for_type_name(path.span); + let self_id = self.lookup_type_ref(typ.id).map(|id| { + self.fmt.ref_str(recorder::TypeRef, + path.span, + sub_span, + id, + self.cur_scope); + id + }); + self.fmt.impl_str(path.span, + sub_span, + item.id, + self_id, + trait_id, + self.cur_scope); + }, + _ => { + // Less useful case, impl for a compound type. + self.visit_ty(&*typ); + + let sub_span = self.span.sub_span_for_type_name(typ.span); + self.fmt.impl_str(typ.span, + sub_span, + item.id, + None, + trait_id, + self.cur_scope); + } + } + + match *trait_ref { + Some(ref trait_ref) => self.process_trait_ref(trait_ref), + None => (), + } + + self.process_generic_params(type_parameters, item.span, "", item.id); + for impl_item in impl_items { + self.visit_impl_item(impl_item); + } + } + + fn process_trait(&mut self, + item: &ast::Item, + generics: &ast::Generics, + trait_refs: &OwnedSlice, + methods: &[P]) { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let val = self.span.snippet(item.span); + let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait); + self.fmt.trait_str(item.span, + sub_span, + item.id, + &qualname[..], + self.cur_scope, + &val[..]); + + // super-traits + for super_bound in &**trait_refs { + let trait_ref = match *super_bound { + ast::TraitTyParamBound(ref trait_ref, _) => { + trait_ref + } + ast::RegionTyParamBound(..) => { + continue; + } + }; + + let trait_ref = &trait_ref.trait_ref; + 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, + self.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[..], item.id); + for method in methods { + self.visit_trait_item(method) + } + } + + fn process_mod(&mut self, + item: &ast::Item, // The module in question, represented as an item. + m: &ast::Mod) { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(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[..], + self.cur_scope, + &filename[..]); + + self.nest(item.id, |v| visit::walk_mod(v, m)); + } + + fn process_path(&mut self, + id: NodeId, + span: Span, + path: &ast::Path, + ref_kind: Option) { + if generated_code(span) { + return + } + + let def_map = self.analysis.ty_cx.def_map.borrow(); + if !def_map.contains_key(&id) { + self.sess.span_bug(span, + &format!("def_map has no key for {} in visit_expr", id)); + } + let def = def_map.get(&id).unwrap().full_def(); + let sub_span = self.span.span_for_last_ident(span); + match def { + def::DefUpvar(..) | + def::DefLocal(..) | + def::DefStatic(..) | + def::DefConst(..) | + def::DefAssociatedConst(..) | + def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef), + span, + sub_span, + def.def_id(), + self.cur_scope), + def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef, + span, + sub_span, + def_id, + self.cur_scope), + def::DefTy(def_id, _) => self.fmt.ref_str(recorder::TypeRef, + span, + sub_span, + def_id, + self.cur_scope), + def::DefMethod(declid, provenence) => { + let sub_span = self.span.sub_span_for_meth_name(span); + let defid = if declid.krate == ast::LOCAL_CRATE { + let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, + declid); + match provenence { + def::FromTrait(def_id) => { + Some(ty::trait_items(&self.analysis.ty_cx, + def_id) + .iter() + .find(|mr| { + mr.name() == ti.name() + }) + .unwrap() + .def_id()) + } + def::FromImpl(def_id) => { + let impl_items = self.analysis + .ty_cx + .impl_items + .borrow(); + Some(impl_items.get(&def_id) + .unwrap() + .iter() + .find(|mr| { + ty::impl_or_trait_item( + &self.analysis.ty_cx, + mr.def_id() + ).name() == ti.name() + }) + .unwrap() + .def_id()) + } + } + } else { + None + }; + self.fmt.meth_call_str(span, + sub_span, + defid, + Some(declid), + self.cur_scope); + }, + def::DefFn(def_id, _) => { + self.fmt.fn_call_str(span, + sub_span, + def_id, + self.cur_scope) + } + _ => self.sess.span_bug(span, + &format!("Unexpected def kind while looking \ + up path in `{}`: `{:?}`", + self.span.snippet(span), + def)), + } + // modules or types in the path prefix + match def { + def::DefMethod(did, _) => { + let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, did); + if let ty::MethodTraitItem(m) = ti { + if m.explicit_self == ty::StaticExplicitSelfCategory { + self.write_sub_path_trait_truncated(path); + } + } + } + def::DefLocal(_) | + def::DefStatic(_,_) | + def::DefConst(..) | + def::DefAssociatedConst(..) | + def::DefStruct(_) | + def::DefVariant(..) | + def::DefFn(..) => self.write_sub_paths_truncated(path, false), + _ => {}, + } + } + + fn process_struct_lit(&mut self, + ex: &ast::Expr, + path: &ast::Path, + fields: &Vec, + base: &Option>) { + if generated_code(path.span) { + return + } + + self.write_sub_paths_truncated(path, false); + + let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, ex).sty; + let struct_def = match *ty { + ty::ty_struct(def_id, _) => { + let sub_span = self.span.span_for_last_ident(path.span); + self.fmt.ref_str(recorder::StructRef, + path.span, + sub_span, + def_id, + self.cur_scope); + Some(def_id) + } + _ => None + }; + + for field in fields { + match struct_def { + Some(struct_def) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); + for f in &fields { + 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, + self.cur_scope); + } + } + } + None => {} + } + + self.visit_expr(&*field.expr) + } + visit::walk_expr_opt(self, base) + } + + fn process_method_call(&mut self, + ex: &ast::Expr, + args: &Vec>) { + let method_map = self.analysis.ty_cx.method_map.borrow(); + let method_callee = method_map.get(&ty::MethodCall::expr(ex.id)).unwrap(); + let (def_id, decl_id) = match method_callee.origin { + ty::MethodStatic(def_id) | + ty::MethodStaticClosure(def_id) => { + // method invoked on an object with a concrete type (not a static method) + let decl_id = + match ty::trait_item_of_item(&self.analysis.ty_cx, + def_id) { + None => None, + Some(decl_id) => Some(decl_id.def_id()), + }; + + // This incantation is required if the method referenced is a + // trait's default implementation. + let def_id = match ty::impl_or_trait_item(&self.analysis + .ty_cx, + def_id) { + ty::MethodTraitItem(method) => { + method.provided_source.unwrap_or(def_id) + } + _ => self.sess + .span_bug(ex.span, + "save::process_method_call: non-method \ + DefId in MethodStatic or MethodStaticClosure"), + }; + (Some(def_id), decl_id) + } + ty::MethodTypeParam(ref mp) => { + // method invoked on a type parameter + let trait_item = ty::trait_item(&self.analysis.ty_cx, + mp.trait_ref.def_id, + mp.method_num); + (None, Some(trait_item.def_id())) + } + ty::MethodTraitObject(ref mo) => { + // method invoked on a trait instance + let trait_item = ty::trait_item(&self.analysis.ty_cx, + mo.trait_ref.def_id, + mo.method_num); + (None, Some(trait_item.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, + self.cur_scope); + + // walk receiver and args + visit::walk_exprs(self, &args[..]); + } + + fn process_pat(&mut self, p:&ast::Pat) { + 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); + + let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def(); + let struct_def = match def { + def::DefConst(..) | def::DefAssociatedConst(..) => None, + def::DefVariant(_, variant_id, _) => Some(variant_id), + _ => { + match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) { + None => { + self.sess.span_bug(p.span, + &format!("Could not find struct_def for `{}`", + self.span.snippet(p.span))); + } + Some(def_id) => Some(def_id), + } + } + }; + + if let Some(struct_def) = struct_def { + let struct_fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); + for &Spanned { node: ref field, span } in fields { + let sub_span = self.span.span_for_first_ident(span); + for f in &struct_fields { + if f.name == field.ident.name { + self.fmt.ref_str(recorder::VarRef, + span, + sub_span, + f.id, + self.cur_scope); + break; + } + } + self.visit_pat(&*field.pat); + } + } + } + ast::PatEnum(ref path, _) | + ast::PatQPath(_, ref path) => { + self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); + visit::walk_pat(self, p); + } + ast::PatIdent(bm, ref path1, 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 + let path = ast_util::ident_to_path(path1.span,path1.node); + self.collected_paths.push((p.id, path, immut, recorder::VarRef)); + match *optional_subpattern { + None => {} + Some(ref subpattern) => self.visit_pat(&**subpattern) + } + } + _ => visit::walk_pat(self, p) + } + } +} + +impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { + fn visit_item(&mut self, item: &ast::Item) { + if generated_code(item.span) { + return + } + + match item.node { + ast::ItemUse(ref use_item) => { + match use_item.node { + ast::ViewPathSimple(ident, ref path) => { + let sub_span = self.span.span_for_last_ident(path.span); + let mod_id = match self.lookup_type_ref(item.id) { + Some(def_id) => { + match self.lookup_def_kind(item.id, path.span) { + Some(kind) => self.fmt.ref_str(kind, + path.span, + sub_span, + def_id, + self.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_after_keyword(use_item.span, keywords::As) { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; + + self.fmt.use_alias_str(path.span, + sub_span, + item.id, + mod_id, + &get_ident(ident), + self.cur_scope); + self.write_sub_paths_truncated(path, true); + } + ast::ViewPathGlob(ref path) => { + // Make a comma-separated list of names of imported modules. + let mut name_string = String::new(); + let glob_map = &self.analysis.glob_map; + let glob_map = glob_map.as_ref().unwrap(); + if glob_map.contains_key(&item.id) { + for n in glob_map.get(&item.id).unwrap() { + if !name_string.is_empty() { + name_string.push_str(", "); + } + name_string.push_str(n.as_str()); + } + } + + let sub_span = self.span.sub_span_of_token(path.span, + token::BinOp(token::Star)); + self.fmt.use_glob_str(path.span, + sub_span, + item.id, + &name_string, + self.cur_scope); + self.write_sub_paths(path, true); + } + ast::ViewPathList(ref path, ref list) => { + for plid in list { + match plid.node { + ast::PathListIdent { id, .. } => { + match self.lookup_type_ref(id) { + Some(def_id) => + match self.lookup_def_kind(id, plid.span) { + Some(kind) => { + self.fmt.ref_str( + kind, plid.span, + Some(plid.span), + def_id, self.cur_scope); + } + None => () + }, + None => () + } + }, + ast::PathListMod { .. } => () + } + } + + self.write_sub_paths(path, true); + } + } + } + ast::ItemExternCrate(ref s) => { + let name = get_ident(item.ident); + let name = &name; + let location = match *s { + Some(s) => s.to_string(), + None => name.to_string(), + }; + let alias_span = self.span.span_for_last_ident(item.span); + let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(item.id) { + Some(cnum) => cnum, + None => 0, + }; + self.fmt.extern_crate_str(item.span, + alias_span, + item.id, + cnum, + name, + &location[..], + self.cur_scope); + } + ast::ItemFn(ref decl, _, _, ref ty_params, ref body) => + self.process_fn(item, &**decl, ty_params, &**body), + ast::ItemStatic(ref typ, mt, ref expr) => + self.process_static(item, &**typ, mt, &**expr), + ast::ItemConst(ref typ, ref expr) => + self.process_const(item.id, &item.ident, item.span, &*typ, &*expr), + ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), + ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), + ast::ItemImpl(_, _, + ref ty_params, + ref trait_ref, + ref typ, + ref impl_items) => { + self.process_impl(item, + ty_params, + trait_ref, + &**typ, + impl_items) + } + ast::ItemTrait(_, ref generics, ref trait_refs, ref methods) => + self.process_trait(item, generics, trait_refs, methods), + ast::ItemMod(ref m) => self.process_mod(item, m), + ast::ItemTy(ref ty, ref ty_params) => { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let value = ty_to_string(&**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[..], + &value[..]); + + self.visit_ty(&**ty); + self.process_generic_params(ty_params, item.span, &qualname, item.id); + }, + ast::ItemMac(_) => (), + _ => visit::walk_item(self, item), + } + } + + fn visit_generics(&mut self, generics: &ast::Generics) { + for param in &*generics.ty_params { + for bound in &*param.bounds { + if let ast::TraitTyParamBound(ref trait_ref, _) = *bound { + self.process_trait_ref(&trait_ref.trait_ref); + } + } + if let Some(ref ty) = param.default { + self.visit_ty(&**ty); + } + } + } + + fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { + match trait_item.node { + ast::ConstTraitItem(ref ty, Some(ref expr)) => { + self.process_const(trait_item.id, &trait_item.ident, + trait_item.span, &*ty, &*expr); + } + ast::MethodTraitItem(ref sig, ref body) => { + self.process_method(sig, body.as_ref().map(|x| &**x), + trait_item.id, trait_item.ident.name, trait_item.span); + } + ast::ConstTraitItem(_, None) | + ast::TypeTraitItem(..) => {} + } + } + + fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { + match impl_item.node { + ast::ConstImplItem(ref ty, ref expr) => { + self.process_const(impl_item.id, &impl_item.ident, + impl_item.span, &ty, &expr); + } + ast::MethodImplItem(ref sig, ref body) => { + self.process_method(sig, Some(body), impl_item.id, + impl_item.ident.name, impl_item.span); + } + ast::TypeImplItem(_) | + ast::MacImplItem(_) => {} + } + } + + fn visit_ty(&mut self, t: &ast::Ty) { + if generated_code(t.span) { + return + } + + match t.node { + ast::TyPath(_, ref path) => { + match self.lookup_type_ref(t.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, + self.cur_scope); + }, + None => () + } + + self.write_sub_paths_truncated(path, false); + + visit::walk_path(self, path); + }, + _ => visit::walk_ty(self, t), + } + } + + fn visit_expr(&mut self, ex: &ast::Expr) { + if generated_code(ex.span) { + return + } + + match ex.node { + ast::ExprCall(ref _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); + } + ast::ExprPath(_, ref path) => { + self.process_path(ex.id, path.span, path, None); + visit::walk_expr(self, ex); + } + ast::ExprStruct(ref path, ref fields, ref base) => + self.process_struct_lit(ex, path, fields, base), + ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args), + ast::ExprField(ref sub_ex, ident) => { + if generated_code(sub_ex.span) { + return + } + + self.visit_expr(&**sub_ex); + let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty; + match *ty { + ty::ty_struct(def_id, _) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); + for f in &fields { + if f.name == ident.node.name { + let sub_span = self.span.span_for_last_ident(ex.span); + self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + f.id, + self.cur_scope); + break; + } + } + } + _ => self.sess.span_bug(ex.span, + &format!("Expected struct type, found {:?}", ty)), + } + }, + ast::ExprTupField(ref sub_ex, idx) => { + if generated_code(sub_ex.span) { + return + } + + self.visit_expr(&**sub_ex); + + let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty; + match *ty { + ty::ty_struct(def_id, _) => { + let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); + for (i, f) in fields.iter().enumerate() { + if i == idx.node { + let sub_span = self.span.sub_span_after_token(ex.span, token::Dot); + self.fmt.ref_str(recorder::VarRef, + ex.span, + sub_span, + f.id, + self.cur_scope); + break; + } + } + } + ty::ty_tup(_) => {} + _ => self.sess.span_bug(ex.span, + &format!("Expected struct or tuple \ + type, found {:?}", ty)), + } + }, + ast::ExprClosure(_, ref decl, ref body) => { + if generated_code(body.span) { + return + } + + let mut id = String::from_str("$"); + id.push_str(&ex.id.to_string()); + self.process_formals(&decl.inputs, &id[..]); + + // walk arg and return types + for arg in &decl.inputs { + self.visit_ty(&*arg.ty); + } + + if let ast::Return(ref ret_ty) = decl.output { + self.visit_ty(&**ret_ty); + } + + // walk the body + self.nest(ex.id, |v| v.visit_block(&**body)); + }, + _ => { + visit::walk_expr(self, ex) + }, + } + } + + fn visit_mac(&mut self, _: &ast::Mac) { + // Just stop, macros are poison to us. + } + + fn visit_pat(&mut self, p: &ast::Pat) { + self.process_pat(p); + if !self.collecting { + self.collected_paths.clear(); + } + } + + fn visit_arm(&mut self, arm: &ast::Arm) { + assert!(self.collected_paths.is_empty() && !self.collecting); + self.collecting = true; + for pattern in &arm.pats { + // collect paths from the arm's patterns + self.visit_pat(&**pattern); + } + + // This is to get around borrow checking, because we need mut self to call process_path. + let mut paths_to_process = vec![]; + // process collected paths + for &(id, ref p, ref immut, ref_kind) in &self.collected_paths { + 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)); + } + let def = def_map.get(&id).unwrap().full_def(); + match def { + def::DefLocal(id) => { + let value = if *immut { + self.span.snippet(p.span).to_string() + } else { + "".to_string() + }; + + assert!(p.segments.len() == 1, "qualified path for local variable def in arm"); + self.fmt.variable_str(p.span, + Some(p.span), + id, + &path_to_string(p), + &value[..], + "") + } + def::DefVariant(..) | def::DefTy(..) | def::DefStruct(..) => { + paths_to_process.push((id, p.clone(), Some(ref_kind))) + } + // FIXME(nrc) what are these doing here? + def::DefStatic(_, _) | + def::DefConst(..) | + def::DefAssociatedConst(..) => {} + _ => error!("unexpected definition kind when processing collected paths: {:?}", + def) + } + } + for &(id, ref path, ref_kind) in &paths_to_process { + self.process_path(id, path.span, path, ref_kind); + } + self.collecting = false; + self.collected_paths.clear(); + visit::walk_expr_opt(self, &arm.guard); + self.visit_expr(&*arm.body); + } + + fn visit_stmt(&mut self, s: &ast::Stmt) { + if generated_code(s.span) { + return + } + + visit::walk_stmt(self, s) + } + + fn visit_local(&mut self, l: &ast::Local) { + 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.is_empty() && !self.collecting); + self.collecting = true; + self.visit_pat(&*l.pat); + self.collecting = false; + + let value = self.span.snippet(l.span); + + for &(id, ref p, ref immut, _) in &self.collected_paths { + let value = if *immut { value.to_string() } else { "".to_string() }; + let types = self.analysis.ty_cx.node_types(); + let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap()); + // 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_string(p), + &value[..], + &typ[..]); + } + self.collected_paths.clear(); + + // Just walk the initialiser and type (don't want to walk the pattern again). + visit::walk_ty_opt(self, &l.ty); + visit::walk_expr_opt(self, &l.init); + } +} diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 89cda6d785f07..7421344548c4f 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,1506 +8,20 @@ // 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 session::Session; +use middle::ty; -use middle::def; -use middle::ty::{self, Ty}; - -use std::cell::Cell; use std::env; use std::fs::{self, File}; use std::path::{Path, PathBuf}; -use syntax::ast_util; -use syntax::ast::{self, NodeId, DefId}; -use syntax::ast_map::NodeItem; -use syntax::attr; +use syntax::{ast, attr, visit}; use syntax::codemap::*; -use syntax::parse::token::{self, get_ident, keywords}; -use syntax::owned_slice::OwnedSlice; -use syntax::visit::{self, Visitor}; -use syntax::print::pprust::{path_to_string, ty_to_string}; -use syntax::ptr::P; - -use self::span_utils::SpanUtils; -use self::recorder::{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_id != NO_EXPANSION || span == DUMMY_SP -} - -struct DxrVisitor<'l, 'tcx: 'l> { - sess: &'l Session, - analysis: &'l ty::CrateAnalysis<'tcx>, - - collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, - collecting: bool, - - span: SpanUtils<'l>, - fmt: FmtStrs<'l>, - - cur_scope: NodeId -} - -impl <'l, 'tcx> DxrVisitor<'l, 'tcx> { - fn nest(&mut self, scope_id: NodeId, f: F) where - F: FnOnce(&mut DxrVisitor<'l, 'tcx>), - { - let parent_scope = self.cur_scope; - self.cur_scope = scope_id; - f(self); - self.cur_scope = parent_scope; - } - - 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, 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_string(path), spans.len(), path.segments.len()); - for s in &spans { - 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 (i, (seg, span)) in path.segments.iter().zip(spans.iter()).enumerate() { - segs.push(seg.clone()); - let sub_path = ast::Path{span: *span, // span for the last segment - global: path.global, - segments: segs}; - let qualname = if i == 0 && path.global { - format!("::{}", path_to_string(&sub_path)) - } else { - path_to_string(&sub_path) - }; - result.push((*span, qualname)); - segs = sub_path.segments; - } - - result - } - - // The global arg allows us to override the global-ness of the path (which - // actually means 'does the path start with `::`', rather than 'is the path - // semantically global). We use the override for `use` imports (etc.) where - // the syntax is non-global, but the semantics are global. - fn write_sub_paths(&mut self, path: &ast::Path, global: bool) { - let sub_paths = self.process_path_prefixes(path); - for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { - let qualname = if i == 0 && global && !path.global { - format!("::{}", qualname) - } else { - qualname.clone() - }; - self.fmt.sub_mod_ref_str(path.span, - *span, - &qualname[..], - self.cur_scope); - } - } - - // As write_sub_paths, but does not process the last ident in the path (assuming it - // will be processed elsewhere). See note on write_sub_paths about global. - fn write_sub_paths_truncated(&mut self, path: &ast::Path, global: bool) { - let sub_paths = self.process_path_prefixes(path); - let len = sub_paths.len(); - if len <= 1 { - return; - } - - let sub_paths = &sub_paths[..len-1]; - for (i, &(ref span, ref qualname)) in sub_paths.iter().enumerate() { - let qualname = if i == 0 && global && !path.global { - format!("::{}", qualname) - } else { - qualname.clone() - }; - self.fmt.sub_mod_ref_str(path.span, - *span, - &qualname[..], - self.cur_scope); - } - } - - // 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) { - let sub_paths = self.process_path_prefixes(path); - let len = sub_paths.len(); - if len <= 1 { - return; - } - let sub_paths = &sub_paths[.. (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[..]); - - // write the other sub-paths - if len <= 2 { - return; - } - let sub_paths = &sub_paths[..len-2]; - for &(ref span, ref qualname) in sub_paths { - self.fmt.sub_mod_ref_str(path.span, - *span, - &qualname[..], - self.cur_scope); - } - } - - // looks up anything, not just a type - fn lookup_type_ref(&self, ref_id: NodeId) -> Option { - 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)); - } - let def = self.analysis.ty_cx.def_map.borrow().get(&ref_id).unwrap().full_def(); - match def { - def::DefPrimTy(_) => None, - _ => Some(def.def_id()), - } - } - - fn lookup_def_kind(&self, ref_id: NodeId, span: Span) -> Option { - 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)); - } - let def = def_map.get(&ref_id).unwrap().full_def(); - match def { - def::DefMod(_) | - def::DefForeignMod(_) => Some(recorder::ModRef), - def::DefStruct(_) => Some(recorder::StructRef), - def::DefTy(..) | - def::DefAssociatedTy(..) | - def::DefTrait(_) => Some(recorder::TypeRef), - def::DefStatic(_, _) | - def::DefConst(_) | - def::DefAssociatedConst(..) | - def::DefLocal(_) | - def::DefVariant(_, _, _) | - def::DefUpvar(..) => Some(recorder::VarRef), - - def::DefFn(..) => Some(recorder::FnRef), - - def::DefSelfTy(..) | - def::DefRegion(_) | - def::DefLabel(_) | - def::DefTyParam(..) | - def::DefUse(_) | - def::DefMethod(..) | - def::DefPrimTy(_) => { - self.sess.span_bug(span, &format!("lookup_def_kind for unexpected item: {:?}", - def)); - }, - } - } - - fn process_formals(&mut self, formals: &Vec, qualname: &str) { - for arg in formals { - assert!(self.collected_paths.is_empty() && !self.collecting); - self.collecting = true; - self.visit_pat(&*arg.pat); - self.collecting = false; - let span_utils = self.span.clone(); - for &(id, ref p, _, _) in &self.collected_paths { - let typ = - ppaux::ty_to_string( - &self.analysis.ty_cx, - *self.analysis.ty_cx.node_types().get(&id).unwrap()); - // 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_string(p), - &typ[..]); - } - self.collected_paths.clear(); - } - } - - fn process_method(&mut self, sig: &ast::MethodSig, - body: Option<&ast::Block>, - id: ast::NodeId, name: ast::Name, - span: Span) { - if generated_code(span) { - return; - } - - debug!("process_method: {}:{}", id, token::get_name(name)); - - 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 qualname = match ty::impl_of_method(&self.analysis.ty_cx, - ast_util::local_def(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(_, _, _, _, ref ty, _) => { - let mut result = String::from_str("<"); - result.push_str(&ty_to_string(&**ty)); - - match ty::trait_of_item(&self.analysis.ty_cx, - ast_util::local_def(id)) { - Some(def_id) => { - result.push_str(" as "); - result.push_str( - &ty::item_path_str(&self.analysis.ty_cx, def_id)); - }, - None => {} - } - result.push_str(">"); - result - } - _ => { - self.sess.span_bug(span, - &format!("Container {} for method {} not an impl?", - impl_id.node, id)); - }, - } - }, - _ => { - self.sess.span_bug(span, - &format!("Container {} for method {} is not a node item {:?}", - impl_id.node, id, self.analysis.ty_cx.map.get(impl_id.node))); - }, - }, - None => match ty::trait_of_item(&self.analysis.ty_cx, - ast_util::local_def(id)) { - Some(def_id) => { - scope_id = def_id.node; - match self.analysis.ty_cx.map.get(def_id.node) { - NodeItem(_) => { - format!("::{}", ty::item_path_str(&self.analysis.ty_cx, def_id)) - } - _ => { - self.sess.span_bug(span, - &format!("Could not find container {} for method {}", - def_id.node, id)); - } - } - }, - None => { - self.sess.span_bug(span, - &format!("Could not find container for method {}", id)); - }, - }, - }; - - let qualname = &format!("{}::{}", qualname, &token::get_name(name)); - - // record the decl for this def (if it has one) - let decl_id = ty::trait_item_of_item(&self.analysis.ty_cx, - ast_util::local_def(id)) - .and_then(|new_id| { - let def_id = new_id.def_id(); - if def_id.node != 0 && def_id != ast_util::local_def(id) { - Some(def_id) - } else { - None - } - }); - - let sub_span = self.span.sub_span_after_keyword(span, keywords::Fn); - if body.is_some() { - self.fmt.method_str(span, - sub_span, - id, - qualname, - decl_id, - scope_id); - self.process_formals(&sig.decl.inputs, qualname); - } else { - self.fmt.method_decl_str(span, - sub_span, - id, - qualname, - scope_id); - } - - // walk arg and return types - for arg in &sig.decl.inputs { - self.visit_ty(&arg.ty); - } - - if let ast::Return(ref ret_ty) = sig.decl.output { - self.visit_ty(ret_ty); - } - - // walk the fn body - if let Some(body) = body { - self.nest(id, |v| v.visit_block(body)); - } - - self.process_generic_params(&sig.generics, - span, - qualname, - id); - } - - fn process_trait_ref(&mut self, - trait_ref: &ast::TraitRef) { - 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, - self.cur_scope); - visit::walk_path(self, &trait_ref.path); - }, - 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_string( - &self.analysis.ty_cx, - *self.analysis.ty_cx.node_types().get(&field.node.id).unwrap()); - 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[..], - &qualname[..], - &typ[..], - scope_id), - None => self.sess.span_bug(field.span, - &format!("Could not find sub-span for field {}", - qualname)), - } - }, - _ => (), - } - } - - // Dump generic params bindings, then visit_generics - fn process_generic_params(&mut self, - generics:&ast::Generics, - full_span: Span, - prefix: &str, - id: NodeId) { - // 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 isize)); - 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[..], - ""); - } - self.visit_generics(generics); - } - - fn process_fn(&mut self, - item: &ast::Item, - decl: &ast::FnDecl, - ty_params: &ast::Generics, - body: &ast::Block) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(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[..], - self.cur_scope); - - self.process_formals(&decl.inputs, &qualname[..]); - - // walk arg and return types - for arg in &decl.inputs { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = decl.output { - self.visit_ty(&**ret_ty); - } - - // walk the body - self.nest(item.id, |v| v.visit_block(&*body)); - - self.process_generic_params(ty_params, item.span, &qualname[..], item.id); - } - - fn process_static(&mut self, - item: &ast::Item, - typ: &ast::Ty, - mt: ast::Mutability, - expr: &ast::Expr) - { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - - // If the variable is immutable, save the initialising expression. - let (value, keyword) = match mt { - ast::MutMutable => (String::from_str(""), keywords::Mut), - ast::MutImmutable => (self.span.snippet(expr.span), keywords::Static), - }; - - let sub_span = self.span.sub_span_after_keyword(item.span, keyword); - self.fmt.static_str(item.span, - sub_span, - item.id, - &get_ident(item.ident), - &qualname[..], - &value[..], - &ty_to_string(&*typ), - self.cur_scope); - - // walk type and init value - self.visit_ty(&*typ); - self.visit_expr(expr); - } - - fn process_const(&mut self, - id: ast::NodeId, - ident: &ast::Ident, - span: Span, - typ: &ast::Ty, - expr: &ast::Expr) - { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(id)); - - let sub_span = self.span.sub_span_after_keyword(span, - keywords::Const); - self.fmt.static_str(span, - sub_span, - id, - &get_ident((*ident).clone()), - &qualname[..], - "", - &ty_to_string(&*typ), - self.cur_scope); - - // walk type and init value - self.visit_ty(typ); - self.visit_expr(expr); - } - - fn process_struct(&mut self, - item: &ast::Item, - def: &ast::StructDef, - ty_params: &ast::Generics) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - - let ctor_id = match def.ctor_id { - Some(node_id) => node_id, - None => -1, - }; - let val = self.span.snippet(item.span); - 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[..], - self.cur_scope, - &val[..]); - - // fields - for field in &def.fields { - self.process_struct_field_def(field, &qualname[..], item.id); - self.visit_ty(&*field.node.ty); - } - - self.process_generic_params(ty_params, item.span, &qualname[..], item.id); - } - - fn process_enum(&mut self, - item: &ast::Item, - enum_definition: &ast::EnumDef, - ty_params: &ast::Generics) { - let enum_name = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - let val = self.span.snippet(item.span); - 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, - &enum_name[..], - self.cur_scope, - &val[..]), - None => self.sess.span_bug(item.span, - &format!("Could not find subspan for enum {}", - enum_name)), - } - for variant in &enum_definition.variants { - let name = get_ident(variant.node.name); - let name = &name; - let mut qualname = enum_name.clone(); - qualname.push_str("::"); - qualname.push_str(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[..], - &enum_name[..], - &val[..], - item.id); - for arg in args { - self.visit_ty(&*arg.ty); - } - } - 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[..], - &enum_name[..], - &val[..], - item.id); - - for field in &struct_def.fields { - self.process_struct_field_def(field, &qualname, variant.node.id); - self.visit_ty(&*field.node.ty); - } - } - } - } - - self.process_generic_params(ty_params, item.span, &enum_name[..], item.id); - } - - fn process_impl(&mut self, - item: &ast::Item, - type_parameters: &ast::Generics, - trait_ref: &Option, - typ: &ast::Ty, - impl_items: &[P]) { - let trait_id = trait_ref.as_ref().and_then(|tr| self.lookup_type_ref(tr.ref_id)); - match typ.node { - // Common case impl for a struct or something basic. - ast::TyPath(None, ref path) => { - let sub_span = self.span.sub_span_for_type_name(path.span); - let self_id = self.lookup_type_ref(typ.id).map(|id| { - self.fmt.ref_str(recorder::TypeRef, - path.span, - sub_span, - id, - self.cur_scope); - id - }); - self.fmt.impl_str(path.span, - sub_span, - item.id, - self_id, - trait_id, - self.cur_scope); - }, - _ => { - // Less useful case, impl for a compound type. - self.visit_ty(&*typ); - - let sub_span = self.span.sub_span_for_type_name(typ.span); - self.fmt.impl_str(typ.span, - sub_span, - item.id, - None, - trait_id, - self.cur_scope); - } - } - - match *trait_ref { - Some(ref trait_ref) => self.process_trait_ref(trait_ref), - None => (), - } - - self.process_generic_params(type_parameters, item.span, "", item.id); - for impl_item in impl_items { - self.visit_impl_item(impl_item); - } - } - - fn process_trait(&mut self, - item: &ast::Item, - generics: &ast::Generics, - trait_refs: &OwnedSlice, - methods: &[P]) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - let val = self.span.snippet(item.span); - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Trait); - self.fmt.trait_str(item.span, - sub_span, - item.id, - &qualname[..], - self.cur_scope, - &val[..]); - - // super-traits - for super_bound in &**trait_refs { - let trait_ref = match *super_bound { - ast::TraitTyParamBound(ref trait_ref, _) => { - trait_ref - } - ast::RegionTyParamBound(..) => { - continue; - } - }; - - let trait_ref = &trait_ref.trait_ref; - 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, - self.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[..], item.id); - for method in methods { - self.visit_trait_item(method) - } - } - - fn process_mod(&mut self, - item: &ast::Item, // The module in question, represented as an item. - m: &ast::Mod) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(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[..], - self.cur_scope, - &filename[..]); - - self.nest(item.id, |v| visit::walk_mod(v, m)); - } - - fn process_path(&mut self, - id: NodeId, - span: Span, - path: &ast::Path, - ref_kind: Option) { - if generated_code(span) { - return - } - - let def_map = self.analysis.ty_cx.def_map.borrow(); - if !def_map.contains_key(&id) { - self.sess.span_bug(span, - &format!("def_map has no key for {} in visit_expr", id)); - } - let def = def_map.get(&id).unwrap().full_def(); - let sub_span = self.span.span_for_last_ident(span); - match def { - def::DefUpvar(..) | - def::DefLocal(..) | - def::DefStatic(..) | - def::DefConst(..) | - def::DefAssociatedConst(..) | - def::DefVariant(..) => self.fmt.ref_str(ref_kind.unwrap_or(recorder::VarRef), - span, - sub_span, - def.def_id(), - self.cur_scope), - def::DefStruct(def_id) => self.fmt.ref_str(recorder::StructRef, - span, - sub_span, - def_id, - self.cur_scope), - def::DefTy(def_id, _) => self.fmt.ref_str(recorder::TypeRef, - span, - sub_span, - def_id, - self.cur_scope), - def::DefMethod(declid, provenence) => { - let sub_span = self.span.sub_span_for_meth_name(span); - let defid = if declid.krate == ast::LOCAL_CRATE { - let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, - declid); - match provenence { - def::FromTrait(def_id) => { - Some(ty::trait_items(&self.analysis.ty_cx, - def_id) - .iter() - .find(|mr| { - mr.name() == ti.name() - }) - .unwrap() - .def_id()) - } - def::FromImpl(def_id) => { - let impl_items = self.analysis - .ty_cx - .impl_items - .borrow(); - Some(impl_items.get(&def_id) - .unwrap() - .iter() - .find(|mr| { - ty::impl_or_trait_item( - &self.analysis.ty_cx, - mr.def_id() - ).name() == ti.name() - }) - .unwrap() - .def_id()) - } - } - } else { - None - }; - self.fmt.meth_call_str(span, - sub_span, - defid, - Some(declid), - self.cur_scope); - }, - def::DefFn(def_id, _) => { - self.fmt.fn_call_str(span, - sub_span, - def_id, - self.cur_scope) - } - _ => self.sess.span_bug(span, - &format!("Unexpected def kind while looking \ - up path in `{}`: `{:?}`", - self.span.snippet(span), - def)), - } - // modules or types in the path prefix - match def { - def::DefMethod(did, _) => { - let ti = ty::impl_or_trait_item(&self.analysis.ty_cx, did); - if let ty::MethodTraitItem(m) = ti { - if m.explicit_self == ty::StaticExplicitSelfCategory { - self.write_sub_path_trait_truncated(path); - } - } - } - def::DefLocal(_) | - def::DefStatic(_,_) | - def::DefConst(..) | - def::DefAssociatedConst(..) | - def::DefStruct(_) | - def::DefVariant(..) | - def::DefFn(..) => self.write_sub_paths_truncated(path, false), - _ => {}, - } - } - - fn process_struct_lit(&mut self, - ex: &ast::Expr, - path: &ast::Path, - fields: &Vec, - base: &Option>) { - if generated_code(path.span) { - return - } - - self.write_sub_paths_truncated(path, false); - - let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, ex).sty; - let struct_def = match *ty { - ty::ty_struct(def_id, _) => { - let sub_span = self.span.span_for_last_ident(path.span); - self.fmt.ref_str(recorder::StructRef, - path.span, - sub_span, - def_id, - self.cur_scope); - Some(def_id) - } - _ => None - }; - - for field in fields { - match struct_def { - Some(struct_def) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); - for f in &fields { - 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, - self.cur_scope); - } - } - } - None => {} - } - - self.visit_expr(&*field.expr) - } - visit::walk_expr_opt(self, base) - } - - fn process_method_call(&mut self, - ex: &ast::Expr, - args: &Vec>) { - let method_map = self.analysis.ty_cx.method_map.borrow(); - let method_callee = method_map.get(&ty::MethodCall::expr(ex.id)).unwrap(); - let (def_id, decl_id) = match method_callee.origin { - ty::MethodStatic(def_id) | - ty::MethodStaticClosure(def_id) => { - // method invoked on an object with a concrete type (not a static method) - let decl_id = - match ty::trait_item_of_item(&self.analysis.ty_cx, - def_id) { - None => None, - Some(decl_id) => Some(decl_id.def_id()), - }; - - // This incantation is required if the method referenced is a - // trait's default implementation. - let def_id = match ty::impl_or_trait_item(&self.analysis - .ty_cx, - def_id) { - ty::MethodTraitItem(method) => { - method.provided_source.unwrap_or(def_id) - } - _ => self.sess - .span_bug(ex.span, - "save::process_method_call: non-method \ - DefId in MethodStatic or MethodStaticClosure"), - }; - (Some(def_id), decl_id) - } - ty::MethodTypeParam(ref mp) => { - // method invoked on a type parameter - let trait_item = ty::trait_item(&self.analysis.ty_cx, - mp.trait_ref.def_id, - mp.method_num); - (None, Some(trait_item.def_id())) - } - ty::MethodTraitObject(ref mo) => { - // method invoked on a trait instance - let trait_item = ty::trait_item(&self.analysis.ty_cx, - mo.trait_ref.def_id, - mo.method_num); - (None, Some(trait_item.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, - self.cur_scope); - - // walk receiver and args - visit::walk_exprs(self, &args[..]); - } - - fn process_pat(&mut self, p:&ast::Pat) { - 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); - - let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def(); - let struct_def = match def { - def::DefConst(..) | def::DefAssociatedConst(..) => None, - def::DefVariant(_, variant_id, _) => Some(variant_id), - _ => { - match ty::ty_to_def_id(ty::node_id_to_type(&self.analysis.ty_cx, p.id)) { - None => { - self.sess.span_bug(p.span, - &format!("Could not find struct_def for `{}`", - self.span.snippet(p.span))); - } - Some(def_id) => Some(def_id), - } - } - }; - - if let Some(struct_def) = struct_def { - let struct_fields = ty::lookup_struct_fields(&self.analysis.ty_cx, struct_def); - for &Spanned { node: ref field, span } in fields { - let sub_span = self.span.span_for_first_ident(span); - for f in &struct_fields { - if f.name == field.ident.name { - self.fmt.ref_str(recorder::VarRef, - span, - sub_span, - f.id, - self.cur_scope); - break; - } - } - self.visit_pat(&*field.pat); - } - } - } - ast::PatEnum(ref path, _) | - ast::PatQPath(_, ref path) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); - visit::walk_pat(self, p); - } - ast::PatIdent(bm, ref path1, 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 - let path = ast_util::ident_to_path(path1.span,path1.node); - self.collected_paths.push((p.id, path, immut, recorder::VarRef)); - match *optional_subpattern { - None => {} - Some(ref subpattern) => self.visit_pat(&**subpattern) - } - } - _ => visit::walk_pat(self, p) - } - } -} - -impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { - fn visit_item(&mut self, item: &ast::Item) { - if generated_code(item.span) { - return - } - - match item.node { - ast::ItemUse(ref use_item) => { - match use_item.node { - ast::ViewPathSimple(ident, ref path) => { - let sub_span = self.span.span_for_last_ident(path.span); - let mod_id = match self.lookup_type_ref(item.id) { - Some(def_id) => { - match self.lookup_def_kind(item.id, path.span) { - Some(kind) => self.fmt.ref_str(kind, - path.span, - sub_span, - def_id, - self.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_after_keyword(use_item.span, keywords::As) { - Some(sub_span) => Some(sub_span), - None => sub_span, - }; - - self.fmt.use_alias_str(path.span, - sub_span, - item.id, - mod_id, - &get_ident(ident), - self.cur_scope); - self.write_sub_paths_truncated(path, true); - } - ast::ViewPathGlob(ref path) => { - // Make a comma-separated list of names of imported modules. - let mut name_string = String::new(); - let glob_map = &self.analysis.glob_map; - let glob_map = glob_map.as_ref().unwrap(); - if glob_map.contains_key(&item.id) { - for n in glob_map.get(&item.id).unwrap() { - if !name_string.is_empty() { - name_string.push_str(", "); - } - name_string.push_str(n.as_str()); - } - } - - let sub_span = self.span.sub_span_of_token(path.span, - token::BinOp(token::Star)); - self.fmt.use_glob_str(path.span, - sub_span, - item.id, - &name_string, - self.cur_scope); - self.write_sub_paths(path, true); - } - ast::ViewPathList(ref path, ref list) => { - for plid in list { - match plid.node { - ast::PathListIdent { id, .. } => { - match self.lookup_type_ref(id) { - Some(def_id) => - match self.lookup_def_kind(id, plid.span) { - Some(kind) => { - self.fmt.ref_str( - kind, plid.span, - Some(plid.span), - def_id, self.cur_scope); - } - None => () - }, - None => () - } - }, - ast::PathListMod { .. } => () - } - } - - self.write_sub_paths(path, true); - } - } - } - ast::ItemExternCrate(ref s) => { - let name = get_ident(item.ident); - let name = &name; - let location = match *s { - Some(s) => s.to_string(), - None => name.to_string(), - }; - let alias_span = self.span.span_for_last_ident(item.span); - let cnum = match self.sess.cstore.find_extern_mod_stmt_cnum(item.id) { - Some(cnum) => cnum, - None => 0, - }; - self.fmt.extern_crate_str(item.span, - alias_span, - item.id, - cnum, - name, - &location[..], - self.cur_scope); - } - ast::ItemFn(ref decl, _, _, ref ty_params, ref body) => - self.process_fn(item, &**decl, ty_params, &**body), - ast::ItemStatic(ref typ, mt, ref expr) => - self.process_static(item, &**typ, mt, &**expr), - ast::ItemConst(ref typ, ref expr) => - self.process_const(item.id, &item.ident, item.span, &*typ, &*expr), - ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), - ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), - ast::ItemImpl(_, _, - ref ty_params, - ref trait_ref, - ref typ, - ref impl_items) => { - self.process_impl(item, - ty_params, - trait_ref, - &**typ, - impl_items) - } - ast::ItemTrait(_, ref generics, ref trait_refs, ref methods) => - self.process_trait(item, generics, trait_refs, methods), - ast::ItemMod(ref m) => self.process_mod(item, m), - ast::ItemTy(ref ty, ref ty_params) => { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - let value = ty_to_string(&**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[..], - &value[..]); - - self.visit_ty(&**ty); - self.process_generic_params(ty_params, item.span, &qualname, item.id); - }, - ast::ItemMac(_) => (), - _ => visit::walk_item(self, item), - } - } - - fn visit_generics(&mut self, generics: &ast::Generics) { - for param in &*generics.ty_params { - for bound in &*param.bounds { - if let ast::TraitTyParamBound(ref trait_ref, _) = *bound { - self.process_trait_ref(&trait_ref.trait_ref); - } - } - if let Some(ref ty) = param.default { - self.visit_ty(&**ty); - } - } - } - - fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { - match trait_item.node { - ast::ConstTraitItem(ref ty, Some(ref expr)) => { - self.process_const(trait_item.id, &trait_item.ident, - trait_item.span, &*ty, &*expr); - } - ast::MethodTraitItem(ref sig, ref body) => { - self.process_method(sig, body.as_ref().map(|x| &**x), - trait_item.id, trait_item.ident.name, trait_item.span); - } - ast::ConstTraitItem(_, None) | - ast::TypeTraitItem(..) => {} - } - } - - fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { - match impl_item.node { - ast::ConstImplItem(ref ty, ref expr) => { - self.process_const(impl_item.id, &impl_item.ident, - impl_item.span, &ty, &expr); - } - ast::MethodImplItem(ref sig, ref body) => { - self.process_method(sig, Some(body), impl_item.id, - impl_item.ident.name, impl_item.span); - } - ast::TypeImplItem(_) | - ast::MacImplItem(_) => {} - } - } - - fn visit_ty(&mut self, t: &ast::Ty) { - if generated_code(t.span) { - return - } - - match t.node { - ast::TyPath(_, ref path) => { - match self.lookup_type_ref(t.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, - self.cur_scope); - }, - None => () - } - - self.write_sub_paths_truncated(path, false); - - visit::walk_path(self, path); - }, - _ => visit::walk_ty(self, t), - } - } - - fn visit_expr(&mut self, ex: &ast::Expr) { - if generated_code(ex.span) { - return - } - - match ex.node { - ast::ExprCall(ref _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); - } - ast::ExprPath(_, ref path) => { - self.process_path(ex.id, path.span, path, None); - visit::walk_expr(self, ex); - } - ast::ExprStruct(ref path, ref fields, ref base) => - self.process_struct_lit(ex, path, fields, base), - ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args), - ast::ExprField(ref sub_ex, ident) => { - if generated_code(sub_ex.span) { - return - } - - self.visit_expr(&**sub_ex); - let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty; - match *ty { - ty::ty_struct(def_id, _) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); - for f in &fields { - if f.name == ident.node.name { - let sub_span = self.span.span_for_last_ident(ex.span); - self.fmt.ref_str(recorder::VarRef, - ex.span, - sub_span, - f.id, - self.cur_scope); - break; - } - } - } - _ => self.sess.span_bug(ex.span, - &format!("Expected struct type, found {:?}", ty)), - } - }, - ast::ExprTupField(ref sub_ex, idx) => { - if generated_code(sub_ex.span) { - return - } - - self.visit_expr(&**sub_ex); - - let ty = &ty::expr_ty_adjusted(&self.analysis.ty_cx, &**sub_ex).sty; - match *ty { - ty::ty_struct(def_id, _) => { - let fields = ty::lookup_struct_fields(&self.analysis.ty_cx, def_id); - for (i, f) in fields.iter().enumerate() { - if i == idx.node { - let sub_span = self.span.sub_span_after_token(ex.span, token::Dot); - self.fmt.ref_str(recorder::VarRef, - ex.span, - sub_span, - f.id, - self.cur_scope); - break; - } - } - } - ty::ty_tup(_) => {} - _ => self.sess.span_bug(ex.span, - &format!("Expected struct or tuple \ - type, found {:?}", ty)), - } - }, - ast::ExprClosure(_, ref decl, ref body) => { - if generated_code(body.span) { - return - } - - let mut id = String::from_str("$"); - id.push_str(&ex.id.to_string()); - self.process_formals(&decl.inputs, &id[..]); - - // walk arg and return types - for arg in &decl.inputs { - self.visit_ty(&*arg.ty); - } - - if let ast::Return(ref ret_ty) = decl.output { - self.visit_ty(&**ret_ty); - } - - // walk the body - self.nest(ex.id, |v| v.visit_block(&**body)); - }, - _ => { - visit::walk_expr(self, ex) - }, - } - } - - fn visit_mac(&mut self, _: &ast::Mac) { - // Just stop, macros are poison to us. - } - - fn visit_pat(&mut self, p: &ast::Pat) { - self.process_pat(p); - if !self.collecting { - self.collected_paths.clear(); - } - } - - fn visit_arm(&mut self, arm: &ast::Arm) { - assert!(self.collected_paths.is_empty() && !self.collecting); - self.collecting = true; - for pattern in &arm.pats { - // collect paths from the arm's patterns - self.visit_pat(&**pattern); - } - - // This is to get around borrow checking, because we need mut self to call process_path. - let mut paths_to_process = vec![]; - // process collected paths - for &(id, ref p, ref immut, ref_kind) in &self.collected_paths { - 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)); - } - let def = def_map.get(&id).unwrap().full_def(); - match def { - def::DefLocal(id) => { - let value = if *immut { - self.span.snippet(p.span).to_string() - } else { - "".to_string() - }; - - assert!(p.segments.len() == 1, "qualified path for local variable def in arm"); - self.fmt.variable_str(p.span, - Some(p.span), - id, - &path_to_string(p), - &value[..], - "") - } - def::DefVariant(..) | def::DefTy(..) | def::DefStruct(..) => { - paths_to_process.push((id, p.clone(), Some(ref_kind))) - } - // FIXME(nrc) what are these doing here? - def::DefStatic(_, _) | - def::DefConst(..) | - def::DefAssociatedConst(..) => {} - _ => error!("unexpected definition kind when processing collected paths: {:?}", - def) - } - } - for &(id, ref path, ref_kind) in &paths_to_process { - self.process_path(id, path.span, path, ref_kind); - } - self.collecting = false; - self.collected_paths.clear(); - visit::walk_expr_opt(self, &arm.guard); - self.visit_expr(&*arm.body); - } - - fn visit_stmt(&mut self, s: &ast::Stmt) { - if generated_code(s.span) { - return - } - - visit::walk_stmt(self, s) - } - - fn visit_local(&mut self, l: &ast::Local) { - 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.is_empty() && !self.collecting); - self.collecting = true; - self.visit_pat(&*l.pat); - self.collecting = false; - - let value = self.span.snippet(l.span); - - for &(id, ref p, ref immut, _) in &self.collected_paths { - let value = if *immut { value.to_string() } else { "".to_string() }; - let types = self.analysis.ty_cx.node_types(); - let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap()); - // 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_string(p), - &value[..], - &typ[..]); - } - self.collected_paths.clear(); - - // Just walk the initialiser and type (don't want to walk the pattern again). - visit::walk_ty_opt(self, &l.ty); - visit::walk_expr_opt(self, &l.init); - } -} +mod dump_csv; #[allow(deprecated)] pub fn process_crate(sess: &Session, @@ -1562,27 +76,21 @@ pub fn process_crate(sess: &Session, }; root_path.pop(); - let mut visitor = DxrVisitor { - sess: sess, - analysis: analysis, - collected_paths: vec!(), - collecting: false, - fmt: FmtStrs::new(box Recorder { - out: output_file, - dump_spans: false, - }, - SpanUtils { - sess: sess, - err_count: Cell::new(0) - }), - span: SpanUtils { - sess: sess, - err_count: Cell::new(0) - }, - cur_scope: 0 - }; + let mut visitor = dump_csv::DumpCsvVisitor::new(sess, analysis, output_file); visitor.dump_crate_info(&cratename[..], krate); - visit::walk_crate(&mut visitor, krate); } + +// Utility functions for the module. + +// 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_id != NO_EXPANSION || span == DUMMY_SP +} From b248ee8746c4c016dd3001e59086f2c2f868f07e Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 5 May 2015 18:46:09 +1200 Subject: [PATCH 2/8] Use the new-style API for external crate listings --- src/librustc_trans/save/dump_csv.rs | 32 +++++++++++++++-------------- src/librustc_trans/save/mod.rs | 28 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index ab66123c4d739..bfd5a618769ae 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -27,7 +27,7 @@ //! the format of the output away from extracting it from the compiler. //! DumpCsvVisitor walks the AST and processes it. -use super::{escape, generated_code, recorder}; +use super::{escape, generated_code, recorder, SaveContext}; use session::Session; @@ -55,6 +55,7 @@ use util::ppaux; pub struct DumpCsvVisitor<'l, 'tcx: 'l> { + save_ctxt: SaveContext<'l>, sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, @@ -68,20 +69,12 @@ pub struct DumpCsvVisitor<'l, 'tcx: 'l> { } impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { - fn nest(&mut self, scope_id: NodeId, f: F) where - F: FnOnce(&mut DumpCsvVisitor<'l, 'tcx>), - { - let parent_scope = self.cur_scope; - self.cur_scope = scope_id; - f(self); - self.cur_scope = parent_scope; - } - pub fn new(sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, output_file: Box) -> DumpCsvVisitor<'l, 'tcx> { DumpCsvVisitor { sess: sess, + save_ctxt: SaveContext { sess: sess }, analysis: analysis, collected_paths: vec![], collecting: false, @@ -101,14 +94,23 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { } } + fn nest(&mut self, scope_id: NodeId, f: F) where + F: FnOnce(&mut DumpCsvVisitor<'l, 'tcx>), + { + let parent_scope = self.cur_scope; + self.cur_scope = scope_id; + f(self); + self.cur_scope = parent_scope; + } + pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { - // the current 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, n); - }); + // Dump info about all the external crates referenced from this crate. + for c in &self.save_ctxt.get_external_crates() { + self.fmt.external_crate_str(krate.span, &c.name, c.number); + } self.fmt.recorder.record("end_external_crates\n"); } diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7421344548c4f..260879ed324ea 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -23,6 +23,34 @@ mod recorder; mod dump_csv; +pub struct SaveContext<'l> { + sess: &'l Session, +} + +pub struct CrateData { + pub name: String, + pub number: u32, +} + +impl<'l> SaveContext<'l> { + pub fn new<'ll>(sess: &'ll Session) -> SaveContext<'ll> { + SaveContext { + sess: sess + } + } + + // List external crates used by the current crate. + pub fn get_external_crates(&self) -> Vec { + let mut result = Vec::new(); + + self.sess.cstore.iter_crate_data(|n, cmd| { + result.push(CrateData { name: cmd.name.clone(), number: n }); + }); + + result + } +} + #[allow(deprecated)] pub fn process_crate(sess: &Session, krate: &ast::Crate, From cea73bfb1596f1e5e1f3a6faa9863d2983a3ccea Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 5 May 2015 19:27:44 +1200 Subject: [PATCH 3/8] move out function data --- src/librustc_trans/save/dump_csv.rs | 7 +++- src/librustc_trans/save/mod.rs | 57 ++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index bfd5a618769ae..d3493251e2f42 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -55,7 +55,7 @@ use util::ppaux; pub struct DumpCsvVisitor<'l, 'tcx: 'l> { - save_ctxt: SaveContext<'l>, + save_ctxt: SaveContext<'l, 'tcx>, sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, @@ -74,7 +74,10 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { output_file: Box) -> DumpCsvVisitor<'l, 'tcx> { DumpCsvVisitor { sess: sess, - save_ctxt: SaveContext { sess: sess }, + save_ctxt: SaveContext::new(sess, analysis, SpanUtils { + sess: sess, + err_count: Cell::new(0) + }), analysis: analysis, collected_paths: vec![], collecting: false, diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 260879ed324ea..98672a546cb11 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -15,16 +15,22 @@ use std::env; use std::fs::{self, File}; use std::path::{Path, PathBuf}; -use syntax::{ast, attr, visit}; +use syntax::{attr, visit}; +use syntax::ast::{self, NodeId, DefId}; +use syntax::parse::token::keywords; use syntax::codemap::*; +use self::span_utils::SpanUtils; + mod span_utils; mod recorder; mod dump_csv; -pub struct SaveContext<'l> { +pub struct SaveContext<'l, 'tcx: 'l> { sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + span_utils: SpanUtils<'l>, } pub struct CrateData { @@ -32,10 +38,27 @@ pub struct CrateData { pub number: u32, } -impl<'l> SaveContext<'l> { - pub fn new<'ll>(sess: &'ll Session) -> SaveContext<'ll> { +pub enum Data { + FunctionData(FunctionData), +} + +pub struct FunctionData { + pub id: NodeId, + pub qualname: String, + pub declaration: Option, + pub span: Span, + pub scope: NodeId, +} + +impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { + pub fn new(sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + span_utils: SpanUtils<'l>) + -> SaveContext<'l, 'tcx> { SaveContext { - sess: sess + sess: sess, + analysis: analysis, + span_utils: span_utils, } } @@ -49,6 +72,30 @@ impl<'l> SaveContext<'l> { result } + + pub fn get_item_data(&self, item: &ast::Item) -> Data { + match item.node { + ast::Item_::ItemFn(..) => { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); + + Data::FunctionData(FunctionData { + id: item.id, + qualname: qualname, + declaration: None, + span: sub_span.unwrap(), + scope: self.analysis.ty_cx.map.get_parent(item.id), + }) + } + _ => { + unimplemented!(); + } + } + } + + pub fn get_data_for_id(&self, id: &NodeId) -> Data { + unimplemented!(); + } } #[allow(deprecated)] From c8ddb0f070db3bebfdc4f581cee1dc212a86bb80 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 5 May 2015 22:03:20 +1200 Subject: [PATCH 4/8] Extract PathCollector --- src/librustc_trans/save/dump_csv.rs | 68 ++++++----------------------- src/librustc_trans/save/mod.rs | 56 +++++++++++++++++++++++- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index d3493251e2f42..798c35874eeb1 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -27,7 +27,8 @@ //! the format of the output away from extracting it from the compiler. //! DumpCsvVisitor walks the AST and processes it. -use super::{escape, generated_code, recorder, SaveContext}; + +use super::{escape, generated_code, recorder, SaveContext, PathCollector}; use session::Session; @@ -59,9 +60,6 @@ pub struct DumpCsvVisitor<'l, 'tcx: 'l> { sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, - collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, - collecting: bool, - span: SpanUtils<'l>, fmt: FmtStrs<'l>, @@ -79,8 +77,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { err_count: Cell::new(0) }), analysis: analysis, - collected_paths: vec![], - collecting: false, span: SpanUtils { sess: sess, err_count: Cell::new(0) @@ -281,12 +277,11 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { fn process_formals(&mut self, formals: &Vec, qualname: &str) { for arg in formals { - assert!(self.collected_paths.is_empty() && !self.collecting); - self.collecting = true; - self.visit_pat(&*arg.pat); - self.collecting = false; + self.visit_pat(&arg.pat); + let mut collector = PathCollector::new(); + collector.visit_pat(&arg.pat); let span_utils = self.span.clone(); - for &(id, ref p, _, _) in &self.collected_paths { + for &(id, ref p, _, _) in &collector.collected_paths { let typ = ppaux::ty_to_string( &self.analysis.ty_cx, @@ -300,7 +295,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { &path_to_string(p), &typ[..]); } - self.collected_paths.clear(); } } @@ -1026,7 +1020,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { 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); let def = self.analysis.ty_cx.def_map.borrow().get(&p.id).unwrap().full_def(); @@ -1063,32 +1056,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { } } } - ast::PatEnum(ref path, _) | - ast::PatQPath(_, ref path) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); - visit::walk_pat(self, p); - } - ast::PatIdent(bm, ref path1, 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 - let path = ast_util::ident_to_path(path1.span,path1.node); - self.collected_paths.push((p.id, path, immut, recorder::VarRef)); - match *optional_subpattern { - None => {} - Some(ref subpattern) => self.visit_pat(&**subpattern) - } - } _ => visit::walk_pat(self, p) } } @@ -1421,23 +1388,20 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { fn visit_pat(&mut self, p: &ast::Pat) { self.process_pat(p); - if !self.collecting { - self.collected_paths.clear(); - } } fn visit_arm(&mut self, arm: &ast::Arm) { - assert!(self.collected_paths.is_empty() && !self.collecting); - self.collecting = true; + let mut collector = PathCollector::new(); for pattern in &arm.pats { // collect paths from the arm's patterns - self.visit_pat(&**pattern); + collector.visit_pat(&pattern); + self.visit_pat(&pattern); } // This is to get around borrow checking, because we need mut self to call process_path. let mut paths_to_process = vec![]; // process collected paths - for &(id, ref p, ref immut, ref_kind) in &self.collected_paths { + for &(id, ref p, ref immut, ref_kind) in &collector.collected_paths { let def_map = self.analysis.ty_cx.def_map.borrow(); if !def_map.contains_key(&id) { self.sess.span_bug(p.span, @@ -1475,8 +1439,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { for &(id, ref path, ref_kind) in &paths_to_process { self.process_path(id, path.span, path, ref_kind); } - self.collecting = false; - self.collected_paths.clear(); visit::walk_expr_opt(self, &arm.guard); self.visit_expr(&*arm.body); } @@ -1496,14 +1458,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { // The local could declare multiple new vars, we must walk the // pattern and collect them all. - assert!(self.collected_paths.is_empty() && !self.collecting); - self.collecting = true; - self.visit_pat(&*l.pat); - self.collecting = false; + let mut collector = PathCollector::new(); + collector.visit_pat(&l.pat); + self.visit_pat(&l.pat); let value = self.span.snippet(l.span); - for &(id, ref p, ref immut, _) in &self.collected_paths { + for &(id, ref p, ref immut, _) in &collector.collected_paths { let value = if *immut { value.to_string() } else { "".to_string() }; let types = self.analysis.ty_cx.node_types(); let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap()); @@ -1518,7 +1479,6 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { &value[..], &typ[..]); } - self.collected_paths.clear(); // Just walk the initialiser and type (don't want to walk the pattern again). visit::walk_ty_opt(self, &l.ty); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 98672a546cb11..dc1c36bd00111 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -15,10 +15,12 @@ use std::env; use std::fs::{self, File}; use std::path::{Path, PathBuf}; -use syntax::{attr, visit}; +use syntax::{attr}; use syntax::ast::{self, NodeId, DefId}; -use syntax::parse::token::keywords; +use syntax::ast_util; use syntax::codemap::*; +use syntax::parse::token::keywords; +use syntax::visit::{self, Visitor}; use self::span_utils::SpanUtils; @@ -94,10 +96,60 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } pub fn get_data_for_id(&self, id: &NodeId) -> Data { + // TODO unimplemented!(); } } +// An AST visitor for collecting paths from patterns. +struct PathCollector { + // TODO bool -> ast::mutable + // TODO recorder -> var kind new enum + // The Row field identifies the kind of formal variable. + collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, +} + +impl PathCollector { + fn new() -> PathCollector { + PathCollector { + collected_paths: vec![], + } + } +} + +impl<'v> Visitor<'v> for PathCollector { + fn visit_pat(&mut self, p: &ast::Pat) { + match p.node { + ast::PatStruct(ref path, _, _) => { + self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef)); + } + ast::PatEnum(ref path, _) | + ast::PatQPath(_, ref path) => { + self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); + } + ast::PatIdent(bm, ref path1, _) => { + 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 + let path = ast_util::ident_to_path(path1.span,path1.node); + self.collected_paths.push((p.id, path, immut, recorder::VarRef)); + } + _ => {} + } + visit::walk_pat(self, p); + } +} + #[allow(deprecated)] pub fn process_crate(sess: &Session, krate: &ast::Crate, From 83c6a12a54f6f9690cb4e1776483d108f653d24a Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 11 May 2015 08:35:08 +1200 Subject: [PATCH 5/8] save-analysis: start factoring out an API --- src/librustc_trans/save/dump_csv.rs | 99 ++++++++++++++--------------- src/librustc_trans/save/mod.rs | 96 ++++++++++++++++++++++------ src/librustc_trans/save/recorder.rs | 3 + 3 files changed, 129 insertions(+), 69 deletions(-) diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 798c35874eeb1..7f66d3a833fde 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -89,7 +89,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { sess: sess, err_count: Cell::new(0) }), - cur_scope: 0 + cur_scope: 0 } } @@ -108,7 +108,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { // Dump info about all the external crates referenced from this crate. for c in &self.save_ctxt.get_external_crates() { - self.fmt.external_crate_str(krate.span, &c.name, c.number); + self.fmt.external_crate_str(krate.span, &c.name, c.number); } self.fmt.recorder.record("end_external_crates\n"); } @@ -496,58 +496,52 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { decl: &ast::FnDecl, ty_params: &ast::Generics, body: &ast::Block) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let fn_data = self.save_ctxt.get_item_data(item); + if let super::Data::FunctionData(fn_data) = fn_data { + self.fmt.fn_str(item.span, + Some(fn_data.span), + fn_data.id, + &fn_data.qualname, + fn_data.scope); - let sub_span = self.span.sub_span_after_keyword(item.span, keywords::Fn); - self.fmt.fn_str(item.span, - sub_span, - item.id, - &qualname[..], - self.cur_scope); - self.process_formals(&decl.inputs, &qualname[..]); + self.process_formals(&decl.inputs, &fn_data.qualname); + self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id); + } else { + unreachable!(); + } - // walk arg and return types for arg in &decl.inputs { - self.visit_ty(&*arg.ty); + self.visit_ty(&arg.ty); } if let ast::Return(ref ret_ty) = decl.output { - self.visit_ty(&**ret_ty); + self.visit_ty(&ret_ty); } - // walk the body - self.nest(item.id, |v| v.visit_block(&*body)); - - self.process_generic_params(ty_params, item.span, &qualname[..], item.id); + self.nest(item.id, |v| v.visit_block(&body)); } - fn process_static(&mut self, - item: &ast::Item, - typ: &ast::Ty, - mt: ast::Mutability, - expr: &ast::Expr) + fn process_static_or_const_item(&mut self, + item: &ast::Item, + typ: &ast::Ty, + expr: &ast::Expr) { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); - - // If the variable is immutable, save the initialising expression. - let (value, keyword) = match mt { - ast::MutMutable => (String::from_str(""), keywords::Mut), - ast::MutImmutable => (self.span.snippet(expr.span), keywords::Static), - }; - - let sub_span = self.span.sub_span_after_keyword(item.span, keyword); - self.fmt.static_str(item.span, - sub_span, - item.id, - &get_ident(item.ident), - &qualname[..], - &value[..], - &ty_to_string(&*typ), - self.cur_scope); + let var_data = self.save_ctxt.get_item_data(item); + if let super::Data::VariableData(var_data) = var_data { + self.fmt.static_str(item.span, + Some(var_data.span), + var_data.id, + &var_data.name, + &var_data.qualname, + &var_data.value, + &var_data.type_value, + var_data.scope); + } else { + unreachable!(); + } - // walk type and init value - self.visit_ty(&*typ); + self.visit_ty(&typ); self.visit_expr(expr); } @@ -562,12 +556,13 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { let sub_span = self.span.sub_span_after_keyword(span, keywords::Const); + self.fmt.static_str(span, sub_span, id, &get_ident((*ident).clone()), &qualname[..], - "", + &self.span.snippet(expr.span), &ty_to_string(&*typ), self.cur_scope); @@ -1174,10 +1169,10 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { } ast::ItemFn(ref decl, _, _, ref ty_params, ref body) => self.process_fn(item, &**decl, ty_params, &**body), - ast::ItemStatic(ref typ, mt, ref expr) => - self.process_static(item, &**typ, mt, &**expr), + ast::ItemStatic(ref typ, _, ref expr) => + self.process_static_or_const_item(item, typ, expr), ast::ItemConst(ref typ, ref expr) => - self.process_const(item.id, &item.ident, item.span, &*typ, &*expr), + self.process_static_or_const_item(item, &typ, &expr), ast::ItemStruct(ref def, ref ty_params) => self.process_struct(item, &**def, ty_params), ast::ItemEnum(ref def, ref ty_params) => self.process_enum(item, def, ty_params), ast::ItemImpl(_, _, @@ -1378,7 +1373,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { }, _ => { visit::walk_expr(self, ex) - }, + } } } @@ -1401,7 +1396,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { // This is to get around borrow checking, because we need mut self to call process_path. let mut paths_to_process = vec![]; // process collected paths - for &(id, ref p, ref immut, ref_kind) in &collector.collected_paths { + for &(id, ref p, immut, ref_kind) in &collector.collected_paths { let def_map = self.analysis.ty_cx.def_map.borrow(); if !def_map.contains_key(&id) { self.sess.span_bug(p.span, @@ -1411,7 +1406,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { let def = def_map.get(&id).unwrap().full_def(); match def { def::DefLocal(id) => { - let value = if *immut { + let value = if immut == ast::MutImmutable { self.span.snippet(p.span).to_string() } else { "".to_string() @@ -1464,8 +1459,12 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> { let value = self.span.snippet(l.span); - for &(id, ref p, ref immut, _) in &collector.collected_paths { - let value = if *immut { value.to_string() } else { "".to_string() }; + for &(id, ref p, immut, _) in &collector.collected_paths { + let value = if immut == ast::MutImmutable { + value.to_string() + } else { + "".to_string() + }; let types = self.analysis.ty_cx.node_types(); let typ = ppaux::ty_to_string(&self.analysis.ty_cx, *types.get(&id).unwrap()); // Get the span only for the name of the variable (I hope the path diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index dc1c36bd00111..7e503c6819ba1 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -19,8 +19,10 @@ use syntax::{attr}; use syntax::ast::{self, NodeId, DefId}; use syntax::ast_util; use syntax::codemap::*; -use syntax::parse::token::keywords; +use syntax::parse::token::{self, get_ident, keywords}; use syntax::visit::{self, Visitor}; +use syntax::print::pprust::ty_to_string; + use self::span_utils::SpanUtils; @@ -40,18 +42,32 @@ pub struct CrateData { pub number: u32, } +// Data for any entity in the Rust language. The actual data contained varied +// with the kind of entity being queried. See the nested structs for details. pub enum Data { FunctionData(FunctionData), + VariableData(VariableData), } pub struct FunctionData { pub id: NodeId, + pub name: String, pub qualname: String, pub declaration: Option, pub span: Span, pub scope: NodeId, } +pub struct VariableData { + pub id: NodeId, + pub name: String, + pub qualname: String, + pub span: Span, + pub scope: NodeId, + pub value: String, + pub type_value: String, +} + impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { pub fn new(sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, @@ -78,35 +94,71 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { pub fn get_item_data(&self, item: &ast::Item) -> Data { match item.node { ast::Item_::ItemFn(..) => { - let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let name = self.analysis.ty_cx.map.path_to_string(item.id); + let qualname = format!("::{}", name); let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn); Data::FunctionData(FunctionData { id: item.id, + name: name, qualname: qualname, declaration: None, span: sub_span.unwrap(), scope: self.analysis.ty_cx.map.get_parent(item.id), }) } + ast::ItemStatic(ref typ, mt, ref expr) => { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + + // If the variable is immutable, save the initialising expression. + let value = match mt { + ast::MutMutable => String::from_str(""), + ast::MutImmutable => self.span_utils.snippet(expr.span), + }; + + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Static); + + Data::VariableData(VariableData { + id: item.id, + name: get_ident(item.ident).to_string(), + qualname: qualname, + span: sub_span.unwrap(), + scope: self.analysis.ty_cx.map.get_parent(item.id), + value: value, + type_value: ty_to_string(&typ), + }) + } + ast::ItemConst(ref typ, ref expr) => { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const); + + Data::VariableData(VariableData { + id: item.id, + name: get_ident(item.ident).to_string(), + qualname: qualname, + span: sub_span.unwrap(), + scope: self.analysis.ty_cx.map.get_parent(item.id), + value: self.span_utils.snippet(expr.span), + type_value: ty_to_string(&typ), + }) + } _ => { + // FIXME unimplemented!(); } } } - pub fn get_data_for_id(&self, id: &NodeId) -> Data { - // TODO - unimplemented!(); + pub fn get_data_for_id(&self, _id: &NodeId) -> Data { + // FIXME + unimplemented!(); } } // An AST visitor for collecting paths from patterns. struct PathCollector { - // TODO bool -> ast::mutable - // TODO recorder -> var kind new enum - // The Row field identifies the kind of formal variable. - collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, + // The Row field identifies the kind of pattern. + collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>, } impl PathCollector { @@ -119,29 +171,35 @@ impl PathCollector { impl<'v> Visitor<'v> for PathCollector { fn visit_pat(&mut self, p: &ast::Pat) { + if generated_code(p.span) { + return; + } + match p.node { ast::PatStruct(ref path, _, _) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::StructRef)); + self.collected_paths.push((p.id, + path.clone(), + ast::MutMutable, + recorder::StructRef)); } ast::PatEnum(ref path, _) | ast::PatQPath(_, ref path) => { - self.collected_paths.push((p.id, path.clone(), false, recorder::VarRef)); + self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::VarRef)); } ast::PatIdent(bm, ref path1, _) => { + debug!("PathCollector, visit ident in pat {}: {:?} {:?}", + token::get_ident(path1.node), + p.span, + path1.span); 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, - } - } + ast::BindByRef(_) => ast::MutImmutable, + ast::BindByValue(mt) => mt, }; // collect path for either visit_local or visit_arm - let path = ast_util::ident_to_path(path1.span,path1.node); + let path = ast_util::ident_to_path(path1.span, path1.node); self.collected_paths.push((p.id, path, immut, recorder::VarRef)); } _ => {} diff --git a/src/librustc_trans/save/recorder.rs b/src/librustc_trans/save/recorder.rs index db724b0ef6b65..193902d981d69 100644 --- a/src/librustc_trans/save/recorder.rs +++ b/src/librustc_trans/save/recorder.rs @@ -62,6 +62,9 @@ macro_rules! svec { }) } +// FIXME recorder should operate on super::Data, rather than lots of ad hoc +// data. + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Row { Variable, From 952614bc8b5e198ffa82dd0ecffc96b141a3cc91 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 14 May 2015 16:49:46 +1200 Subject: [PATCH 6/8] save-analysis: update the smoke test --- src/test/run-make/save-analysis/Makefile | 5 +- src/test/run-make/save-analysis/foo.rs | 104 +++++++++++++--------- src/test/run-make/save-analysis/krate2.rs | 18 ++++ 3 files changed, 84 insertions(+), 43 deletions(-) create mode 100644 src/test/run-make/save-analysis/krate2.rs diff --git a/src/test/run-make/save-analysis/Makefile b/src/test/run-make/save-analysis/Makefile index e1cbf7549467e..701bdee1109ed 100644 --- a/src/test/run-make/save-analysis/Makefile +++ b/src/test/run-make/save-analysis/Makefile @@ -1,3 +1,6 @@ -include ../tools.mk -all: +all: code +krate2: krate2.rs + $(RUSTC) $< +code: foo.rs krate2 $(RUSTC) foo.rs -Zsave-analysis diff --git a/src/test/run-make/save-analysis/foo.rs b/src/test/run-make/save-analysis/foo.rs index 4d75e58aad938..baf6999b10a69 100644 --- a/src/test/run-make/save-analysis/foo.rs +++ b/src/test/run-make/save-analysis/foo.rs @@ -9,43 +9,52 @@ // except according to those terms. #![ crate_name = "test" ] -#![allow(unstable)] -#![feature(box_syntax, rustc_private, core, zero_one)] +#![feature(box_syntax)] +#![feature(rustc_private)] + extern crate graphviz; // A simple rust project +extern crate krate2; +extern crate krate2 as krate3; extern crate flate as myflate; +use graphviz::RenderOption; use std::collections::{HashMap,HashSet}; use std::cell::RefCell; +use std::io::Write; use sub::sub2 as msalias; use sub::sub2; use sub::sub2::nested_struct as sub_struct; -use std::num::One; use std::mem::size_of; +use std::char::from_u32; + static uni: &'static str = "Les Miséééééééérables"; static yy: usize = 25; -static bob: Option<&'static [isize]> = None; +static bob: Option = None; // buglink test - see issue #1337. fn test_alias(i: Option<::Item>) { - let s = sub_struct{ field2: 45, }; + let s = sub_struct{ field2: 45u32, }; // import tests - fn foo(x: &One) {} + fn foo(x: &Write) {} + let _: Option<_> = from_u32(45); - let x = 42; + let x = 42usize; + krate2::hello(); + krate3::hello(); myflate::deflate_bytes(&[]); - let x = (3, 4); + let x = (3isize, 4usize); let y = x.1; } @@ -55,15 +64,21 @@ fn test_tup_struct(x: TupStruct) -> isize { x.1 } +fn println(s: &str) { + std::io::stdout().write_all(s.as_bytes()); +} + mod sub { pub mod sub2 { + use std::io::Write; pub mod sub3 { + use std::io::Write; pub fn hello() { - println!("hello from module 3"); + ::println("hello from module 3"); } } pub fn hello() { - println!("hello from a module"); + ::println("hello from a module"); } pub struct nested_struct { @@ -93,14 +108,14 @@ struct some_fields { type SF = some_fields; trait SuperTrait { - fn dummy(&self) { } + fn qux(&self) { panic!(); } } trait SomeTrait: SuperTrait { fn Method(&self, x: u32) -> u32; fn prov(&self, x: u32) -> u32 { - println!("{}", &x.to_string()); + println(&x.to_string()); 42 } fn provided_method(&self) -> u32 { @@ -116,7 +131,7 @@ trait SubTrait: SomeTrait { impl SomeTrait for some_fields { fn Method(&self, x: u32) -> u32 { - println!("{}", &x.to_string()); + println(&x.to_string()); self.field1 } } @@ -128,7 +143,7 @@ impl SubTrait for some_fields {} impl some_fields { fn stat(x: u32) -> u32 { - println!("{}", &x.to_string()); + println(&x.to_string()); 42 } fn stat2(x: &some_fields) -> u32 { @@ -136,6 +151,7 @@ impl some_fields { } fn align_to(&mut self) { + } fn test(&mut self) { @@ -188,20 +204,18 @@ enum SomeStructEnum { fn matchSomeEnum(val: SomeEnum) { match val { - SomeEnum::Ints(int1, int2) => { println!("{}", &(int1+int2).to_string()); } - SomeEnum::Floats(float1, float2) => { println!("{}", &(float2*float1).to_string()); } - SomeEnum::Strings(_, _, s3) => { println!("{}", s3); } - SomeEnum::MyTypes(mt1, mt2) => { - println!("{}", &(mt1.field1 - mt2.field1).to_string()); - } + SomeEnum::Ints(int1, int2) => { println(&(int1+int2).to_string()); } + SomeEnum::Floats(float1, float2) => { println(&(float2*float1).to_string()); } + SomeEnum::Strings(_, _, s3) => { println(s3); } + SomeEnum::MyTypes(mt1, mt2) => { println(&(mt1.field1 - mt2.field1).to_string()); } } } fn matchSomeStructEnum(se: SomeStructEnum) { match se { - SomeStructEnum::EnumStruct{a:a, ..} => println!("{}", &a.to_string()), - SomeStructEnum::EnumStruct2{f1:f1, f2:f_2} => println!("{}", &f_2.field1.to_string()), - SomeStructEnum::EnumStruct3{f1, ..} => println!("{}", &f1.field1.to_string()), + SomeStructEnum::EnumStruct{a:a, ..} => println(&a.to_string()), + SomeStructEnum::EnumStruct2{f1:f1, f2:f_2} => println(&f_2.field1.to_string()), + SomeStructEnum::EnumStruct3{f1, ..} => println(&f1.field1.to_string()), } } @@ -209,9 +223,9 @@ fn matchSomeStructEnum(se: SomeStructEnum) { fn matchSomeStructEnum2(se: SomeStructEnum) { use SomeStructEnum::*; match se { - EnumStruct{a: ref aaa, ..} => println!("{}", &aaa.to_string()), - EnumStruct2{f1, f2: f2} => println!("{}", &f1.field1.to_string()), - EnumStruct3{f1, f3: SomeEnum::Ints(_, _), f2} => println!("{}", &f1.field1.to_string()), + EnumStruct{a: ref aaa, ..} => println(&aaa.to_string()), + EnumStruct2{f1, f2: f2} => println(&f1.field1.to_string()), + EnumStruct3{f1, f3: SomeEnum::Ints(_, _), f2} => println(&f1.field1.to_string()), _ => {}, } } @@ -219,30 +233,29 @@ fn matchSomeStructEnum2(se: SomeStructEnum) { fn matchSomeOtherEnum(val: SomeOtherEnum) { use SomeOtherEnum::{SomeConst2, SomeConst3}; match val { - SomeOtherEnum::SomeConst1 => { println!("I'm const1."); } - SomeConst2 | SomeConst3 => { println!("I'm const2 or const3."); } + SomeOtherEnum::SomeConst1 => { println("I'm const1."); } + SomeConst2 | SomeConst3 => { println("I'm const2 or const3."); } } } fn hello((z, a) : (u32, String), ex: X) { SameDir2::hello(43); - println!("{}", &yy.to_string()); + println(&yy.to_string()); let (x, y): (u32, u32) = (5, 3); - println!("{}", &x.to_string()); - println!("{}", &z.to_string()); + println(&x.to_string()); + println(&z.to_string()); let x: u32 = x; - println!("{}", &x.to_string()); + println(&x.to_string()); let x = "hello"; - println!("{}", x); + println(x); let x = 32.0f32; let _ = (x + ((x * x) + 1.0).sqrt()).ln(); - // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. - let s: Box = Box::new(some_fields {field1: 43}); + let s: Box = box some_fields {field1: 43}; let s2: Box = box some_fields {field1: 43}; - let s3: Box<_> = box nofields; + let s3 = box nofields; s.Method(43); s3.Method(43); @@ -253,8 +266,6 @@ fn hello((z, a) : (u32, String), ex: X) { let y: u32 = 56; // static method on struct let r = some_fields::stat(y); - // trait static method, calls override - let r = SubTrait::stat2(&*s2); // trait static method, calls default let r = SubTrait::stat2(&*s3); @@ -277,7 +288,7 @@ pub struct blah { } fn main() { // foo - let s: Box<_> = box some_fields {field1: 43}; + let s = box some_fields {field1: 43}; hello((43, "a".to_string()), *s); sub::sub2::hello(); sub2::sub3::hello(); @@ -306,7 +317,7 @@ fn main() { // foo let s3: some_fields = some_fields{ field1: 55}; let s4: msalias::nested_struct = sub::sub2::nested_struct{ field2: 55}; let s4: msalias::nested_struct = sub2::nested_struct{ field2: 55}; - println!("{}", &s2.field1.to_string()); + println(&s2.field1.to_string()); let s5: MyType = box some_fields{ field1: 55}; let s = SameDir::SameStruct{name: "Bob".to_string()}; let s = SubDir::SubStruct{name:"Bob".to_string()}; @@ -316,9 +327,18 @@ fn main() { // foo matchSomeEnum(s7); let s8: SomeOtherEnum = SomeOtherEnum::SomeConst2; matchSomeOtherEnum(s8); - let s9: SomeStructEnum = - SomeStructEnum::EnumStruct2{f1: box some_fields{field1:10}, f2: box s2}; + let s9: SomeStructEnum = SomeStructEnum::EnumStruct2{ f1: box some_fields{ field1:10 }, + f2: box s2 }; matchSomeStructEnum(s9); + + for x in &vec![1, 2, 3] { + let _y = x; + } + + let s7: SomeEnum = SomeEnum::Strings("one", "two", "three"); + if let SomeEnum::Strings(..) = s7 { + println!("hello!"); + } } impl Iterator for nofields { diff --git a/src/test/run-make/save-analysis/krate2.rs b/src/test/run-make/save-analysis/krate2.rs new file mode 100644 index 0000000000000..2c6f517ff3882 --- /dev/null +++ b/src/test/run-make/save-analysis/krate2.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![ crate_name = "krate2" ] +#![ crate_type = "lib" ] + +use std::io::Write; + +pub fn hello() { + std::io::stdout().write_all(b"hello world!\n"); +} From 7ca560d6abc0217943db436bb4b738bf6977cf93 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 14 May 2015 17:44:08 +1200 Subject: [PATCH 7/8] save-analysis: fix a bracket counting bug --- src/librustc_trans/save/mod.rs | 8 ++++---- src/librustc_trans/save/span_utils.rs | 17 +++++++---------- src/test/run-make/save-analysis/foo.rs | 12 ++++++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7e503c6819ba1..1b9976be72f42 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -111,12 +111,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(item.id)); // If the variable is immutable, save the initialising expression. - let value = match mt { - ast::MutMutable => String::from_str(""), - ast::MutImmutable => self.span_utils.snippet(expr.span), + let (value, keyword) = match mt { + ast::MutMutable => (String::from_str(""), keywords::Mut), + ast::MutImmutable => (self.span_utils.snippet(expr.span), keywords::Static), }; - let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Static); + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); Data::VariableData(VariableData { id: item.id, diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs index 504663571f533..ba027e4c2d192 100644 --- a/src/librustc_trans/save/span_utils.rs +++ b/src/librustc_trans/save/span_utils.rs @@ -237,7 +237,7 @@ impl<'a> SpanUtils<'a> { let mut toks = self.retokenise_span(span); // We keep track of how many brackets we're nested in - let mut bracket_count = 0; + let mut bracket_count: isize = 0; let mut found_ufcs_sep = false; loop { let ts = toks.real_token(); @@ -255,19 +255,16 @@ impl<'a> SpanUtils<'a> { } bracket_count += match ts.tok { token::Lt => 1, - token::Gt => { - // Ignore the `>::` in `::AssocTy`. - if !found_ufcs_sep && bracket_count == 0 { - found_ufcs_sep = true; - 0 - } else { - -1 - } - } + token::Gt => -1, token::BinOp(token::Shl) => 2, token::BinOp(token::Shr) => -2, _ => 0 }; + // Ignore the `>::` in `::AssocTy`. + if !found_ufcs_sep && bracket_count == -1 { + found_ufcs_sep = true; + bracket_count += 1 + } if ts.tok.is_ident() && bracket_count == nesting { result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); } diff --git a/src/test/run-make/save-analysis/foo.rs b/src/test/run-make/save-analysis/foo.rs index baf6999b10a69..07b99dff4e0fa 100644 --- a/src/test/run-make/save-analysis/foo.rs +++ b/src/test/run-make/save-analysis/foo.rs @@ -352,3 +352,15 @@ impl Iterator for nofields { panic!() } } + +trait Pattern<'a> { + type Searcher; +} + +struct CharEqPattern; + +impl<'a> Pattern<'a> for CharEqPattern { + type Searcher = CharEqPattern; +} + +struct CharSearcher<'a>(>::Searcher); From 7555e7081df536796aa7163a456b6f8cc4649595 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Fri, 15 May 2015 19:06:56 +1200 Subject: [PATCH 8/8] comments --- src/librustc_trans/save/mod.rs | 8 ++++++-- src/librustc_trans/save/span_utils.rs | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 1b9976be72f42..c5c4a75ef823b 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -42,13 +42,16 @@ pub struct CrateData { pub number: u32, } -// Data for any entity in the Rust language. The actual data contained varied -// with the kind of entity being queried. See the nested structs for details. +/// Data for any entity in the Rust language. The actual data contained varied +/// with the kind of entity being queried. See the nested structs for details. pub enum Data { + /// Data for all kinds of functions and methods. FunctionData(FunctionData), + /// Data for local and global variables (consts and statics). VariableData(VariableData), } +/// Data for all kinds of functions and methods. pub struct FunctionData { pub id: NodeId, pub name: String, @@ -58,6 +61,7 @@ pub struct FunctionData { pub scope: NodeId, } +/// Data for local and global variables (consts and statics). pub struct VariableData { pub id: NodeId, pub name: String, diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs index ba027e4c2d192..c3ac805af27ec 100644 --- a/src/librustc_trans/save/span_utils.rs +++ b/src/librustc_trans/save/span_utils.rs @@ -230,8 +230,8 @@ impl<'a> SpanUtils<'a> { // 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> - // Nesting = 0: all idents outside of brackets: Vec - // Nesting = 1: idents within one level of brackets: Vec + // 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: isize, limit: isize) -> Vec { let mut result: Vec = vec!(); @@ -260,10 +260,20 @@ impl<'a> SpanUtils<'a> { token::BinOp(token::Shr) => -2, _ => 0 }; + // Ignore the `>::` in `::AssocTy`. + + // The root cause of this hack is that the AST representation of + // qpaths is horrible. It treats ::C as a path with two + // segments, B and C and notes that there is also a self type A at + // position 0. Because we don't have spans for individual idents, + // only the whole path, we have to iterate over the tokens in the + // path, trying to pull out the non-nested idents (e.g., avoiding 'a + // in `>::C`). So we end up with a span for `B>::C` from + // the start of the first ident to the end of the path. if !found_ufcs_sep && bracket_count == -1 { found_ufcs_sep = true; - bracket_count += 1 + bracket_count += 1; } if ts.tok.is_ident() && bracket_count == nesting { result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); @@ -332,7 +342,7 @@ impl<'a> SpanUtils<'a> { } - // Returns a list of the spans of idents in a patch. + // Returns a list of the spans of idents in a path. // E.g., For foo::bar::baz, we return [foo, bar, baz] (well, their spans) pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec { if generated_code(path.span) {