diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs new file mode 100644 index 0000000000000..7f66d3a833fde --- /dev/null +++ b/src/librustc_trans/save/dump_csv.rs @@ -0,0 +1,1486 @@ +// 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, SaveContext, PathCollector}; + +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> { + save_ctxt: SaveContext<'l, 'tcx>, + sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + + span: SpanUtils<'l>, + fmt: FmtStrs<'l>, + + cur_scope: NodeId +} + +impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { + pub fn new(sess: &'l Session, + analysis: &'l ty::CrateAnalysis<'tcx>, + output_file: Box) -> DumpCsvVisitor<'l, 'tcx> { + DumpCsvVisitor { + sess: sess, + save_ctxt: SaveContext::new(sess, analysis, SpanUtils { + sess: sess, + err_count: Cell::new(0) + }), + analysis: analysis, + 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 + } + } + + 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. + self.fmt.crate_str(krate.span, name); + + // 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"); + } + + // 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 { + 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 &collector.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[..]); + } + } + } + + 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 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); + + + self.process_formals(&decl.inputs, &fn_data.qualname); + self.process_generic_params(ty_params, item.span, &fn_data.qualname, item.id); + } else { + unreachable!(); + } + + for arg in &decl.inputs { + self.visit_ty(&arg.ty); + } + + if let ast::Return(ref ret_ty) = decl.output { + self.visit_ty(&ret_ty); + } + + self.nest(item.id, |v| v.visit_block(&body)); + } + + fn process_static_or_const_item(&mut self, + item: &ast::Item, + typ: &ast::Ty, + expr: &ast::Expr) + { + 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!(); + } + + 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[..], + &self.span.snippet(expr.span), + &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, _) => { + 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); + } + } + } + _ => 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, _, ref expr) => + self.process_static_or_const_item(item, typ, expr), + ast::ItemConst(ref typ, ref 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(_, _, + 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); + } + + fn visit_arm(&mut self, arm: &ast::Arm) { + let mut collector = PathCollector::new(); + for pattern in &arm.pats { + // collect paths from the arm's patterns + 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, 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, + &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 == ast::MutImmutable { + 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); + } + 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. + 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, 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 + // 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[..]); + } + + // 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..c5c4a75ef823b 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,1504 +8,207 @@ // 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::{attr}; use syntax::ast::{self, NodeId, DefId}; -use syntax::ast_map::NodeItem; -use syntax::attr; +use syntax::ast_util; 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 syntax::print::pprust::ty_to_string; -use self::span_utils::SpanUtils; -use self::recorder::{Recorder, FmtStrs}; -use util::ppaux; +use self::span_utils::SpanUtils; 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 -} +mod dump_csv; -struct DxrVisitor<'l, 'tcx: 'l> { +pub struct SaveContext<'l, 'tcx: 'l> { sess: &'l Session, analysis: &'l ty::CrateAnalysis<'tcx>, + span_utils: SpanUtils<'l>, +} + +pub struct CrateData { + pub name: String, + pub number: u32, +} - collected_paths: Vec<(NodeId, ast::Path, bool, recorder::Row)>, - collecting: bool, +/// 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), +} - span: SpanUtils<'l>, - fmt: FmtStrs<'l>, +/// Data for all kinds of functions and methods. +pub struct FunctionData { + pub id: NodeId, + pub name: String, + pub qualname: String, + pub declaration: Option, + pub span: Span, + pub scope: NodeId, +} - cur_scope: NodeId +/// Data for local and global variables (consts and statics). +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> 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; +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, + analysis: analysis, + span_utils: span_utils, + } } - fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) { - // the current crate - self.fmt.crate_str(krate.span, name); + // List external crates used by the current crate. + pub fn get_external_crates(&self) -> Vec { + let mut result = Vec::new(); - // 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); + result.push(CrateData { name: cmd.name.clone(), number: 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[..]); + pub fn get_item_data(&self, item: &ast::Item) -> Data { + match item.node { + ast::Item_::ItemFn(..) => { + 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), + }) } - 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); - } + ast::ItemStatic(ref typ, mt, ref expr) => { + let qualname = format!("::{}", self.analysis.ty_cx.map.path_to_string(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); + // 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_utils.snippet(expr.span), keywords::Static), + }; - for field in &struct_def.fields { - self.process_struct_field_def(field, &qualname, variant.node.id); - self.visit_ty(&*field.node.ty); - } - } + let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword); + + 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), + }) } - } - - 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); + 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), + }) } - } - - 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 => () + _ => { + // FIXME + unimplemented!(); } } - - // 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)); + pub fn get_data_for_id(&self, _id: &NodeId) -> Data { + // FIXME + unimplemented!(); } +} - 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 => {} - } +// An AST visitor for collecting paths from patterns. +struct PathCollector { + // The Row field identifies the kind of pattern. + collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>, +} - self.visit_expr(&*field.expr) +impl PathCollector { + fn new() -> PathCollector { + PathCollector { + collected_paths: vec![], } - 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) { +impl<'v> Visitor<'v> for PathCollector { + fn visit_pat(&mut self, p: &ast::Pat) { if generated_code(p.span) { - return + 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::PatStruct(ref path, _, _) => { + 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)); - visit::walk_pat(self, p); + self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::VarRef)); } - ast::PatIdent(bm, ref path1, ref optional_subpattern) => { + 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)); - 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); + visit::walk_pat(self, p); } } @@ -1562,27 +265,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 +} 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, diff --git a/src/librustc_trans/save/span_utils.rs b/src/librustc_trans/save/span_utils.rs index 504663571f533..c3ac805af27ec 100644 --- a/src/librustc_trans/save/span_utils.rs +++ b/src/librustc_trans/save/span_utils.rs @@ -230,14 +230,14 @@ 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!(); 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,26 @@ 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`. + + // 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; + } if ts.tok.is_ident() && bracket_count == nesting { result.push(self.make_sub_span(span, Some(ts.sp)).unwrap()); } @@ -335,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) { 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..07b99dff4e0fa 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 { @@ -332,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); 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"); +}