Rollup merge of #40751 - nrc:save-callback, r=eddyb

save-analysis: allow clients to get data directly without writing to a file.
This commit is contained in:
Alex Crichton 2017-03-27 15:24:17 -05:00
commit 51371157e2
4 changed files with 167 additions and 75 deletions

View File

@ -258,10 +258,7 @@ 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_api
sess.opts.debugging_opts.keep_ast || ::save_analysis(sess)
}
/// The name used for source code that doesn't originate in a file

View File

@ -67,6 +67,7 @@ use pretty::{PpMode, UserIdentifiedItem};
use rustc_resolve as resolve;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
use rustc_trans::back::link;
use rustc_trans::back::write::{create_target_machine, RELOC_MODEL_ARGS, CODE_GEN_MODEL_ARGS};
use rustc::dep_graph::DepGraph;
@ -507,8 +508,9 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls {
state.expanded_crate.unwrap(),
state.analysis.unwrap(),
state.crate_name.unwrap(),
state.out_dir,
save_analysis_format(state.session))
DumpHandler::new(save_analysis_format(state.session),
state.out_dir,
state.crate_name.unwrap()))
});
};
control.after_analysis.run_callback_on_error = true;

View File

@ -22,22 +22,52 @@ use external_data::*;
use data::{self, VariableKind};
use dump::Dump;
pub struct JsonDumper<'b, W: Write + 'b> {
output: &'b mut W,
pub struct JsonDumper<O: DumpOutput> {
result: Analysis,
output: O,
}
impl<'b, W: Write> JsonDumper<'b, W> {
pub fn new(writer: &'b mut W) -> JsonDumper<'b, W> {
JsonDumper { output: writer, result: Analysis::new() }
pub trait DumpOutput {
fn dump(&mut self, result: &Analysis);
}
pub struct WriteOutput<'b, W: Write + 'b> {
output: &'b mut W,
}
impl<'b, W: Write> DumpOutput for WriteOutput<'b, W> {
fn dump(&mut self, result: &Analysis) {
if let Err(_) = write!(self.output, "{}", as_json(&result)) {
error!("Error writing output");
}
}
}
impl<'b, W: Write> Drop for JsonDumper<'b, W> {
pub struct CallbackOutput<'b> {
callback: &'b mut FnMut(&Analysis),
}
impl<'b> DumpOutput for CallbackOutput<'b> {
fn dump(&mut self, result: &Analysis) {
(self.callback)(result)
}
}
impl<'b, W: Write> JsonDumper<WriteOutput<'b, W>> {
pub fn new(writer: &'b mut W) -> JsonDumper<WriteOutput<'b, W>> {
JsonDumper { output: WriteOutput { output: writer }, result: Analysis::new() }
}
}
impl<'b> JsonDumper<CallbackOutput<'b>> {
pub fn with_callback(callback: &'b mut FnMut(&Analysis)) -> JsonDumper<CallbackOutput<'b>> {
JsonDumper { output: CallbackOutput { callback: callback }, result: Analysis::new() }
}
}
impl<O: DumpOutput> Drop for JsonDumper<O> {
fn drop(&mut self) {
if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
error!("Error writing output");
}
self.output.dump(&self.result);
}
}
@ -49,7 +79,7 @@ macro_rules! impl_fn {
}
}
impl<'b, W: Write + 'b> Dump for JsonDumper<'b, W> {
impl<'b, O: DumpOutput + 'b> Dump for JsonDumper<O> {
fn crate_prelude(&mut self, data: CratePreludeData) {
self.result.prelude = Some(data)
}

View File

@ -48,6 +48,7 @@ use rustc::hir::def::Def;
use rustc::hir::map::Node;
use rustc::hir::def_id::DefId;
use rustc::session::config::CrateType::CrateTypeExecutable;
use rustc::session::Session;
use rustc::ty::{self, TyCtxt};
use std::env;
@ -866,56 +867,132 @@ impl Format {
}
}
pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
krate: &ast::Crate,
analysis: &'l ty::CrateAnalysis,
cratename: &str,
odir: Option<&Path>,
format: Format) {
/// Defines what to do with the results of saving the analysis.
pub trait SaveHandler {
fn save<'l, 'tcx>(&mut self,
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str);
}
/// Dump the save-analysis results to a file.
pub struct DumpHandler<'a> {
format: Format,
odir: Option<&'a Path>,
cratename: String
}
impl<'a> DumpHandler<'a> {
pub fn new(format: Format, odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
DumpHandler {
format: format,
odir: odir,
cratename: cratename.to_owned()
}
}
fn output_file(&self, sess: &Session) -> File {
let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
Some(val) => PathBuf::from(val),
None => match self.odir {
Some(val) => val.join("save-analysis"),
None => PathBuf::from("save-analysis-temp"),
},
};
if let Err(e) = std::fs::create_dir_all(&root_path) {
error!("Could not create directory {}: {}", root_path.display(), e);
}
{
let disp = root_path.display();
info!("Writing output to {}", disp);
}
let executable = sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
let mut out_name = if executable {
"".to_owned()
} else {
"lib".to_owned()
};
out_name.push_str(&self.cratename);
out_name.push_str(&sess.opts.cg.extra_filename);
out_name.push_str(self.format.extension());
root_path.push(&out_name);
let output_file = File::create(&root_path).unwrap_or_else(|e| {
let disp = root_path.display();
sess.fatal(&format!("Could not open {}: {}", disp, e));
});
root_path.pop();
output_file
}
}
impl<'a> SaveHandler for DumpHandler<'a> {
fn save<'l, 'tcx>(&mut self,
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str) {
macro_rules! dump {
($new_dumper: expr) => {{
let mut dumper = $new_dumper;
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
visitor.dump_crate_info(cratename, krate);
visit::walk_crate(&mut visitor, krate);
}}
}
let output = &mut self.output_file(&save_ctxt.tcx.sess);
match self.format {
Format::Csv => dump!(CsvDumper::new(output)),
Format::Json => dump!(JsonDumper::new(output)),
Format::JsonApi => dump!(JsonApiDumper::new(output)),
}
}
}
/// Call a callback with the results of save-analysis.
pub struct CallbackHandler<'b> {
pub callback: &'b mut FnMut(&rls_data::Analysis),
}
impl<'b> SaveHandler for CallbackHandler<'b> {
fn save<'l, 'tcx>(&mut self,
save_ctxt: SaveContext<'l, 'tcx>,
krate: &ast::Crate,
cratename: &str) {
macro_rules! dump {
($new_dumper: expr) => {{
let mut dumper = $new_dumper;
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
visitor.dump_crate_info(cratename, krate);
visit::walk_crate(&mut visitor, krate);
}}
}
// We're using the JsonDumper here because it has the format of the
// save-analysis results that we will pass to the callback. IOW, we are
// using the JsonDumper to collect the save-analysis results, but not
// actually to dump them to a file. This is all a bit convoluted and
// there is certainly a simpler design here trying to get out (FIXME).
dump!(JsonDumper::with_callback(self.callback))
}
}
pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
krate: &ast::Crate,
analysis: &'l ty::CrateAnalysis,
cratename: &str,
mut handler: H) {
let _ignore = tcx.dep_graph.in_ignore();
assert!(analysis.glob_map.is_some());
info!("Dumping crate {}", cratename);
// find a path to dump our data to
let mut root_path = match env::var_os("RUST_SAVE_ANALYSIS_FOLDER") {
Some(val) => PathBuf::from(val),
None => match odir {
Some(val) => val.join("save-analysis"),
None => PathBuf::from("save-analysis-temp"),
},
};
if let Err(e) = std::fs::create_dir_all(&root_path) {
tcx.sess.err(&format!("Could not create directory {}: {}",
root_path.display(),
e));
}
{
let disp = root_path.display();
info!("Writing output to {}", disp);
}
// Create output file.
let executable = tcx.sess.crate_types.borrow().iter().any(|ct| *ct == CrateTypeExecutable);
let mut out_name = if executable {
"".to_owned()
} else {
"lib".to_owned()
};
out_name.push_str(&cratename);
out_name.push_str(&tcx.sess.opts.cg.extra_filename);
out_name.push_str(format.extension());
root_path.push(&out_name);
let mut output_file = File::create(&root_path).unwrap_or_else(|e| {
let disp = root_path.display();
tcx.sess.fatal(&format!("Could not open {}: {}", disp, e));
});
root_path.pop();
let output = &mut output_file;
let save_ctxt = SaveContext {
tcx: tcx,
tables: &ty::TypeckTables::empty(),
@ -923,21 +1000,7 @@ pub fn process_crate<'l, 'tcx>(tcx: TyCtxt<'l, 'tcx, 'tcx>,
span_utils: SpanUtils::new(&tcx.sess),
};
macro_rules! dump {
($new_dumper: expr) => {{
let mut dumper = $new_dumper;
let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper);
visitor.dump_crate_info(cratename, krate);
visit::walk_crate(&mut visitor, krate);
}}
}
match format {
Format::Csv => dump!(CsvDumper::new(output)),
Format::Json => dump!(JsonDumper::new(output)),
Format::JsonApi => dump!(JsonApiDumper::new(output)),
}
handler.save(save_ctxt, krate, cratename)
}
// Utility functions for the module.