Auto merge of #36132 - nrc:save-std, r=@eddyb

Add --Zsave-analysis-api

This is a save-analysis variation which can be used with libraries distributed without their source (e.g., libstd). It will allow IDEs and other tools to get info about types and create URLs to docs and source, without the unnecessary clutter of internal-only save-analysis info. I'm sure we'll iterate somewhat on the design, but this is a first draft.
This commit is contained in:
bors 2016-09-04 02:40:31 -07:00 committed by GitHub
commit e77d86c142
9 changed files with 608 additions and 83 deletions

View File

@ -849,9 +849,13 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
ls: bool = (false, parse_bool, [UNTRACKED],
"list the symbols defined by a library crate"),
save_analysis: bool = (false, parse_bool, [UNTRACKED],
"write syntax and type analysis (in JSON format) information in addition to normal output"),
"write syntax and type analysis (in JSON format) information, in \
addition to normal output"),
save_analysis_csv: bool = (false, parse_bool, [UNTRACKED],
"write syntax and type analysis (in CSV format) information in addition to normal output"),
"write syntax and type analysis (in CSV format) information, in addition to normal output"),
save_analysis_api: bool = (false, parse_bool, [UNTRACKED],
"write syntax and type analysis information for opaque libraries (in JSON format), \
in addition to normal output"),
print_move_fragments: bool = (false, parse_bool, [UNTRACKED],
"print out move-fragment data for every fn"),
flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED],
@ -2365,6 +2369,8 @@ mod tests {
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
opts.debugging_opts.save_analysis_csv = true;
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
opts.debugging_opts.save_analysis_api = true;
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
opts.debugging_opts.print_move_fragments = true;
assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
opts.debugging_opts.flowgraph_print_loans = true;

View File

@ -250,7 +250,8 @@ fn keep_hygiene_data(sess: &Session) -> bool {
fn keep_ast(sess: &Session) -> bool {
sess.opts.debugging_opts.keep_ast ||
sess.opts.debugging_opts.save_analysis ||
sess.opts.debugging_opts.save_analysis_csv
sess.opts.debugging_opts.save_analysis_csv ||
sess.opts.debugging_opts.save_analysis_api
}
/// The name used for source code that doesn't originate in a file

View File

@ -555,7 +555,8 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
fn save_analysis(sess: &Session) -> bool {
sess.opts.debugging_opts.save_analysis ||
sess.opts.debugging_opts.save_analysis_csv
sess.opts.debugging_opts.save_analysis_csv ||
sess.opts.debugging_opts.save_analysis_api
}
fn save_analysis_format(sess: &Session) -> save::Format {
@ -563,6 +564,8 @@ fn save_analysis_format(sess: &Session) -> save::Format {
save::Format::Json
} else if sess.opts.debugging_opts.save_analysis_csv {
save::Format::Csv
} else if sess.opts.debugging_opts.save_analysis_api {
save::Format::JsonApi
} else {
unreachable!();
}

View File

@ -13,8 +13,9 @@
//! The `Dump` trait can be used together with `DumpVisitor` in order to
//! retrieve the data from a crate.
use rustc::hir;
use rustc::hir::def_id::DefId;
use syntax::ast::{CrateNum, NodeId};
use syntax::ast::{self, CrateNum, NodeId};
use syntax_pos::Span;
pub struct CrateData {
@ -76,6 +77,35 @@ pub enum Data {
VariableRefData(VariableRefData),
}
#[derive(Eq, PartialEq, Clone, Copy, Debug, RustcEncodable)]
pub enum Visibility {
Public,
Restricted,
Inherited,
}
impl<'a> From<&'a ast::Visibility> for Visibility {
fn from(v: &'a ast::Visibility) -> Visibility {
match *v {
ast::Visibility::Public => Visibility::Public,
ast::Visibility::Crate(_) => Visibility::Restricted,
ast::Visibility::Restricted { .. } => Visibility::Restricted,
ast::Visibility::Inherited => Visibility::Inherited,
}
}
}
impl<'a> From<&'a hir::Visibility> for Visibility {
fn from(v: &'a hir::Visibility) -> Visibility {
match *v {
hir::Visibility::Public => Visibility::Public,
hir::Visibility::Crate => Visibility::Restricted,
hir::Visibility::Restricted { .. } => Visibility::Restricted,
hir::Visibility::Inherited => Visibility::Inherited,
}
}
}
/// Data for the prelude of a crate.
#[derive(Debug, RustcEncodable)]
pub struct CratePreludeData {
@ -103,7 +133,7 @@ pub struct EnumData {
pub span: Span,
pub scope: NodeId,
pub variants: Vec<NodeId>,
pub visibility: Visibility,
}
/// Data for extern crates.
@ -135,6 +165,8 @@ pub struct FunctionData {
pub span: Span,
pub scope: NodeId,
pub value: String,
pub visibility: Visibility,
pub parent: Option<NodeId>,
}
/// Data about a function call.
@ -215,6 +247,7 @@ pub struct MethodData {
pub scope: NodeId,
pub value: String,
pub decl_id: Option<DefId>,
pub visibility: Visibility,
}
/// Data for modules.
@ -227,6 +260,7 @@ pub struct ModData {
pub scope: NodeId,
pub filename: String,
pub items: Vec<NodeId>,
pub visibility: Visibility,
}
/// Data for a reference to a module.
@ -248,6 +282,7 @@ pub struct StructData {
pub scope: NodeId,
pub value: String,
pub fields: Vec<NodeId>,
pub visibility: Visibility,
}
#[derive(Debug, RustcEncodable)]
@ -258,7 +293,8 @@ pub struct StructVariantData {
pub qualname: String,
pub type_value: String,
pub value: String,
pub scope: NodeId
pub scope: NodeId,
pub parent: Option<NodeId>,
}
#[derive(Debug, RustcEncodable)]
@ -270,6 +306,7 @@ pub struct TraitData {
pub scope: NodeId,
pub value: String,
pub items: Vec<NodeId>,
pub visibility: Visibility,
}
#[derive(Debug, RustcEncodable)]
@ -280,7 +317,8 @@ pub struct TupleVariantData {
pub qualname: String,
pub type_value: String,
pub value: String,
pub scope: NodeId
pub scope: NodeId,
pub parent: Option<NodeId>,
}
/// Data for a typedef.
@ -291,6 +329,8 @@ pub struct TypeDefData {
pub span: Span,
pub qualname: String,
pub value: String,
pub visibility: Visibility,
pub parent: Option<NodeId>,
}
/// Data for a reference to a type or trait.
@ -308,7 +348,8 @@ pub struct UseData {
pub span: Span,
pub name: String,
pub mod_id: Option<DefId>,
pub scope: NodeId
pub scope: NodeId,
pub visibility: Visibility,
}
#[derive(Debug, RustcEncodable)]
@ -316,7 +357,8 @@ pub struct UseGlobData {
pub id: NodeId,
pub span: Span,
pub names: Vec<String>,
pub scope: NodeId
pub scope: NodeId,
pub visibility: Visibility,
}
/// Data for local and global variables (consts and statics).
@ -328,8 +370,10 @@ pub struct VariableData {
pub qualname: String,
pub span: Span,
pub scope: NodeId,
pub parent: Option<NodeId>,
pub value: String,
pub type_value: String,
pub visibility: Visibility,
}
#[derive(Debug, RustcEncodable)]

View File

@ -365,7 +365,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: format!("{}::{}", qualname, path_to_string(p)),
type_value: typ,
value: String::new(),
scope: 0
scope: 0,
parent: None,
visibility: Visibility::Inherited,
}.lower(self.tcx));
}
}
@ -377,6 +379,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
body: Option<&ast::Block>,
id: ast::NodeId,
name: ast::Name,
vis: Visibility,
span: Span) {
debug!("process_method: {}:{}", id, name);
@ -417,6 +420,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: method_data.qualname.clone(),
value: sig_str,
decl_id: decl_id,
visibility: vis,
}.lower(self.tcx));
}
@ -484,7 +488,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
name: name,
id: param.id,
qualname: qualname,
value: String::new()
value: String::new(),
visibility: Visibility::Inherited,
parent: None,
}.lower(self.tcx));
}
}
@ -528,12 +534,14 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
self.visit_expr(expr);
}
fn process_const(&mut self,
id: ast::NodeId,
name: ast::Name,
span: Span,
typ: &ast::Ty,
expr: &ast::Expr) {
fn process_assoc_const(&mut self,
id: ast::NodeId,
name: ast::Name,
span: Span,
typ: &ast::Ty,
expr: &ast::Expr,
parent_id: NodeId,
vis: Visibility) {
let qualname = format!("::{}", self.tcx.node_path_str(id));
let sub_span = self.span.sub_span_after_keyword(span, keywords::Const);
@ -547,7 +555,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: qualname,
value: self.span.snippet(expr.span),
type_value: ty_to_string(&typ),
scope: self.cur_scope
scope: self.cur_scope,
parent: Some(parent_id),
visibility: vis,
}.lower(self.tcx));
}
@ -589,6 +599,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
scope: self.cur_scope,
value: val,
fields: fields,
visibility: From::from(&item.vis),
}.lower(self.tcx));
}
@ -640,7 +651,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: qualname,
type_value: enum_data.qualname.clone(),
value: val,
scope: enum_data.scope
scope: enum_data.scope,
parent: Some(item.id),
}.lower(self.tcx));
}
}
@ -663,7 +675,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: qualname,
type_value: enum_data.qualname.clone(),
value: val,
scope: enum_data.scope
scope: enum_data.scope,
parent: Some(item.id),
}.lower(self.tcx));
}
}
@ -716,7 +729,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
}
self.process_generic_params(type_parameters, item.span, "", item.id);
for impl_item in impl_items {
self.visit_impl_item(impl_item);
self.process_impl_item(impl_item, item.id);
}
}
@ -745,6 +758,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
scope: self.cur_scope,
value: val,
items: methods.iter().map(|i| i.id).collect(),
visibility: From::from(&item.vis),
}.lower(self.tcx));
}
@ -785,7 +799,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
// walk generics and methods
self.process_generic_params(generics, item.span, &qualname, item.id);
for method in methods {
self.visit_trait_item(method)
self.process_trait_item(method, item.id)
}
}
@ -990,7 +1004,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
qualname: format!("{}${}", path_to_string(p), id),
value: value,
type_value: typ,
scope: 0
scope: 0,
parent: None,
visibility: Visibility::Inherited,
}.lower(self.tcx));
}
}
@ -1038,6 +1054,57 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> {
}
}
}
fn process_trait_item(&mut self, trait_item: &ast::TraitItem, trait_id: NodeId) {
self.process_macro_use(trait_item.span, trait_item.id);
match trait_item.node {
ast::TraitItemKind::Const(ref ty, Some(ref expr)) => {
self.process_assoc_const(trait_item.id,
trait_item.ident.name,
trait_item.span,
&ty,
&expr,
trait_id,
Visibility::Public);
}
ast::TraitItemKind::Method(ref sig, ref body) => {
self.process_method(sig,
body.as_ref().map(|x| &**x),
trait_item.id,
trait_item.ident.name,
Visibility::Public,
trait_item.span);
}
ast::TraitItemKind::Const(_, None) |
ast::TraitItemKind::Type(..) |
ast::TraitItemKind::Macro(_) => {}
}
}
fn process_impl_item(&mut self, impl_item: &ast::ImplItem, impl_id: NodeId) {
self.process_macro_use(impl_item.span, impl_item.id);
match impl_item.node {
ast::ImplItemKind::Const(ref ty, ref expr) => {
self.process_assoc_const(impl_item.id,
impl_item.ident.name,
impl_item.span,
&ty,
&expr,
impl_id,
From::from(&impl_item.vis));
}
ast::ImplItemKind::Method(ref sig, ref body) => {
self.process_method(sig,
Some(body),
impl_item.id,
impl_item.ident.name,
From::from(&impl_item.vis),
impl_item.span);
}
ast::ImplItemKind::Type(_) |
ast::ImplItemKind::Macro(_) => {}
}
}
}
impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D> {
@ -1073,7 +1140,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
id: item.id,
mod_id: mod_id,
name: ident.to_string(),
scope: self.cur_scope
scope: self.cur_scope,
visibility: From::from(&item.vis),
}.lower(self.tcx));
}
self.write_sub_paths_truncated(path, true);
@ -1096,7 +1164,8 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
span: sub_span.expect("No span found for use glob"),
id: item.id,
names: names,
scope: self.cur_scope
scope: self.cur_scope,
visibility: From::from(&item.vis),
}.lower(self.tcx));
}
self.write_sub_paths(path, true);
@ -1168,7 +1237,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
name: item.ident.to_string(),
id: item.id,
qualname: qualname.clone(),
value: value
value: value,
visibility: From::from(&item.vis),
parent: None,
}.lower(self.tcx));
}
@ -1193,51 +1264,6 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
}
}
fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
self.process_macro_use(trait_item.span, trait_item.id);
match trait_item.node {
ast::TraitItemKind::Const(ref ty, Some(ref expr)) => {
self.process_const(trait_item.id,
trait_item.ident.name,
trait_item.span,
&ty,
&expr);
}
ast::TraitItemKind::Method(ref sig, ref body) => {
self.process_method(sig,
body.as_ref().map(|x| &**x),
trait_item.id,
trait_item.ident.name,
trait_item.span);
}
ast::TraitItemKind::Const(_, None) |
ast::TraitItemKind::Type(..) |
ast::TraitItemKind::Macro(_) => {}
}
}
fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) {
self.process_macro_use(impl_item.span, impl_item.id);
match impl_item.node {
ast::ImplItemKind::Const(ref ty, ref expr) => {
self.process_const(impl_item.id,
impl_item.ident.name,
impl_item.span,
&ty,
&expr);
}
ast::ImplItemKind::Method(ref sig, ref body) => {
self.process_method(sig,
Some(body),
impl_item.id,
impl_item.ident.name,
impl_item.span);
}
ast::ImplItemKind::Type(_) |
ast::ImplItemKind::Macro(_) => {}
}
}
fn visit_ty(&mut self, t: &ast::Ty) {
self.process_macro_use(t.span, t.id);
match t.node {
@ -1400,7 +1426,9 @@ impl<'l, 'tcx: 'l, 'll, D: Dump +'ll> Visitor for DumpVisitor<'l, 'tcx, 'll, D>
qualname: format!("{}${}", path_to_string(p), id),
value: value,
type_value: String::new(),
scope: 0
scope: 0,
parent: None,
visibility: Visibility::Inherited,
}.lower(self.tcx));
}
}

View File

@ -15,7 +15,7 @@ use syntax::ast::{CrateNum, NodeId};
use syntax::codemap::CodeMap;
use syntax_pos::Span;
use data;
use data::{self, Visibility};
// FIXME: this should be pub(crate), but the current snapshot doesn't allow it yet
pub trait Lower {
@ -91,7 +91,8 @@ pub struct EnumData {
pub qualname: String,
pub span: SpanData,
pub scope: DefId,
pub variants: Vec<DefId>
pub variants: Vec<DefId>,
pub visibility: Visibility,
}
impl Lower for data::EnumData {
@ -106,6 +107,7 @@ impl Lower for data::EnumData {
span: SpanData::from_span(self.span, tcx.sess.codemap()),
scope: make_def_id(self.scope, &tcx.map),
variants: self.variants.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
visibility: self.visibility,
}
}
}
@ -166,6 +168,8 @@ pub struct FunctionData {
pub span: SpanData,
pub scope: DefId,
pub value: String,
pub visibility: Visibility,
pub parent: Option<DefId>,
}
impl Lower for data::FunctionData {
@ -180,6 +184,8 @@ impl Lower for data::FunctionData {
span: SpanData::from_span(self.span, tcx.sess.codemap()),
scope: make_def_id(self.scope, &tcx.map),
value: self.value,
visibility: self.visibility,
parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
}
}
}
@ -323,6 +329,8 @@ pub struct MethodData {
pub scope: DefId,
pub value: String,
pub decl_id: Option<DefId>,
pub visibility: Visibility,
pub parent: Option<DefId>
}
impl Lower for data::MethodData {
@ -337,6 +345,8 @@ impl Lower for data::MethodData {
qualname: self.qualname,
value: self.value,
decl_id: self.decl_id,
visibility: self.visibility,
parent: Some(make_def_id(self.scope, &tcx.map)),
}
}
}
@ -351,6 +361,7 @@ pub struct ModData {
pub scope: DefId,
pub filename: String,
pub items: Vec<DefId>,
pub visibility: Visibility,
}
impl Lower for data::ModData {
@ -365,6 +376,7 @@ impl Lower for data::ModData {
scope: make_def_id(self.scope, &tcx.map),
filename: self.filename,
items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
visibility: self.visibility,
}
}
}
@ -401,6 +413,7 @@ pub struct StructData {
pub scope: DefId,
pub value: String,
pub fields: Vec<DefId>,
pub visibility: Visibility,
}
impl Lower for data::StructData {
@ -416,6 +429,7 @@ impl Lower for data::StructData {
scope: make_def_id(self.scope, &tcx.map),
value: self.value,
fields: self.fields.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
visibility: self.visibility,
}
}
}
@ -428,7 +442,8 @@ pub struct StructVariantData {
pub qualname: String,
pub type_value: String,
pub value: String,
pub scope: DefId
pub scope: DefId,
pub parent: Option<DefId>,
}
impl Lower for data::StructVariantData {
@ -443,6 +458,7 @@ impl Lower for data::StructVariantData {
type_value: self.type_value,
value: self.value,
scope: make_def_id(self.scope, &tcx.map),
parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
}
}
}
@ -456,6 +472,7 @@ pub struct TraitData {
pub scope: DefId,
pub value: String,
pub items: Vec<DefId>,
pub visibility: Visibility,
}
impl Lower for data::TraitData {
@ -470,6 +487,7 @@ impl Lower for data::TraitData {
scope: make_def_id(self.scope, &tcx.map),
value: self.value,
items: self.items.into_iter().map(|id| make_def_id(id, &tcx.map)).collect(),
visibility: self.visibility,
}
}
}
@ -483,6 +501,7 @@ pub struct TupleVariantData {
pub type_value: String,
pub value: String,
pub scope: DefId,
pub parent: Option<DefId>,
}
impl Lower for data::TupleVariantData {
@ -497,6 +516,7 @@ impl Lower for data::TupleVariantData {
type_value: self.type_value,
value: self.value,
scope: make_def_id(self.scope, &tcx.map),
parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
}
}
}
@ -509,6 +529,8 @@ pub struct TypeDefData {
pub span: SpanData,
pub qualname: String,
pub value: String,
pub visibility: Visibility,
pub parent: Option<DefId>,
}
impl Lower for data::TypeDefData {
@ -521,6 +543,8 @@ impl Lower for data::TypeDefData {
span: SpanData::from_span(self.span, tcx.sess.codemap()),
qualname: self.qualname,
value: self.value,
visibility: self.visibility,
parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
}
}
}
@ -553,7 +577,8 @@ pub struct UseData {
pub span: SpanData,
pub name: String,
pub mod_id: Option<DefId>,
pub scope: DefId
pub scope: DefId,
pub visibility: Visibility,
}
impl Lower for data::UseData {
@ -566,6 +591,7 @@ impl Lower for data::UseData {
name: self.name,
mod_id: self.mod_id,
scope: make_def_id(self.scope, &tcx.map),
visibility: self.visibility,
}
}
}
@ -575,7 +601,8 @@ pub struct UseGlobData {
pub id: DefId,
pub span: SpanData,
pub names: Vec<String>,
pub scope: DefId
pub scope: DefId,
pub visibility: Visibility,
}
impl Lower for data::UseGlobData {
@ -587,6 +614,7 @@ impl Lower for data::UseGlobData {
span: SpanData::from_span(self.span, tcx.sess.codemap()),
names: self.names,
scope: make_def_id(self.scope, &tcx.map),
visibility: self.visibility,
}
}
}
@ -602,6 +630,8 @@ pub struct VariableData {
pub scope: DefId,
pub value: String,
pub type_value: String,
pub parent: Option<DefId>,
pub visibility: Visibility,
}
impl Lower for data::VariableData {
@ -617,6 +647,8 @@ impl Lower for data::VariableData {
scope: make_def_id(self.scope, &tcx.map),
value: self.value,
type_value: self.type_value,
parent: self.parent.map(|id| make_def_id(id, &tcx.map)),
visibility: self.visibility,
}
}
}

View File

@ -0,0 +1,393 @@
// Copyright 2016 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 std::io::Write;
use rustc::hir::def_id::DefId;
use rustc_serialize::json::as_json;
use external_data::*;
use data::{VariableKind, Visibility};
use dump::Dump;
// A dumper to dump a restricted set of JSON information, designed for use with
// libraries distributed without their source. Clients are likely to use type
// information here, and (for example) generate Rustdoc URLs, but don't need
// information for navigating the source of the crate.
// Relative to the regular JSON save-analysis info, this form is filtered to
// remove non-visible items, but includes some extra info for items (e.g., the
// parent field for finding the struct to which a field belongs).
pub struct JsonApiDumper<'b, W: Write + 'b> {
output: &'b mut W,
result: Analysis,
}
impl<'b, W: Write> JsonApiDumper<'b, W> {
pub fn new(writer: &'b mut W) -> JsonApiDumper<'b, W> {
JsonApiDumper { output: writer, result: Analysis::new() }
}
}
impl<'b, W: Write> Drop for JsonApiDumper<'b, W> {
fn drop(&mut self) {
if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
error!("Error writing output");
}
}
}
macro_rules! impl_fn {
($fn_name: ident, $data_type: ident, $bucket: ident) => {
fn $fn_name(&mut self, data: $data_type) {
if let Some(datum) = From::from(data) {
self.result.$bucket.push(datum);
}
}
}
}
impl<'b, W: Write + 'b> Dump for JsonApiDumper<'b, W> {
fn crate_prelude(&mut self, data: CratePreludeData) {
self.result.prelude = Some(data)
}
impl_fn!(use_data, UseData, imports);
impl_fn!(use_glob, UseGlobData, imports);
impl_fn!(enum_data, EnumData, defs);
impl_fn!(tuple_variant, TupleVariantData, defs);
impl_fn!(struct_variant, StructVariantData, defs);
impl_fn!(struct_data, StructData, defs);
impl_fn!(trait_data, TraitData, defs);
impl_fn!(function, FunctionData, defs);
impl_fn!(method, MethodData, defs);
impl_fn!(macro_data, MacroData, defs);
impl_fn!(mod_data, ModData, defs);
impl_fn!(typedef, TypeDefData, defs);
impl_fn!(variable, VariableData, defs);
}
// FIXME methods. The defs have information about possible overriding and the
// refs have decl information (e.g., a trait method where we know the required
// method, but not the supplied method). In both cases, we are currently
// ignoring it.
#[derive(Debug, RustcEncodable)]
struct Analysis {
prelude: Option<CratePreludeData>,
imports: Vec<Import>,
defs: Vec<Def>,
}
impl Analysis {
fn new() -> Analysis {
Analysis {
prelude: None,
imports: vec![],
defs: vec![],
}
}
}
// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
// we use our own Id which is the same, but without the newtype.
#[derive(Debug, RustcEncodable)]
struct Id {
krate: u32,
index: u32,
}
impl From<DefId> for Id {
fn from(id: DefId) -> Id {
Id {
krate: id.krate,
index: id.index.as_u32(),
}
}
}
#[derive(Debug, RustcEncodable)]
struct Import {
kind: ImportKind,
id: Id,
span: SpanData,
name: String,
value: String,
}
#[derive(Debug, RustcEncodable)]
enum ImportKind {
Use,
GlobUse,
}
impl From<UseData> for Option<Import> {
fn from(data: UseData) -> Option<Import> {
match data.visibility {
Visibility::Public => Some(Import {
kind: ImportKind::Use,
id: From::from(data.id),
span: data.span,
name: data.name,
value: String::new(),
}),
_ => None,
}
}
}
impl From<UseGlobData> for Option<Import> {
fn from(data: UseGlobData) -> Option<Import> {
match data.visibility {
Visibility::Public => Some(Import {
kind: ImportKind::GlobUse,
id: From::from(data.id),
span: data.span,
name: "*".to_owned(),
value: data.names.join(", "),
}),
_ => None,
}
}
}
#[derive(Debug, RustcEncodable)]
struct Def {
kind: DefKind,
id: Id,
span: SpanData,
name: String,
qualname: String,
value: String,
parent: Option<Id>,
children: Vec<Id>,
decl_id: Option<Id>,
}
#[derive(Debug, RustcEncodable)]
enum DefKind {
// value = variant names
Enum,
// value = enum name + variant name + types
Tuple,
// value = [enum name +] name + fields
Struct,
// value = signature
Trait,
// value = type + generics
Function,
// value = type + generics
Method,
// No id, no value.
Macro,
// value = file_name
Mod,
// value = aliased type
Type,
// value = type and init expression (for all variable kinds).
Static,
Const,
Field,
}
impl From<EnumData> for Option<Def> {
fn from(data: EnumData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Enum,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
parent: None,
children: data.variants.into_iter().map(|id| From::from(id)).collect(),
decl_id: None,
}),
_ => None,
}
}
}
impl From<TupleVariantData> for Option<Def> {
fn from(data: TupleVariantData) -> Option<Def> {
Some(Def {
kind: DefKind::Tuple,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
parent: data.parent.map(|id| From::from(id)),
children: vec![],
decl_id: None,
})
}
}
impl From<StructVariantData> for Option<Def> {
fn from(data: StructVariantData) -> Option<Def> {
Some(Def {
kind: DefKind::Struct,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
parent: data.parent.map(|id| From::from(id)),
children: vec![],
decl_id: None,
})
}
}
impl From<StructData> for Option<Def> {
fn from(data: StructData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Struct,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
parent: None,
children: data.fields.into_iter().map(|id| From::from(id)).collect(),
decl_id: None,
}),
_ => None,
}
}
}
impl From<TraitData> for Option<Def> {
fn from(data: TraitData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Trait,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
children: data.items.into_iter().map(|id| From::from(id)).collect(),
parent: None,
decl_id: None,
}),
_ => None,
}
}
}
impl From<FunctionData> for Option<Def> {
fn from(data: FunctionData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Function,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
children: vec![],
parent: data.parent.map(|id| From::from(id)),
decl_id: None,
}),
_ => None,
}
}
}
impl From<MethodData> for Option<Def> {
fn from(data: MethodData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Method,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
children: vec![],
parent: data.parent.map(|id| From::from(id)),
decl_id: data.decl_id.map(|id| From::from(id)),
}),
_ => None,
}
}
}
impl From<MacroData> for Option<Def> {
fn from(data: MacroData) -> Option<Def> {
Some(Def {
kind: DefKind::Macro,
id: From::from(null_def_id()),
span: data.span,
name: data.name,
qualname: data.qualname,
value: String::new(),
children: vec![],
parent: None,
decl_id: None,
})
}
}
impl From<ModData> for Option<Def> {
fn from(data:ModData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Mod,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.filename,
children: data.items.into_iter().map(|id| From::from(id)).collect(),
parent: None,
decl_id: None,
}),
_ => None,
}
}
}
impl From<TypeDefData> for Option<Def> {
fn from(data: TypeDefData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: DefKind::Type,
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
children: vec![],
parent: data.parent.map(|id| From::from(id)),
decl_id: None,
}),
_ => None,
}
}
}
impl From<VariableData> for Option<Def> {
fn from(data: VariableData) -> Option<Def> {
match data.visibility {
Visibility::Public => Some(Def {
kind: match data.kind {
VariableKind::Static => DefKind::Static,
VariableKind::Const => DefKind::Const,
VariableKind::Local => { return None }
VariableKind::Field => DefKind::Field,
},
id: From::from(data.id),
span: data.span,
name: data.name,
qualname: data.qualname,
value: data.value,
children: vec![],
parent: data.parent.map(|id| From::from(id)),
decl_id: None,
}),
_ => None,
}
}
}

View File

@ -30,6 +30,7 @@ extern crate serialize as rustc_serialize;
extern crate syntax_pos;
mod csv_dumper;
mod json_api_dumper;
mod json_dumper;
mod data;
mod dump;
@ -57,6 +58,7 @@ use syntax::codemap::MacroAttribute;
use syntax_pos::*;
pub use self::csv_dumper::CsvDumper;
pub use self::json_api_dumper::JsonApiDumper;
pub use self::json_dumper::JsonDumper;
pub use self::data::*;
pub use self::dump::Dump;
@ -138,6 +140,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
span: sub_span.unwrap(),
scope: self.enclosing_scope(item.id),
value: make_signature(decl, generics),
visibility: From::from(&item.vis),
parent: None,
}))
}
ast::ItemKind::Static(ref typ, mt, ref expr) => {
@ -160,8 +164,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
qualname: qualname,
span: sub_span.unwrap(),
scope: self.enclosing_scope(item.id),
parent: None,
value: value,
type_value: ty_to_string(&typ),
visibility: From::from(&item.vis),
}))
}
ast::ItemKind::Const(ref typ, ref expr) => {
@ -175,8 +181,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
qualname: qualname,
span: sub_span.unwrap(),
scope: self.enclosing_scope(item.id),
parent: None,
value: self.span_utils.snippet(expr.span),
type_value: ty_to_string(&typ),
visibility: From::from(&item.vis),
}))
}
ast::ItemKind::Mod(ref m) => {
@ -195,6 +203,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
scope: self.enclosing_scope(item.id),
filename: filename,
items: m.items.iter().map(|i| i.id).collect(),
visibility: From::from(&item.vis),
}))
}
ast::ItemKind::Enum(ref def, _) => {
@ -215,6 +224,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
qualname: qualname,
scope: self.enclosing_scope(item.id),
variants: def.variants.iter().map(|v| v.node.data.id()).collect(),
visibility: From::from(&item.vis),
}))
}
ast::ItemKind::Impl(_, _, _, ref trait_ref, ref typ, _) => {
@ -277,8 +287,10 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
qualname: qualname,
span: sub_span.unwrap(),
scope: scope,
parent: Some(scope),
value: "".to_owned(),
type_value: typ,
visibility: From::from(&field.vis),
})
} else {
None
@ -291,7 +303,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
name: ast::Name, span: Span) -> Option<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(self.tcx.map.local_def_id(id)) {
let (qualname, vis) = match self.tcx.impl_of_method(self.tcx.map.local_def_id(id)) {
Some(impl_id) => match self.tcx.map.get_if_local(impl_id) {
Some(NodeItem(item)) => {
match item.node {
@ -304,7 +316,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
result.push_str(&self.tcx.item_path_str(def_id));
}
result.push_str(">");
result
(result, From::from(&item.vis))
}
_ => {
span_bug!(span,
@ -325,8 +337,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
None => match self.tcx.trait_of_item(self.tcx.map.local_def_id(id)) {
Some(def_id) => {
match self.tcx.map.get_if_local(def_id) {
Some(NodeItem(_)) => {
format!("::{}", self.tcx.item_path_str(def_id))
Some(NodeItem(item)) => {
(format!("::{}", self.tcx.item_path_str(def_id)), From::from(&item.vis))
}
r => {
span_bug!(span,
@ -358,6 +370,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
let sub_span = self.span_utils.sub_span_after_keyword(span, keywords::Fn);
filter!(self.span_utils, sub_span, span, None);
let parent_scope = self.enclosing_scope(id);
Some(FunctionData {
id: id,
name: name.to_string(),
@ -367,6 +380,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
scope: self.enclosing_scope(id),
// FIXME you get better data here by using the visitor.
value: String::new(),
visibility: vis,
parent: Some(parent_scope),
})
}
@ -728,13 +743,14 @@ impl Visitor for PathCollector {
pub enum Format {
Csv,
Json,
JsonApi,
}
impl Format {
fn extension(&self) -> &'static str {
match *self {
Format::Csv => ".csv",
Format::Json => ".json",
Format::Json | Format::JsonApi => ".json",
}
}
}
@ -804,6 +820,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
match format {
Format::Csv => dump!(CsvDumper::new(output)),
Format::Json => dump!(JsonDumper::new(output)),
Format::JsonApi => dump!(JsonApiDumper::new(output)),
}
}

View File

@ -5,3 +5,4 @@ krate2: krate2.rs
code: foo.rs krate2
$(RUSTC) foo.rs -Zsave-analysis-csv
$(RUSTC) foo.rs -Zsave-analysis
$(RUSTC) foo.rs -Zsave-analysis-api