1
Fork 0

auto merge of #13222 : nick29581/rust/dxr4, r=brson

Adds a -Z flag `save-analysis` which runs after the analysis phase of the compiler and saves a bunch of info into a CSV file for the crate. This is designed to work with the DXR code browser, but is frontend-independent, that is this info should be useful for all kinds of code browsers, IDEs, or other tools.

I need to squash commits before landing (there will probably be a fair few to come), please ignore them for now and just comment on the changes.
This commit is contained in:
bors 2014-06-13 09:57:06 +00:00
commit c119903f62
11 changed files with 2531 additions and 37 deletions

View file

@ -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

View file

@ -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<Path>) {
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,

View file

@ -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,

View file

@ -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 {

File diff suppressed because it is too large Load diff

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::save::escape;
use middle::save::span_utils::SpanUtils;
use std::vec::Vec;
use syntax::ast;
use syntax::ast::{NodeId,DefId};
use syntax::codemap::*;
pub struct Recorder {
// output file
pub out: Box<Writer>,
pub dump_spans: bool,
}
impl Recorder {
pub fn record(&mut self, info: &str) {
match write!(self.out, "{}", info) {
Err(_) => error!("Error writing output '{}'", info),
_ => (),
}
}
pub fn dump_span(&mut self,
su: SpanUtils,
kind: &str,
span: Span,
_sub_span: Option<Span>) {
assert!(self.dump_spans);
let result = format!("span,kind,{},{},text,\"{}\"\n",
kind, su.extent_str(span), escape(su.snippet(span)));
self.record(result.as_slice());
}
}
pub struct FmtStrs<'a> {
pub recorder: Box<Recorder>,
span: SpanUtils<'a>,
krate: String,
}
macro_rules! s { ($e:expr) => { format!("{}", $e) }}
macro_rules! svec {
($($e:expr),*) => ({
// leading _ to allow empty construction without a warning.
let mut _temp = ::std::vec::Vec::new();
$(_temp.push(s!($e));)*
_temp
})
}
pub enum Row {
Variable,
Enum,
Variant,
VariantStruct,
Function,
MethodDecl,
Struct,
Trait,
Impl,
Module,
UseAlias,
ExternCrate,
Inheritance,
MethodCall,
Typedef,
ExternalCrate,
Crate,
FnCall,
ModRef,
VarRef,
TypeRef,
StructRef,
FnRef,
}
impl<'a> FmtStrs<'a> {
pub fn new(rec: Box<Recorder>, span: SpanUtils<'a>, krate: String) -> FmtStrs<'a> {
FmtStrs {
recorder: rec,
span: span,
krate: krate,
}
}
// A map from kind of item to a tuple of
// a string representation of the name
// a vector of field names
// whether this kind requires a span
// whether dump_spans should dump for this kind
fn lookup_row(r: Row) -> (&'static str, Vec<&'static str>, bool, bool) {
match r {
Variable => ("variable",
vec!("id","name","qualname","value","type","scopeid"),
true, true),
Enum => ("enum", vec!("id","qualname","scopeid"), true, true),
Variant => ("variant", vec!("id","name","qualname","value","scopeid"), true, true),
VariantStruct => ("variant_struct",
vec!("id","ctor_id","qualname","value","scopeid"), true, true),
Function => ("function", vec!("id","qualname","declid","declidcrate","scopeid"),
true, true),
MethodDecl => ("method_decl", vec!("id","qualname","scopeid"), true, true),
Struct => ("struct", vec!("id","ctor_id","qualname","scopeid"), true, true),
Trait => ("trait", vec!("id","qualname","scopeid"), true, true),
Impl => ("impl", vec!("id","refid","refidcrate","scopeid"), true, true),
Module => ("module", vec!("id","qualname","scopeid","def_file"), true, false),
UseAlias => ("use_alias",
vec!("id","refid","refidcrate","name","scopeid"),
true, true),
ExternCrate => ("extern_crate",
vec!("id","name","location","crate","scopeid"),
true, true),
Inheritance => ("inheritance",
vec!("base","basecrate","derived","derivedcrate"),
true, false),
MethodCall => ("method_call",
vec!("refid","refidcrate","declid","declidcrate","scopeid"),
true, true),
Typedef => ("typedef", vec!("id","qualname","value"), true, true),
ExternalCrate => ("external_crate", vec!("name","crate","file_name"), false, false),
Crate => ("crate", vec!("name"), true, false),
FnCall => ("fn_call", vec!("refid","refidcrate","qualname","scopeid"), true, true),
ModRef => ("mod_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true),
VarRef => ("var_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true),
TypeRef => ("type_ref",
vec!("refid","refidcrate","qualname","scopeid"),
true, true),
StructRef => ("struct_ref",
vec!("refid","refidcrate","qualname","scopeid"),
true, true),
FnRef => ("fn_ref", vec!("refid","refidcrate","qualname","scopeid"), true, true)
}
}
pub fn make_values_str(&self,
kind: &'static str,
fields: &Vec<&'static str>,
values: Vec<String>,
span: Span) -> Option<String> {
if values.len() != fields.len() {
self.span.sess.span_bug(span, format!(
"Mismatch between length of fields for '{}', expected '{}', found '{}'",
kind, fields.len(), values.len()).as_slice());
}
let values = values.iter().map(|s| {
if s.len() > 1020 {
s.as_slice().slice_to(1020)
} else {
s.as_slice()
}
});
let pairs = fields.iter().zip(values);
let mut strs = pairs.map(|(f, v)| format!(",{},\"{}\"", f, escape(
if *f == "qualname" {
self.krate.clone().append("::").append(v)
} else {
String::from_str(v)
}
)));
Some(strs.fold(String::new(), |s, ss| s.append(ss.as_slice()))).map(|s| s.into_owned())
}
pub fn record_without_span(&mut self,
kind: Row,
values: Vec<String>,
span: Span) {
let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
if needs_span {
self.span.sess.span_bug(span, format!(
"Called record_without_span for '{}' which does requires a span",
label).as_slice());
}
assert!(!dump_spans);
if self.recorder.dump_spans {
return;
}
let values_str = match self.make_values_str(label, fields, values, span) {
Some(vs) => vs,
None => return,
};
let result = String::from_str(label);
self.recorder.record(result.append(values_str.as_slice()).append("\n").as_slice());
}
pub fn record_with_span(&mut self,
kind: Row,
span: Span,
sub_span: Span,
values: Vec<String>) {
let (label, ref fields, needs_span, dump_spans) = FmtStrs::lookup_row(kind);
if self.recorder.dump_spans {
if dump_spans {
self.recorder.dump_span(self.span, label, span, Some(sub_span));
}
return;
}
if !needs_span {
self.span.sess.span_bug(span,
format!("Called record_with_span for '{}' \
which does not require a span", label).as_slice());
}
let values_str = match self.make_values_str(label, fields, values, span) {
Some(vs) => vs,
None => return,
};
let result = format!("{},{}{}\n", label, self.span.extent_str(sub_span), values_str);
self.recorder.record(result.as_slice());
}
pub fn check_and_record(&mut self,
kind: Row,
span: Span,
sub_span: Option<Span>,
values: Vec<String>) {
match sub_span {
Some(sub_span) => self.record_with_span(kind, span, sub_span, values),
None => {
let (label, _, _, _) = FmtStrs::lookup_row(kind);
self.span.report_span_err(label, span);
}
}
}
pub fn variable_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
value: &str,
typ: &str) {
// Getting a fully qualified name for a variable is hard because in
// the local case they can be overridden in one block and there is no nice way
// to refer to such a scope in english, so we just hack it by appending the
// variable def's node id
let qualname = String::from_str(name).append("$").append(id.to_str().as_slice());
self.check_and_record(Variable,
span,
sub_span,
svec!(id, name, qualname, value, typ, 0));
}
// formal parameters
pub fn formal_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
fn_name: &str,
name: &str,
typ: &str) {
let qualname = String::from_str(fn_name).append("::").append(name);
self.check_and_record(Variable,
span,
sub_span,
svec!(id, name, qualname, "", typ, 0));
}
// value is the initialising expression of the static if it is not mut, otherwise "".
pub fn static_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
qualname: &str,
value: &str,
typ: &str,
scope_id: NodeId) {
self.check_and_record(Variable,
span,
sub_span,
svec!(id, name, qualname, value, typ, scope_id));
}
pub fn field_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
qualname: &str,
typ: &str,
scope_id: NodeId) {
self.check_and_record(Variable,
span,
sub_span,
svec!(id, name, qualname, "", typ, scope_id));
}
pub fn enum_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
scope_id: NodeId) {
self.check_and_record(Enum,
span,
sub_span,
svec!(id, name, scope_id));
}
pub fn tuple_variant_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
qualname: &str,
val: &str,
scope_id: NodeId) {
self.check_and_record(Variant,
span,
sub_span,
svec!(id, name, qualname, val, scope_id));
}
pub fn struct_variant_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
ctor_id: NodeId,
name: &str,
val: &str,
scope_id: NodeId) {
self.check_and_record(VariantStruct,
span,
sub_span,
svec!(id, ctor_id, name, val, scope_id));
}
pub fn fn_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
scope_id: NodeId) {
self.check_and_record(Function,
span,
sub_span,
svec!(id, name, "", "", scope_id));
}
pub fn method_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
decl_id: Option<DefId>,
scope_id: NodeId) {
let values = match decl_id {
Some(decl_id) => svec!(id, name, decl_id.node, decl_id.krate, scope_id),
None => svec!(id, name, "", "", scope_id)
};
self.check_and_record(Function,
span,
sub_span,
values);
}
pub fn method_decl_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
scope_id: NodeId) {
self.check_and_record(MethodDecl,
span,
sub_span,
svec!(id, name, scope_id));
}
pub fn struct_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
ctor_id: NodeId,
name: &str,
scope_id: NodeId) {
self.check_and_record(Struct,
span,
sub_span,
svec!(id, ctor_id, name, scope_id));
}
pub fn trait_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
scope_id: NodeId) {
self.check_and_record(Trait,
span,
sub_span,
svec!(id, name, scope_id));
}
pub fn impl_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
ref_id: DefId,
scope_id: NodeId) {
self.check_and_record(Impl,
span,
sub_span,
svec!(id, ref_id.node, ref_id.krate, scope_id));
}
pub fn mod_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
name: &str,
parent: NodeId,
filename: &str) {
self.check_and_record(Module,
span,
sub_span,
svec!(id, name, parent, filename));
}
pub fn use_alias_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
mod_id: Option<DefId>,
name: &str,
parent: NodeId) {
let (mod_node, mod_crate) = match mod_id {
Some(mod_id) => (mod_id.node, mod_id.krate),
None => (0, 0)
};
self.check_and_record(UseAlias,
span,
sub_span,
svec!(id, mod_node, mod_crate, name, parent));
}
pub fn extern_crate_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
cnum: ast::CrateNum,
name: &str,
loc: &str,
parent: NodeId) {
self.check_and_record(ExternCrate,
span,
sub_span,
svec!(id, name, loc, cnum, parent));
}
pub fn inherit_str(&mut self,
span: Span,
sub_span: Option<Span>,
base_id: DefId,
deriv_id: NodeId) {
self.check_and_record(Inheritance,
span,
sub_span,
svec!(base_id.node, base_id.krate, deriv_id, 0));
}
pub fn fn_call_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: DefId,
scope_id:NodeId) {
self.check_and_record(FnCall,
span,
sub_span,
svec!(id.node, id.krate, "", scope_id));
}
pub fn meth_call_str(&mut self,
span: Span,
sub_span: Option<Span>,
defid: Option<DefId>,
declid: Option<DefId>,
scope_id: NodeId) {
let (dfn, dfk) = match defid {
Some(defid) => (defid.node, defid.krate),
None => (0, 0)
};
let (dcn, dck) = match declid {
Some(declid) => (s!(declid.node), s!(declid.krate)),
None => ("".to_owned(), "".to_owned())
};
self.check_and_record(MethodCall,
span,
sub_span,
svec!(dfn, dfk, dcn, dck, scope_id));
}
pub fn sub_mod_ref_str(&mut self,
span: Span,
sub_span: Span,
qualname: &str,
parent:NodeId) {
self.record_with_span(ModRef,
span,
sub_span,
svec!(0, 0, qualname, parent));
}
pub fn typedef_str(&mut self,
span: Span,
sub_span: Option<Span>,
id: NodeId,
qualname: &str,
value: &str) {
self.check_and_record(Typedef,
span,
sub_span,
svec!(id, qualname, value));
}
pub fn crate_str(&mut self,
span: Span,
name: &str) {
self.record_with_span(Crate,
span,
span,
svec!(name));
}
pub fn external_crate_str(&mut self,
span: Span,
name: &str,
num: ast::CrateNum) {
let lo_loc = self.span.sess.codemap().lookup_char_pos(span.lo);
self.record_without_span(ExternalCrate,
svec!(name, num, lo_loc.file.name),
span);
}
pub fn sub_type_ref_str(&mut self,
span: Span,
sub_span: Span,
qualname: &str) {
self.record_with_span(TypeRef,
span,
sub_span,
svec!(0, 0, qualname, 0));
}
// A slightly generic function for a reference to an item of any kind.
pub fn ref_str(&mut self,
kind: Row,
span: Span,
sub_span: Option<Span>,
id: DefId,
scope_id: NodeId) {
self.check_and_record(kind,
span,
sub_span,
svec!(id.node, id.krate, "", scope_id));
}
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use driver::session::Session;
use middle::save::generated_code;
use std::cell::Cell;
use syntax::ast;
use syntax::codemap::*;
use syntax::parse::lexer;
use syntax::parse::lexer::{Reader,StringReader};
use syntax::parse::token;
use syntax::parse::token::{is_keyword,keywords,is_ident,Token};
pub struct SpanUtils<'a> {
pub sess: &'a Session,
pub err_count: Cell<int>,
}
impl<'a> SpanUtils<'a> {
// Standard string for extents/location.
pub fn extent_str(&self, span: Span) -> String {
let lo_loc = self.sess.codemap().lookup_char_pos(span.lo);
let hi_loc = self.sess.codemap().lookup_char_pos(span.hi);
let lo_pos = self.sess.codemap().lookup_byte_offset(span.lo).pos;
let hi_pos = self.sess.codemap().lookup_byte_offset(span.hi).pos;
format!("file_name,{},file_line,{},file_col,{},extent_start,{},\
file_line_end,{},file_col_end,{},extent_end,{}",
lo_loc.file.name, lo_loc.line, lo_loc.col.to_uint(), lo_pos.to_uint(),
hi_loc.line, hi_loc.col.to_uint(), hi_pos.to_uint())
}
// sub_span starts at span.lo, so we need to adjust the positions etc.
// If sub_span is None, we don't need to adjust.
pub fn make_sub_span(&self, span: Span, sub_span: Option<Span>) -> Option<Span> {
let loc = self.sess.codemap().lookup_char_pos(span.lo);
assert!(!generated_code(span),
"generated code; we should not be processing this `{}` in {}, line {}",
self.snippet(span), loc.file.name, loc.line);
match sub_span {
None => None,
Some(sub) => {
let FileMapAndBytePos {fm, pos} =
self.sess.codemap().lookup_byte_offset(span.lo);
let base = pos + fm.start_pos;
Some(Span {
lo: base + self.sess.codemap().lookup_byte_offset(sub.lo).pos,
hi: base + self.sess.codemap().lookup_byte_offset(sub.hi).pos,
expn_info: None,
})
}
}
}
pub fn snippet(&self, span: Span) -> String {
match self.sess.codemap().span_to_snippet(span) {
Some(s) => s,
None => String::new(),
}
}
pub fn retokenise_span(&self, span: Span) -> StringReader<'a> {
// sadness - we don't have spans for sub-expressions nor access to the tokens
// so in order to get extents for the function name itself (which dxr expects)
// we need to re-tokenise the fn definition
// Note: this is a bit awful - it adds the contents of span to the end of
// the codemap as a new filemap. This is mostly OK, but means we should
// not iterate over the codemap. Also, any spans over the new filemap
// are incompatible with spans over other filemaps.
let filemap = self.sess.codemap().new_filemap(String::from_str("<anon-dxr>"),
self.snippet(span));
let s = self.sess;
lexer::StringReader::new(s.diagnostic(), filemap)
}
// Re-parses a path and returns the span for the last identifier in the path
pub fn span_for_last_ident(&self, span: Span) -> Option<Span> {
let mut result = None;
let mut toks = self.retokenise_span(span);
let mut bracket_count = 0;
loop {
let ts = toks.next_token();
if ts.tok == token::EOF {
return self.make_sub_span(span, result)
}
if bracket_count == 0 &&
(is_ident(&ts.tok) || is_keyword(keywords::Self, &ts.tok)) {
result = Some(ts.sp);
}
bracket_count += match ts.tok {
token::LT => 1,
token::GT => -1,
token::BINOP(token::SHR) => -2,
_ => 0
}
}
}
// Return the span for the first identifier in the path.
pub fn span_for_first_ident(&self, span: Span) -> Option<Span> {
let mut toks = self.retokenise_span(span);
let mut bracket_count = 0;
loop {
let ts = toks.next_token();
if ts.tok == token::EOF {
return None;
}
if bracket_count == 0 &&
(is_ident(&ts.tok) || is_keyword(keywords::Self, &ts.tok)) {
return self.make_sub_span(span, Some(ts.sp));
}
bracket_count += match ts.tok {
token::LT => 1,
token::GT => -1,
token::BINOP(token::SHR) => -2,
_ => 0
}
}
}
// Return the span for the last ident before a `(` or `<` or '::<' and outside any
// any brackets, or the last span.
pub fn sub_span_for_meth_name(&self, span: Span) -> Option<Span> {
let mut toks = self.retokenise_span(span);
let mut prev = toks.next_token();
let mut result = None;
let mut bracket_count = 0;
let mut last_span = None;
while prev.tok != token::EOF {
last_span = None;
let mut next = toks.next_token();
if (next.tok == token::LPAREN ||
next.tok == token::LT) &&
bracket_count == 0 &&
is_ident(&prev.tok) {
result = Some(prev.sp);
}
if bracket_count == 0 &&
next.tok == token::MOD_SEP {
let old = prev;
prev = next;
next = toks.next_token();
if next.tok == token::LT &&
is_ident(&old.tok) {
result = Some(old.sp);
}
}
bracket_count += match prev.tok {
token::LPAREN | token::LT => 1,
token::RPAREN | token::GT => -1,
token::BINOP(token::SHR) => -2,
_ => 0
};
if is_ident(&prev.tok) && bracket_count == 0 {
last_span = Some(prev.sp);
}
prev = next;
}
if result.is_none() && last_span.is_some() {
return self.make_sub_span(span, last_span);
}
return self.make_sub_span(span, result);
}
// Return the span for the last ident before a `<` and outside any
// brackets, or the last span.
pub fn sub_span_for_type_name(&self, span: Span) -> Option<Span> {
let mut toks = self.retokenise_span(span);
let mut prev = toks.next_token();
let mut result = None;
let mut bracket_count = 0;
loop {
let next = toks.next_token();
if (next.tok == token::LT ||
next.tok == token::COLON) &&
bracket_count == 0 &&
is_ident(&prev.tok) {
result = Some(prev.sp);
}
bracket_count += match prev.tok {
token::LT => 1,
token::GT => -1,
token::BINOP(token::SHR) => -2,
_ => 0
};
if next.tok == token::EOF {
break;
}
prev = next;
}
if bracket_count != 0 {
let loc = self.sess.codemap().lookup_char_pos(span.lo);
self.sess.span_bug(span,
format!("Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
self.snippet(span), loc.file.name, loc.line).as_slice());
}
if result.is_none() && is_ident(&prev.tok) && bracket_count == 0 {
return self.make_sub_span(span, Some(prev.sp));
}
self.make_sub_span(span, result)
}
// Reparse span and return an owned vector of sub spans of the first limit
// identifier tokens in the given nesting level.
// example with Foo<Bar<T,V>, Bar<T,V>>
// Nesting = 0: all idents outside of brackets: ~[Foo]
// Nesting = 1: idents within one level of brackets: ~[Bar, Bar]
pub fn spans_with_brackets(&self, span: Span, nesting: int, limit: int) -> Vec<Span> {
let mut result: Vec<Span> = vec!();
let mut toks = self.retokenise_span(span);
// We keep track of how many brackets we're nested in
let mut bracket_count = 0;
loop {
let ts = toks.next_token();
if ts.tok == token::EOF {
if bracket_count != 0 {
let loc = self.sess.codemap().lookup_char_pos(span.lo);
self.sess.span_bug(span, format!(
"Mis-counted brackets when breaking path? Parsing '{}' in {}, line {}",
self.snippet(span), loc.file.name, loc.line).as_slice());
}
return result
}
if (result.len() as int) == limit {
return result;
}
bracket_count += match ts.tok {
token::LT => 1,
token::GT => -1,
token::BINOP(token::SHL) => 2,
token::BINOP(token::SHR) => -2,
_ => 0
};
if is_ident(&ts.tok) &&
bracket_count == nesting {
result.push(self.make_sub_span(span, Some(ts.sp)).unwrap());
}
}
}
pub fn sub_span_before_token(&self, span: Span, tok: Token) -> Option<Span> {
let mut toks = self.retokenise_span(span);
let mut prev = toks.next_token();
loop {
if prev.tok == token::EOF {
return None;
}
let next = toks.next_token();
if next.tok == tok {
return self.make_sub_span(span, Some(prev.sp));
}
prev = next;
}
}
// Return an owned vector of the subspans of the tokens that come before tok2
// which is before tok1. If there is no instance of tok2 before tok1, then that
// place in the result is None.
// Everything returned must be inside a set of (non-angle) brackets, but no
// more deeply nested than that.
pub fn sub_spans_before_tokens(&self,
span: Span,
tok1: Token,
tok2: Token) -> Vec<Option<Span>> {
let mut sub_spans : Vec<Option<Span>> = vec!();
let mut toks = self.retokenise_span(span);
let mut prev = toks.next_token();
let mut next = toks.next_token();
let mut stored_val = false;
let mut found_val = false;
let mut bracket_count = 0;
while next.tok != token::EOF {
if bracket_count == 1 {
if next.tok == tok2 {
sub_spans.push(self.make_sub_span(span, Some(prev.sp)));
stored_val = true;
found_val = false;
}
if next.tok == tok1 {
if !stored_val {
sub_spans.push(None);
} else {
stored_val = false;
}
found_val = false;
}
if !stored_val &&
is_ident(&next.tok) {
found_val = true;
}
}
bracket_count += match next.tok {
token::LPAREN | token::LBRACE => 1,
token::RPAREN | token::RBRACE => -1,
_ => 0
};
prev = next;
next = toks.next_token();
}
if found_val {
sub_spans.push(None);
}
return sub_spans;
}
pub fn sub_span_after_keyword(&self,
span: Span,
keyword: keywords::Keyword) -> Option<Span> {
let mut toks = self.retokenise_span(span);
loop {
let ts = toks.next_token();
if ts.tok == token::EOF {
return None;
}
if is_keyword(keyword, &ts.tok) {
let ts = toks.next_token();
if ts.tok == token::EOF {
return None
} else {
return self.make_sub_span(span, Some(ts.sp));
}
}
}
}
// Returns a list of the spans of idents in a patch.
// E.g., For foo::bar<x,t>::baz, we return [foo, bar, baz] (well, their spans)
pub fn spans_for_path_segments(&self, path: &ast::Path) -> Vec<Span> {
if generated_code(path.span) {
return vec!();
}
self.spans_with_brackets(path.span, 0, -1)
}
// Return an owned vector of the subspans of the param identifier
// tokens found in span.
pub fn spans_for_ty_params(&self, span: Span, number: int) -> Vec<Span> {
if generated_code(span) {
return vec!();
}
// Type params are nested within one level of brackets:
// i.e. we want ~[A, B] from Foo<A, B<T,U>>
self.spans_with_brackets(span, 1, number)
}
pub fn report_span_err(&self, kind: &str, span: Span) {
let loc = self.sess.codemap().lookup_char_pos(span.lo);
info!("({}) Could not find sub_span in `{}` in {}, line {}",
kind, self.snippet(span), loc.file.name, loc.line);
self.err_count.set(self.err_count.get()+1);
if self.err_count.get() > 1000 {
self.sess.bug("span errors reached 1000, giving up");
}
}
}

View file

@ -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<ast::DefId> {
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`.

View file

@ -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)]

View file

@ -0,0 +1,3 @@
-include ../tools.mk
all:
$(RUSTC) foo.rs -Zsave-analysis

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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>) -> Foo;
}
impl Tr for Foo {
fn tar(&self, x: Box<Foo>) -> Foo {
Foo{ f: self.f + x.f }
}
}
trait Tr2<X, Y: Tr> {
fn squid(&self, y: &Y, z: Self) -> Box<X>;
}
impl Tr2<Foo, Foo> for Foo {
fn squid(&self, y: &Foo, z: Foo) -> Box<Foo> {
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()
};
}