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:
commit
c119903f62
11 changed files with 2531 additions and 37 deletions
|
@ -172,7 +172,8 @@ debugging_opts!(
|
||||||
LTO,
|
LTO,
|
||||||
AST_JSON,
|
AST_JSON,
|
||||||
AST_JSON_NOEXPAND,
|
AST_JSON_NOEXPAND,
|
||||||
LS
|
LS,
|
||||||
|
SAVE_ANALYSIS
|
||||||
]
|
]
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
@ -206,7 +207,9 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
|
||||||
("lto", "Perform LLVM link-time optimizations", LTO),
|
("lto", "Perform LLVM link-time optimizations", LTO),
|
||||||
("ast-json", "Print the AST as JSON and halt", AST_JSON),
|
("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),
|
("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
|
/// Declare a macro that will define all CodegenOptions fields and parsers all
|
||||||
|
|
|
@ -87,6 +87,7 @@ pub fn compile_input(sess: Session,
|
||||||
if stop_after_phase_2(&sess) { return; }
|
if stop_after_phase_2(&sess) { return; }
|
||||||
|
|
||||||
let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
|
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; }
|
if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
|
||||||
let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
|
let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
|
||||||
analysis, &outputs);
|
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 struct CrateTranslation {
|
||||||
pub context: ContextRef,
|
pub context: ContextRef,
|
||||||
pub module: ModuleRef,
|
pub module: ModuleRef,
|
||||||
|
|
|
@ -28,6 +28,7 @@ use syntax::{ast, codemap};
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
|
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub targ_cfg: config::Config,
|
pub targ_cfg: config::Config,
|
||||||
pub opts: config::Options,
|
pub opts: config::Options,
|
||||||
|
|
|
@ -84,6 +84,7 @@ pub mod middle {
|
||||||
pub mod expr_use_visitor;
|
pub mod expr_use_visitor;
|
||||||
pub mod dependency_format;
|
pub mod dependency_format;
|
||||||
pub mod weak_lang_items;
|
pub mod weak_lang_items;
|
||||||
|
pub mod save;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod front {
|
pub mod front {
|
||||||
|
|
1439
src/librustc/middle/save/mod.rs
Normal file
1439
src/librustc/middle/save/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
575
src/librustc/middle/save/recorder.rs
Normal file
575
src/librustc/middle/save/recorder.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
381
src/librustc/middle/save/span_utils.rs
Normal file
381
src/librustc/middle/save/span_utils.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
/// 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
|
/// default method or an implementation of a trait method), return the ID of
|
||||||
/// the trait that the method belongs to. Otherwise, return `None`.
|
/// the trait that the method belongs to. Otherwise, return `None`.
|
||||||
|
|
|
@ -421,6 +421,41 @@ impl CodeMap {
|
||||||
fail!("asking for {} which we don't know about", filename);
|
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 {
|
fn lookup_filemap_idx(&self, pos: BytePos) -> uint {
|
||||||
let files = self.files.borrow();
|
let files = self.files.borrow();
|
||||||
let files = files;
|
let files = files;
|
||||||
|
@ -491,41 +526,6 @@ impl CodeMap {
|
||||||
col: chpos - linechpos
|
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)]
|
#[cfg(test)]
|
||||||
|
|
3
src/test/run-make/save-analysis/Makefile
Normal file
3
src/test/run-make/save-analysis/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
-include ../tools.mk
|
||||||
|
all:
|
||||||
|
$(RUSTC) foo.rs -Zsave-analysis
|
58
src/test/run-make/save-analysis/foo.rs
Normal file
58
src/test/run-make/save-analysis/foo.rs
Normal 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()
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue