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