Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

save-analysis: allow clients to get data directly without writing to a file. #40751

Merged
merged 1 commit into from
Mar 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,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
Expand Down
6 changes: 4 additions & 2 deletions src/librustc_driver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,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;
Expand Down Expand Up @@ -506,8 +507,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;
Expand Down
50 changes: 40 additions & 10 deletions src/librustc_save_analysis/json_dumper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,55 @@ 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);
}

impl<'b, W: Write> Drop for JsonDumper<'b, W> {
fn drop(&mut self) {
if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
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");
}
}
}

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) {
self.output.dump(&self.result);
}
}

macro_rules! impl_fn {
($fn_name: ident, $data_type: ident, $bucket: ident) => {
fn $fn_name(&mut self, data: $data_type) {
Expand All @@ -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)
}
Expand Down
175 changes: 119 additions & 56 deletions src/librustc_save_analysis/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -866,55 +867,131 @@ 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) {
let _ignore = tcx.dep_graph.in_ignore();
/// 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);
}

assert!(analysis.glob_map.is_some());
/// Dump the save-analysis results to a file.
pub struct DumpHandler<'a> {
format: Format,
odir: Option<&'a Path>,
cratename: String
}

info!("Dumping crate {}", cratename);
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()
}
}

// 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"),
},
};
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) {
tcx.sess.err(&format!("Could not create directory {}: {}",
root_path.display(),
e));
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);

{
let disp = root_path.display();
info!("Writing output to {}", disp);
match self.format {
Format::Csv => dump!(CsvDumper::new(output)),
Format::Json => dump!(JsonDumper::new(output)),
Format::JsonApi => dump!(JsonApiDumper::new(output)),
}
}
}

// 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;
/// 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);

let save_ctxt = SaveContext {
tcx: tcx,
Expand All @@ -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.
Expand Down