2015-05-04 10:33:07 +00:00
|
|
|
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
2014-02-05 04:31:33 +00:00
|
|
|
// 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.
|
|
|
|
|
2015-05-04 10:33:07 +00:00
|
|
|
use middle::ty;
|
2015-06-05 05:50:04 +00:00
|
|
|
use middle::def;
|
2014-02-05 04:31:33 +00:00
|
|
|
|
2015-01-27 20:20:58 +00:00
|
|
|
use std::env;
|
2015-02-27 05:00:43 +00:00
|
|
|
use std::fs::{self, File};
|
|
|
|
use std::path::{Path, PathBuf};
|
2014-02-05 04:31:33 +00:00
|
|
|
|
2015-07-09 00:23:29 +00:00
|
|
|
use rustc::ast_map::NodeItem;
|
|
|
|
|
2015-05-05 10:03:20 +00:00
|
|
|
use syntax::{attr};
|
2015-05-05 07:27:44 +00:00
|
|
|
use syntax::ast::{self, NodeId, DefId};
|
2015-05-05 10:03:20 +00:00
|
|
|
use syntax::ast_util;
|
2014-02-05 04:31:33 +00:00
|
|
|
use syntax::codemap::*;
|
2015-05-10 20:35:08 +00:00
|
|
|
use syntax::parse::token::{self, get_ident, keywords};
|
2015-05-05 10:03:20 +00:00
|
|
|
use syntax::visit::{self, Visitor};
|
2015-05-10 20:35:08 +00:00
|
|
|
use syntax::print::pprust::ty_to_string;
|
|
|
|
|
2015-05-05 07:27:44 +00:00
|
|
|
use self::span_utils::SpanUtils;
|
|
|
|
|
2015-06-05 04:03:48 +00:00
|
|
|
|
2014-02-05 04:31:33 +00:00
|
|
|
mod span_utils;
|
|
|
|
mod recorder;
|
|
|
|
|
2015-05-04 10:33:07 +00:00
|
|
|
mod dump_csv;
|
2014-02-05 04:31:33 +00:00
|
|
|
|
2015-05-05 07:27:44 +00:00
|
|
|
pub struct SaveContext<'l, 'tcx: 'l> {
|
2015-06-13 22:49:28 +00:00
|
|
|
tcx: &'l ty::ctxt<'tcx>,
|
2015-05-05 07:27:44 +00:00
|
|
|
span_utils: SpanUtils<'l>,
|
2015-05-05 06:46:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CrateData {
|
|
|
|
pub name: String,
|
|
|
|
pub number: u32,
|
|
|
|
}
|
|
|
|
|
2015-05-15 07:06:56 +00:00
|
|
|
/// Data for any entity in the Rust language. The actual data contained varied
|
|
|
|
/// with the kind of entity being queried. See the nested structs for details.
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-05-05 07:27:44 +00:00
|
|
|
pub enum Data {
|
2015-05-15 07:06:56 +00:00
|
|
|
/// Data for all kinds of functions and methods.
|
2015-05-05 07:27:44 +00:00
|
|
|
FunctionData(FunctionData),
|
2015-06-05 04:03:48 +00:00
|
|
|
/// Data for local and global variables (consts and statics), and fields.
|
2015-05-10 20:35:08 +00:00
|
|
|
VariableData(VariableData),
|
2015-05-25 22:35:53 +00:00
|
|
|
/// Data for modules.
|
|
|
|
ModData(ModData),
|
2015-06-02 19:21:20 +00:00
|
|
|
/// Data for Enums.
|
|
|
|
EnumData(EnumData),
|
2015-06-05 05:50:04 +00:00
|
|
|
/// Data for impls.
|
|
|
|
ImplData(ImplData),
|
2015-05-25 22:35:53 +00:00
|
|
|
|
|
|
|
/// Data for the use of some variable (e.g., the use of a local variable, which
|
|
|
|
/// will refere to that variables declaration).
|
|
|
|
VariableRefData(VariableRefData),
|
2015-06-05 05:50:04 +00:00
|
|
|
/// Data for a reference to a type or trait.
|
|
|
|
TypeRefData(TypeRefData),
|
2015-07-08 02:30:18 +00:00
|
|
|
/// Data about a function call.
|
|
|
|
FunctionCallData(FunctionCallData),
|
2015-07-06 23:42:43 +00:00
|
|
|
/// Data about a method call.
|
|
|
|
MethodCallData(MethodCallData),
|
2015-05-05 07:27:44 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 07:06:56 +00:00
|
|
|
/// Data for all kinds of functions and methods.
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-05-05 07:27:44 +00:00
|
|
|
pub struct FunctionData {
|
|
|
|
pub id: NodeId,
|
2015-05-10 20:35:08 +00:00
|
|
|
pub name: String,
|
2015-05-05 07:27:44 +00:00
|
|
|
pub qualname: String,
|
|
|
|
pub declaration: Option<DefId>,
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
}
|
|
|
|
|
2015-05-15 07:06:56 +00:00
|
|
|
/// Data for local and global variables (consts and statics).
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-05-10 20:35:08 +00:00
|
|
|
pub struct VariableData {
|
|
|
|
pub id: NodeId,
|
|
|
|
pub name: String,
|
|
|
|
pub qualname: String,
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub value: String,
|
|
|
|
pub type_value: String,
|
|
|
|
}
|
|
|
|
|
2015-05-25 22:35:53 +00:00
|
|
|
/// Data for modules.
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-05-25 22:35:53 +00:00
|
|
|
pub struct ModData {
|
|
|
|
pub id: NodeId,
|
|
|
|
pub name: String,
|
|
|
|
pub qualname: String,
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub filename: String,
|
|
|
|
}
|
|
|
|
|
2015-06-02 19:21:20 +00:00
|
|
|
/// Data for enum declarations.
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-06-02 19:21:20 +00:00
|
|
|
pub struct EnumData {
|
|
|
|
pub id: NodeId,
|
|
|
|
pub value: String,
|
|
|
|
pub qualname: String,
|
|
|
|
pub span: Span,
|
2015-06-05 05:50:04 +00:00
|
|
|
pub scope: NodeId,
|
|
|
|
}
|
|
|
|
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-06-05 05:50:04 +00:00
|
|
|
pub struct ImplData {
|
|
|
|
pub id: NodeId,
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
// FIXME: I'm not really sure inline data is the best way to do this. Seems
|
|
|
|
// OK in this case, but generalising leads to returning chunks of AST, which
|
|
|
|
// feels wrong.
|
|
|
|
pub trait_ref: Option<TypeRefData>,
|
|
|
|
pub self_ref: Option<TypeRefData>,
|
2015-06-02 19:21:20 +00:00
|
|
|
}
|
|
|
|
|
2015-05-25 22:35:53 +00:00
|
|
|
/// Data for the use of some item (e.g., the use of a local variable, which
|
2015-07-08 02:30:18 +00:00
|
|
|
/// will refer to that variables declaration (by ref_id)).
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-05-25 22:35:53 +00:00
|
|
|
pub struct VariableRefData {
|
|
|
|
pub name: String,
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub ref_id: DefId,
|
|
|
|
}
|
|
|
|
|
2015-06-05 05:50:04 +00:00
|
|
|
/// Data for a reference to a type or trait.
|
2015-06-08 22:36:30 +00:00
|
|
|
#[derive(Debug)]
|
2015-06-05 05:50:04 +00:00
|
|
|
pub struct TypeRefData {
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub ref_id: DefId,
|
|
|
|
}
|
|
|
|
|
2015-07-08 02:30:18 +00:00
|
|
|
/// Data about a function call.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct FunctionCallData {
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub ref_id: DefId,
|
|
|
|
}
|
|
|
|
|
2015-07-06 23:42:43 +00:00
|
|
|
/// Data about a method call.
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct MethodCallData {
|
|
|
|
pub span: Span,
|
|
|
|
pub scope: NodeId,
|
|
|
|
pub ref_id: Option<DefId>,
|
|
|
|
pub decl_id: Option<DefId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-25 22:35:53 +00:00
|
|
|
|
2015-05-05 07:27:44 +00:00
|
|
|
impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
|
2015-06-13 22:49:28 +00:00
|
|
|
pub fn new(tcx: &'l ty::ctxt<'tcx>,
|
2015-05-05 07:27:44 +00:00
|
|
|
span_utils: SpanUtils<'l>)
|
|
|
|
-> SaveContext<'l, 'tcx> {
|
2015-05-05 06:46:09 +00:00
|
|
|
SaveContext {
|
2015-06-13 22:49:28 +00:00
|
|
|
tcx: tcx,
|
2015-05-05 07:27:44 +00:00
|
|
|
span_utils: span_utils,
|
2015-05-05 06:46:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// List external crates used by the current crate.
|
|
|
|
pub fn get_external_crates(&self) -> Vec<CrateData> {
|
|
|
|
let mut result = Vec::new();
|
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
self.tcx.sess.cstore.iter_crate_data(|n, cmd| {
|
2015-05-05 06:46:09 +00:00
|
|
|
result.push(CrateData { name: cmd.name.clone(), number: n });
|
|
|
|
});
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
2015-05-05 07:27:44 +00:00
|
|
|
|
|
|
|
pub fn get_item_data(&self, item: &ast::Item) -> Data {
|
|
|
|
match item.node {
|
2015-05-25 22:35:53 +00:00
|
|
|
ast::ItemFn(..) => {
|
2015-06-13 22:49:28 +00:00
|
|
|
let name = self.tcx.map.path_to_string(item.id);
|
2015-05-10 20:35:08 +00:00
|
|
|
let qualname = format!("::{}", name);
|
2015-05-05 07:27:44 +00:00
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Fn);
|
|
|
|
|
|
|
|
Data::FunctionData(FunctionData {
|
|
|
|
id: item.id,
|
2015-05-10 20:35:08 +00:00
|
|
|
name: name,
|
2015-05-05 07:27:44 +00:00
|
|
|
qualname: qualname,
|
|
|
|
declaration: None,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(item.id),
|
2015-05-05 07:27:44 +00:00
|
|
|
})
|
|
|
|
}
|
2015-05-10 20:35:08 +00:00
|
|
|
ast::ItemStatic(ref typ, mt, ref expr) => {
|
2015-06-13 22:49:28 +00:00
|
|
|
let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
|
2015-05-10 20:35:08 +00:00
|
|
|
|
|
|
|
// If the variable is immutable, save the initialising expression.
|
2015-05-14 05:44:08 +00:00
|
|
|
let (value, keyword) = match mt {
|
2015-06-08 14:55:35 +00:00
|
|
|
ast::MutMutable => (String::from("<mutable>"), keywords::Mut),
|
2015-05-14 05:44:08 +00:00
|
|
|
ast::MutImmutable => (self.span_utils.snippet(expr.span), keywords::Static),
|
2015-05-10 20:35:08 +00:00
|
|
|
};
|
|
|
|
|
2015-05-14 05:44:08 +00:00
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(item.span, keyword);
|
2015-05-10 20:35:08 +00:00
|
|
|
|
|
|
|
Data::VariableData(VariableData {
|
|
|
|
id: item.id,
|
|
|
|
name: get_ident(item.ident).to_string(),
|
|
|
|
qualname: qualname,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(item.id),
|
2015-05-10 20:35:08 +00:00
|
|
|
value: value,
|
|
|
|
type_value: ty_to_string(&typ),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
ast::ItemConst(ref typ, ref expr) => {
|
2015-06-13 22:49:28 +00:00
|
|
|
let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
|
2015-05-10 20:35:08 +00:00
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Const);
|
|
|
|
|
|
|
|
Data::VariableData(VariableData {
|
|
|
|
id: item.id,
|
|
|
|
name: get_ident(item.ident).to_string(),
|
|
|
|
qualname: qualname,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-09 00:23:29 +00:00
|
|
|
scope: self.enclosing_scope(item.id),
|
2015-05-10 20:35:08 +00:00
|
|
|
value: self.span_utils.snippet(expr.span),
|
|
|
|
type_value: ty_to_string(&typ),
|
|
|
|
})
|
|
|
|
}
|
2015-05-25 22:35:53 +00:00
|
|
|
ast::ItemMod(ref m) => {
|
2015-06-13 22:49:28 +00:00
|
|
|
let qualname = format!("::{}", self.tcx.map.path_to_string(item.id));
|
2015-05-25 22:35:53 +00:00
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
let cm = self.tcx.sess.codemap();
|
2015-05-25 22:35:53 +00:00
|
|
|
let filename = cm.span_to_filename(m.inner);
|
|
|
|
|
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Mod);
|
|
|
|
|
|
|
|
Data::ModData(ModData {
|
|
|
|
id: item.id,
|
|
|
|
name: get_ident(item.ident).to_string(),
|
|
|
|
qualname: qualname,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(item.id),
|
2015-05-25 22:35:53 +00:00
|
|
|
filename: filename,
|
|
|
|
})
|
2015-06-02 19:21:20 +00:00
|
|
|
},
|
|
|
|
ast::ItemEnum(..) => {
|
2015-06-13 22:49:28 +00:00
|
|
|
let enum_name = format!("::{}", self.tcx.map.path_to_string(item.id));
|
2015-06-02 19:21:20 +00:00
|
|
|
let val = self.span_utils.snippet(item.span);
|
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(item.span, keywords::Enum);
|
|
|
|
|
|
|
|
Data::EnumData(EnumData {
|
|
|
|
id: item.id,
|
|
|
|
value: val,
|
|
|
|
span: sub_span.unwrap(),
|
|
|
|
qualname: enum_name,
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(item.id),
|
2015-06-02 19:21:20 +00:00
|
|
|
})
|
|
|
|
},
|
2015-06-05 05:50:04 +00:00
|
|
|
ast::ItemImpl(_, _, _, ref trait_ref, ref typ, _) => {
|
|
|
|
let mut type_data = None;
|
|
|
|
let sub_span;
|
|
|
|
|
2015-07-08 08:54:45 +00:00
|
|
|
let parent = self.enclosing_scope(item.id);
|
2015-06-05 05:50:04 +00:00
|
|
|
|
|
|
|
match typ.node {
|
|
|
|
// Common case impl for a struct or something basic.
|
|
|
|
ast::TyPath(None, ref path) => {
|
2015-06-15 05:58:10 +00:00
|
|
|
sub_span = self.span_utils.sub_span_for_type_name(path.span).unwrap();
|
2015-06-05 05:50:04 +00:00
|
|
|
type_data = self.lookup_ref_id(typ.id).map(|id| TypeRefData {
|
2015-06-15 05:58:10 +00:00
|
|
|
span: sub_span,
|
2015-06-05 05:50:04 +00:00
|
|
|
scope: parent,
|
|
|
|
ref_id: id,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
// Less useful case, impl for a compound type.
|
2015-06-15 05:58:10 +00:00
|
|
|
let span = typ.span;
|
|
|
|
sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span);
|
2015-06-05 05:50:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let trait_data =
|
|
|
|
trait_ref.as_ref().and_then(|tr| self.get_trait_ref_data(tr, parent));
|
|
|
|
|
|
|
|
Data::ImplData(ImplData {
|
|
|
|
id: item.id,
|
2015-06-15 05:58:10 +00:00
|
|
|
span: sub_span,
|
2015-06-05 05:50:04 +00:00
|
|
|
scope: parent,
|
|
|
|
trait_ref: trait_data,
|
|
|
|
self_ref: type_data,
|
|
|
|
})
|
|
|
|
}
|
2015-05-25 22:35:53 +00:00
|
|
|
_ => {
|
|
|
|
// FIXME
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-09 00:23:29 +00:00
|
|
|
pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<VariableData> {
|
2015-06-05 04:03:48 +00:00
|
|
|
match field.node.kind {
|
|
|
|
ast::NamedField(ident, _) => {
|
|
|
|
let name = get_ident(ident);
|
|
|
|
let qualname = format!("::{}::{}",
|
2015-07-08 08:54:45 +00:00
|
|
|
self.tcx.map.path_to_string(scope),
|
2015-06-05 04:03:48 +00:00
|
|
|
name);
|
2015-06-15 02:31:01 +00:00
|
|
|
let typ = self.tcx.node_types().get(&field.node.id).unwrap()
|
2015-06-18 17:25:05 +00:00
|
|
|
.to_string();
|
2015-06-05 04:03:48 +00:00
|
|
|
let sub_span = self.span_utils.sub_span_before_token(field.span, token::Colon);
|
2015-07-09 00:23:29 +00:00
|
|
|
Some(VariableData {
|
2015-06-05 04:03:48 +00:00
|
|
|
id: field.node.id,
|
|
|
|
name: get_ident(ident).to_string(),
|
|
|
|
qualname: qualname,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: scope,
|
2015-06-05 04:03:48 +00:00
|
|
|
value: "".to_owned(),
|
|
|
|
type_value: typ,
|
2015-07-09 00:23:29 +00:00
|
|
|
})
|
2015-06-05 04:03:48 +00:00
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-09 00:23:29 +00:00
|
|
|
// FIXME would be nice to take a MethodItem here, but the ast provides both
|
|
|
|
// trait and impl flavours, so the caller must do the disassembly.
|
|
|
|
pub fn get_method_data(&self,
|
|
|
|
id: ast::NodeId,
|
|
|
|
name: ast::Name,
|
|
|
|
span: Span) -> FunctionData {
|
|
|
|
// The qualname for a method is the trait name or name of the struct in an impl in
|
|
|
|
// which the method is declared in, followed by the method's name.
|
|
|
|
let qualname = match self.tcx.impl_of_method(ast_util::local_def(id)) {
|
|
|
|
Some(impl_id) => match self.tcx.map.get(impl_id.node) {
|
|
|
|
NodeItem(item) => {
|
|
|
|
match item.node {
|
|
|
|
ast::ItemImpl(_, _, _, _, ref ty, _) => {
|
|
|
|
let mut result = String::from("<");
|
|
|
|
result.push_str(&ty_to_string(&**ty));
|
|
|
|
|
|
|
|
match self.tcx.trait_of_item(ast_util::local_def(id)) {
|
|
|
|
Some(def_id) => {
|
|
|
|
result.push_str(" as ");
|
|
|
|
result.push_str(
|
|
|
|
&self.tcx.item_path_str(def_id));
|
|
|
|
},
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
result.push_str(">");
|
|
|
|
result
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.tcx.sess.span_bug(span,
|
|
|
|
&format!("Container {} for method {} not an impl?",
|
|
|
|
impl_id.node, id));
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
self.tcx.sess.span_bug(span,
|
|
|
|
&format!("Container {} for method {} is not a node item {:?}",
|
|
|
|
impl_id.node, id, self.tcx.map.get(impl_id.node)));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
None => match self.tcx.trait_of_item(ast_util::local_def(id)) {
|
|
|
|
Some(def_id) => {
|
|
|
|
match self.tcx.map.get(def_id.node) {
|
|
|
|
NodeItem(_) => {
|
|
|
|
format!("::{}", self.tcx.item_path_str(def_id))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.tcx.sess.span_bug(span,
|
|
|
|
&format!("Could not find container {} for method {}",
|
|
|
|
def_id.node, id));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
self.tcx.sess.span_bug(span,
|
|
|
|
&format!("Could not find container for method {}", id));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let qualname = format!("{}::{}", qualname, &token::get_name(name));
|
|
|
|
|
|
|
|
let decl_id = self.tcx.trait_item_of_item(ast_util::local_def(id))
|
|
|
|
.and_then(|new_id| {
|
|
|
|
let def_id = new_id.def_id();
|
|
|
|
if def_id.node != 0 && def_id != ast_util::local_def(id) {
|
|
|
|
Some(def_id)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
|
|
|
|
|
|
|
|
FunctionData {
|
|
|
|
id: id,
|
|
|
|
name: token::get_name(name).to_string(),
|
|
|
|
qualname: qualname,
|
|
|
|
declaration: decl_id,
|
|
|
|
span: sub_span.unwrap(),
|
|
|
|
scope: self.enclosing_scope(id),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-05 05:50:04 +00:00
|
|
|
pub fn get_trait_ref_data(&self,
|
|
|
|
trait_ref: &ast::TraitRef,
|
|
|
|
parent: NodeId)
|
|
|
|
-> Option<TypeRefData> {
|
|
|
|
self.lookup_ref_id(trait_ref.ref_id).map(|def_id| {
|
2015-06-15 05:58:10 +00:00
|
|
|
let span = trait_ref.path.span;
|
|
|
|
let sub_span = self.span_utils.sub_span_for_type_name(span).unwrap_or(span);
|
2015-06-05 05:50:04 +00:00
|
|
|
TypeRefData {
|
2015-06-15 05:58:10 +00:00
|
|
|
span: sub_span,
|
2015-06-05 05:50:04 +00:00
|
|
|
scope: parent,
|
|
|
|
ref_id: def_id,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-06-14 22:06:01 +00:00
|
|
|
pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
|
2015-05-25 22:35:53 +00:00
|
|
|
match expr.node {
|
|
|
|
ast::ExprField(ref sub_ex, ident) => {
|
2015-06-25 20:42:17 +00:00
|
|
|
let ty = &self.tcx.expr_ty_adjusted(&sub_ex).sty;
|
2015-05-25 22:35:53 +00:00
|
|
|
match *ty {
|
2015-06-11 23:21:46 +00:00
|
|
|
ty::TyStruct(def_id, _) => {
|
2015-06-25 20:42:17 +00:00
|
|
|
let fields = self.tcx.lookup_struct_fields(def_id);
|
2015-05-25 22:35:53 +00:00
|
|
|
for f in &fields {
|
|
|
|
if f.name == ident.node.name {
|
|
|
|
let sub_span = self.span_utils.span_for_last_ident(expr.span);
|
2015-06-14 22:06:01 +00:00
|
|
|
return Some(Data::VariableRefData(VariableRefData {
|
2015-05-25 22:35:53 +00:00
|
|
|
name: get_ident(ident.node).to_string(),
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(expr.id),
|
2015-05-25 22:35:53 +00:00
|
|
|
ref_id: f.id,
|
2015-06-14 22:06:01 +00:00
|
|
|
}));
|
2015-05-25 22:35:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
self.tcx.sess.span_bug(expr.span,
|
|
|
|
&format!("Couldn't find field {} on {:?}",
|
|
|
|
&get_ident(ident.node), ty))
|
2015-05-25 22:35:53 +00:00
|
|
|
}
|
2015-06-14 22:06:01 +00:00
|
|
|
_ => {
|
|
|
|
debug!("Expected struct type, found {:?}", ty);
|
|
|
|
None
|
|
|
|
}
|
2015-05-25 22:35:53 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-08 21:51:54 +00:00
|
|
|
ast::ExprStruct(ref path, _, _) => {
|
2015-06-25 20:42:17 +00:00
|
|
|
let ty = &self.tcx.expr_ty_adjusted(expr).sty;
|
2015-06-08 21:51:54 +00:00
|
|
|
match *ty {
|
2015-06-14 22:06:01 +00:00
|
|
|
ty::TyStruct(def_id, _) => {
|
2015-06-08 21:51:54 +00:00
|
|
|
let sub_span = self.span_utils.span_for_last_ident(path.span);
|
2015-06-14 22:06:01 +00:00
|
|
|
Some(Data::TypeRefData(TypeRefData {
|
2015-06-08 21:51:54 +00:00
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(expr.id),
|
2015-06-08 21:51:54 +00:00
|
|
|
ref_id: def_id,
|
2015-06-14 22:06:01 +00:00
|
|
|
}))
|
2015-06-08 21:51:54 +00:00
|
|
|
}
|
|
|
|
_ => {
|
2015-06-14 22:06:01 +00:00
|
|
|
// FIXME ty could legitimately be a TyEnum, but then we will fail
|
|
|
|
// later if we try to look up the fields.
|
|
|
|
debug!("expected TyStruct, found {:?}", ty);
|
|
|
|
None
|
2015-06-08 21:51:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-06 23:42:43 +00:00
|
|
|
ast::ExprMethodCall(..) => {
|
|
|
|
let method_call = ty::MethodCall::expr(expr.id);
|
|
|
|
let method_id = self.tcx.tables.borrow().method_map[&method_call].def_id;
|
|
|
|
let (def_id, decl_id) = match self.tcx.impl_or_trait_item(method_id).container() {
|
|
|
|
ty::ImplContainer(_) => (Some(method_id), None),
|
|
|
|
ty::TraitContainer(_) => (None, Some(method_id))
|
|
|
|
};
|
|
|
|
let sub_span = self.span_utils.sub_span_for_meth_name(expr.span);
|
2015-07-08 08:54:45 +00:00
|
|
|
let parent = self.enclosing_scope(expr.id);
|
2015-07-06 23:42:43 +00:00
|
|
|
Some(Data::MethodCallData(MethodCallData {
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 02:30:18 +00:00
|
|
|
scope: parent,
|
2015-07-06 23:42:43 +00:00
|
|
|
ref_id: def_id,
|
2015-07-08 02:30:18 +00:00
|
|
|
decl_id: decl_id,
|
2015-07-06 23:42:43 +00:00
|
|
|
}))
|
|
|
|
}
|
2015-07-08 02:30:18 +00:00
|
|
|
ast::ExprPath(_, ref path) => {
|
|
|
|
Some(self.get_path_data(expr.id, path))
|
|
|
|
}
|
2015-05-05 07:27:44 +00:00
|
|
|
_ => {
|
2015-05-10 20:35:08 +00:00
|
|
|
// FIXME
|
2015-05-05 07:27:44 +00:00
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-08 02:30:18 +00:00
|
|
|
pub fn get_path_data(&self,
|
|
|
|
id: NodeId,
|
|
|
|
path: &ast::Path)
|
|
|
|
-> Data {
|
|
|
|
let def_map = self.tcx.def_map.borrow();
|
|
|
|
if !def_map.contains_key(&id) {
|
|
|
|
self.tcx.sess.span_bug(path.span,
|
|
|
|
&format!("def_map has no key for {} in visit_expr", id));
|
|
|
|
}
|
|
|
|
let def = def_map.get(&id).unwrap().full_def();
|
|
|
|
let sub_span = self.span_utils.span_for_last_ident(path.span);
|
|
|
|
match def {
|
|
|
|
def::DefUpvar(..) |
|
|
|
|
def::DefLocal(..) |
|
|
|
|
def::DefStatic(..) |
|
|
|
|
def::DefConst(..) |
|
|
|
|
def::DefAssociatedConst(..) |
|
|
|
|
def::DefVariant(..) => {
|
|
|
|
Data::VariableRefData(VariableRefData {
|
|
|
|
name: self.span_utils.snippet(sub_span.unwrap()),
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(id),
|
2015-07-08 02:30:18 +00:00
|
|
|
ref_id: def.def_id(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
def::DefStruct(def_id) | def::DefTy(def_id, _) => {
|
|
|
|
Data::TypeRefData(TypeRefData {
|
|
|
|
span: sub_span.unwrap(),
|
|
|
|
ref_id: def_id,
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(id),
|
2015-07-08 02:30:18 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
def::DefMethod(decl_id, provenence) => {
|
|
|
|
let sub_span = self.span_utils.sub_span_for_meth_name(path.span);
|
|
|
|
let def_id = if decl_id.krate == ast::LOCAL_CRATE {
|
|
|
|
let ti = self.tcx.impl_or_trait_item(decl_id);
|
|
|
|
match provenence {
|
|
|
|
def::FromTrait(def_id) => {
|
|
|
|
Some(self.tcx.trait_items(def_id)
|
|
|
|
.iter()
|
|
|
|
.find(|mr| {
|
|
|
|
mr.name() == ti.name()
|
|
|
|
})
|
|
|
|
.unwrap()
|
|
|
|
.def_id())
|
|
|
|
}
|
|
|
|
def::FromImpl(def_id) => {
|
|
|
|
let impl_items = self.tcx.impl_items.borrow();
|
|
|
|
Some(impl_items.get(&def_id)
|
|
|
|
.unwrap()
|
|
|
|
.iter()
|
|
|
|
.find(|mr| {
|
|
|
|
self.tcx.impl_or_trait_item(mr.def_id()).name()
|
|
|
|
== ti.name()
|
|
|
|
})
|
|
|
|
.unwrap()
|
|
|
|
.def_id())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Data::MethodCallData(MethodCallData {
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(id),
|
2015-07-08 02:30:18 +00:00
|
|
|
ref_id: def_id,
|
|
|
|
decl_id: Some(decl_id),
|
|
|
|
})
|
|
|
|
},
|
|
|
|
def::DefFn(def_id, _) => {
|
|
|
|
Data::FunctionCallData(FunctionCallData {
|
|
|
|
ref_id: def_id,
|
|
|
|
span: sub_span.unwrap(),
|
2015-07-08 08:54:45 +00:00
|
|
|
scope: self.enclosing_scope(id),
|
2015-07-08 02:30:18 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => self.tcx.sess.span_bug(path.span,
|
|
|
|
&format!("Unexpected def kind while looking \
|
|
|
|
up path in `{}`: `{:?}`",
|
|
|
|
self.span_utils.snippet(path.span),
|
|
|
|
def)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-08 21:51:54 +00:00
|
|
|
pub fn get_field_ref_data(&self,
|
|
|
|
field_ref: &ast::Field,
|
|
|
|
struct_id: DefId,
|
|
|
|
parent: NodeId)
|
|
|
|
-> VariableRefData {
|
2015-06-25 20:42:17 +00:00
|
|
|
let fields = self.tcx.lookup_struct_fields(struct_id);
|
2015-06-08 21:51:54 +00:00
|
|
|
let field_name = get_ident(field_ref.ident.node).to_string();
|
|
|
|
for f in &fields {
|
|
|
|
if f.name == field_ref.ident.node.name {
|
|
|
|
// We don't really need a sub-span here, but no harm done
|
|
|
|
let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
|
|
|
|
return VariableRefData {
|
|
|
|
name: field_name,
|
|
|
|
span: sub_span.unwrap(),
|
|
|
|
scope: parent,
|
|
|
|
ref_id: f.id,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
self.tcx.sess.span_bug(field_ref.span,
|
|
|
|
&format!("Couldn't find field {}", field_name));
|
2015-06-08 21:51:54 +00:00
|
|
|
}
|
|
|
|
|
2015-05-10 20:35:08 +00:00
|
|
|
pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
|
|
|
|
// FIXME
|
|
|
|
unimplemented!();
|
2015-05-05 07:27:44 +00:00
|
|
|
}
|
2015-06-05 05:50:04 +00:00
|
|
|
|
|
|
|
fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
|
2015-06-13 22:49:28 +00:00
|
|
|
if !self.tcx.def_map.borrow().contains_key(&ref_id) {
|
|
|
|
self.tcx.sess.bug(&format!("def_map has no key for {} in lookup_type_ref",
|
|
|
|
ref_id));
|
2015-06-05 05:50:04 +00:00
|
|
|
}
|
2015-06-13 22:49:28 +00:00
|
|
|
let def = self.tcx.def_map.borrow().get(&ref_id).unwrap().full_def();
|
2015-06-05 05:50:04 +00:00
|
|
|
match def {
|
|
|
|
def::DefPrimTy(_) => None,
|
|
|
|
_ => Some(def.def_id()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-08 08:54:45 +00:00
|
|
|
#[inline]
|
|
|
|
fn enclosing_scope(&self, id: NodeId) -> NodeId {
|
|
|
|
self.tcx.map.get_enclosing_scope(id).unwrap_or(0)
|
|
|
|
}
|
2015-05-05 06:46:09 +00:00
|
|
|
}
|
|
|
|
|
2015-05-05 10:03:20 +00:00
|
|
|
// An AST visitor for collecting paths from patterns.
|
|
|
|
struct PathCollector {
|
2015-05-10 20:35:08 +00:00
|
|
|
// The Row field identifies the kind of pattern.
|
|
|
|
collected_paths: Vec<(NodeId, ast::Path, ast::Mutability, recorder::Row)>,
|
2015-05-05 10:03:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PathCollector {
|
|
|
|
fn new() -> PathCollector {
|
|
|
|
PathCollector {
|
|
|
|
collected_paths: vec![],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'v> Visitor<'v> for PathCollector {
|
|
|
|
fn visit_pat(&mut self, p: &ast::Pat) {
|
2015-05-10 20:35:08 +00:00
|
|
|
if generated_code(p.span) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-05 10:03:20 +00:00
|
|
|
match p.node {
|
|
|
|
ast::PatStruct(ref path, _, _) => {
|
2015-05-10 20:35:08 +00:00
|
|
|
self.collected_paths.push((p.id,
|
|
|
|
path.clone(),
|
|
|
|
ast::MutMutable,
|
2015-06-08 21:51:54 +00:00
|
|
|
recorder::TypeRef));
|
2015-05-05 10:03:20 +00:00
|
|
|
}
|
|
|
|
ast::PatEnum(ref path, _) |
|
|
|
|
ast::PatQPath(_, ref path) => {
|
2015-05-10 20:35:08 +00:00
|
|
|
self.collected_paths.push((p.id, path.clone(), ast::MutMutable, recorder::VarRef));
|
2015-05-05 10:03:20 +00:00
|
|
|
}
|
|
|
|
ast::PatIdent(bm, ref path1, _) => {
|
2015-05-10 20:35:08 +00:00
|
|
|
debug!("PathCollector, visit ident in pat {}: {:?} {:?}",
|
|
|
|
token::get_ident(path1.node),
|
|
|
|
p.span,
|
|
|
|
path1.span);
|
2015-05-05 10:03:20 +00:00
|
|
|
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.
|
2015-05-10 20:35:08 +00:00
|
|
|
ast::BindByRef(_) => ast::MutImmutable,
|
|
|
|
ast::BindByValue(mt) => mt,
|
2015-05-05 10:03:20 +00:00
|
|
|
};
|
|
|
|
// collect path for either visit_local or visit_arm
|
2015-05-10 20:35:08 +00:00
|
|
|
let path = ast_util::ident_to_path(path1.span, path1.node);
|
2015-05-05 10:03:20 +00:00
|
|
|
self.collected_paths.push((p.id, path, immut, recorder::VarRef));
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
visit::walk_pat(self, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-27 05:00:43 +00:00
|
|
|
#[allow(deprecated)]
|
2015-06-13 22:49:28 +00:00
|
|
|
pub fn process_crate(tcx: &ty::ctxt,
|
2014-11-27 12:21:26 +00:00
|
|
|
analysis: &ty::CrateAnalysis,
|
2015-01-11 02:03:34 +00:00
|
|
|
odir: Option<&Path>) {
|
2015-06-13 22:49:28 +00:00
|
|
|
let krate = tcx.map.krate();
|
2014-02-05 04:31:33 +00:00
|
|
|
if generated_code(krate.span) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-23 09:29:41 +00:00
|
|
|
assert!(analysis.glob_map.is_some());
|
2015-02-20 19:08:14 +00:00
|
|
|
let cratename = match attr::find_crate_name(&krate.attrs) {
|
2015-02-04 00:04:50 +00:00
|
|
|
Some(name) => name.to_string(),
|
2014-02-05 04:31:33 +00:00
|
|
|
None => {
|
|
|
|
info!("Could not find crate name, using 'unknown_crate'");
|
2015-06-08 14:55:35 +00:00
|
|
|
String::from("unknown_crate")
|
2014-02-05 04:31:33 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-06-06 20:21:18 +00:00
|
|
|
info!("Dumping crate {}", cratename);
|
2014-02-05 04:31:33 +00:00
|
|
|
|
|
|
|
// find a path to dump our data to
|
2015-02-27 05:00:43 +00:00
|
|
|
let mut root_path = match env::var_os("DXR_RUST_TEMP_FOLDER") {
|
2015-03-18 16:14:54 +00:00
|
|
|
Some(val) => PathBuf::from(val),
|
2015-02-27 05:00:43 +00:00
|
|
|
None => match odir {
|
2015-01-11 02:03:34 +00:00
|
|
|
Some(val) => val.join("dxr"),
|
2015-03-18 16:14:54 +00:00
|
|
|
None => PathBuf::from("dxr-temp"),
|
2014-02-05 04:31:33 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
if let Err(e) = fs::create_dir_all(&root_path) {
|
|
|
|
tcx.sess.err(&format!("Could not create directory {}: {}",
|
|
|
|
root_path.display(), e));
|
2014-02-05 04:31:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let disp = root_path.display();
|
|
|
|
info!("Writing output to {}", disp);
|
|
|
|
}
|
|
|
|
|
2014-07-03 01:27:07 +00:00
|
|
|
// Create output file.
|
2014-02-05 04:31:33 +00:00
|
|
|
let mut out_name = cratename.clone();
|
|
|
|
out_name.push_str(".csv");
|
2015-02-27 05:00:43 +00:00
|
|
|
root_path.push(&out_name);
|
2014-02-05 04:31:33 +00:00
|
|
|
let output_file = match File::create(&root_path) {
|
|
|
|
Ok(f) => box f,
|
|
|
|
Err(e) => {
|
|
|
|
let disp = root_path.display();
|
2015-06-13 22:49:28 +00:00
|
|
|
tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
|
2014-02-05 04:31:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
root_path.pop();
|
|
|
|
|
2015-06-13 22:49:28 +00:00
|
|
|
let mut visitor = dump_csv::DumpCsvVisitor::new(tcx, analysis, output_file);
|
2014-02-05 04:31:33 +00:00
|
|
|
|
2015-06-02 19:21:49 +00:00
|
|
|
visitor.dump_crate_info(&cratename, krate);
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut visitor, krate);
|
2014-02-05 04:31:33 +00:00
|
|
|
}
|
2015-05-04 10:33:07 +00:00
|
|
|
|
|
|
|
// Utility functions for the module.
|
|
|
|
|
|
|
|
// Helper function to escape quotes in a string
|
|
|
|
fn escape(s: String) -> String {
|
|
|
|
s.replace("\"", "\"\"")
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the expression is a macro expansion or other generated code, run screaming
|
|
|
|
// and don't index.
|
|
|
|
fn generated_code(span: Span) -> bool {
|
|
|
|
span.expn_id != NO_EXPANSION || span == DUMMY_SP
|
|
|
|
}
|