diff --git a/src/Cargo.lock b/src/Cargo.lock index 9fae4b7e924c..ae31a580ad5c 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1049,8 +1049,7 @@ dependencies = [ [[package]] name = "rls-data" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.8.0" dependencies = [ "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1372,10 +1371,11 @@ name = "rustc_save_analysis" version = "0.0.0" dependencies = [ "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rls-data 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rls-data 0.8.0", "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_borrowck 0.0.0", "rustc_typeck 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", @@ -2084,7 +2084,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" "checksum rls-analysis 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d77d58e8933752142b5b92e3f8ba6d6f1630be6da5627c492268a43f79ffbda" "checksum rls-data 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "374a8fad31cc0681a7bfd8a04079dd4afd0e981d34e18a171b1a467445bdf51e" -"checksum rls-data 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e2087477364c34faca86c2476765deb1185dbae3c598cfb1eb040f3a74d22b5" "checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a" "checksum rls-vfs 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ace07060dd154731b39254864245cbdd33c8f5f64fe1f630a089c72e2468f854" "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index 7dcb6ce76a40..903eea78ff7f 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -37,11 +37,12 @@ mod move_error; pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, body: hir::BodyId) - -> (Vec>, move_data::MoveData<'tcx>) { + -> (Vec, Vec>, move_data::MoveData<'tcx>) { let def_id = bccx.tcx.hir.body_owner_def_id(body); let param_env = bccx.tcx.param_env(def_id); let mut glcx = GatherLoanCtxt { bccx: bccx, + safe_loans: Vec::new(), all_loans: Vec::new(), item_ub: region::CodeExtent::Misc(body.node_id), move_data: MoveData::new(), @@ -53,14 +54,15 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, .consume_body(body); glcx.report_potential_errors(); - let GatherLoanCtxt { all_loans, move_data, .. } = glcx; - (all_loans, move_data) + let GatherLoanCtxt { safe_loans, all_loans, move_data, .. } = glcx; + (safe_loans, all_loans, move_data) } struct GatherLoanCtxt<'a, 'tcx: 'a> { bccx: &'a BorrowckCtxt<'a, 'tcx>, move_data: move_data::MoveData<'tcx>, move_error_collector: move_error::MoveErrorCollector<'tcx>, + safe_loans: Vec, all_loans: Vec>, /// `item_ub` is used as an upper-bound on the lifetime whenever we /// ask for the scope of an expression categorized as an upvar. @@ -289,6 +291,33 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { } } + /// Given a region for a loan (Loan/SafeLoan), returns the corresponding loan_scope, + /// which is a CodeExtent that could represent the gen_scope and/or kill_scope + /// of a loan. + fn loan_scope_from_region(&self, loan_region: ty::Region<'tcx>) -> Option { + Some(match *loan_region { + ty::ReScope(scope) => scope, + + ty::ReEarlyBound(ref br) => { + self.bccx.region_maps.early_free_extent(self.tcx(), br) + } + + ty::ReFree(ref fr) => { + self.bccx.region_maps.free_extent(self.tcx(), fr) + } + + ty::ReStatic => self.item_ub, + + ty::ReEmpty | + ty::ReLateBound(..) | + ty::ReVar(..) | + ty::ReSkolemized(..) | + ty::ReErased => { + return None; + } + }) + } + /// Guarantees that `addr_of(cmt)` will be valid for the duration of `static_scope_r`, or /// reports an error. This may entail taking out loans, which will be added to the /// `req_loan_map`. @@ -343,34 +372,40 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { // Create the loan record (if needed). let loan = match restr { RestrictionResult::Safe => { - // No restrictions---no loan record necessary - return; - } - - RestrictionResult::SafeIf(loan_path, restricted_paths) => { - let loan_scope = match *loan_region { - ty::ReScope(scope) => scope, + let loan_scope = match self.loan_scope_from_region(loan_region) { + Some(loan_scope) => loan_scope, + None => { + span_bug!( + cmt.span, + "invalid borrow lifetime: {:?}", + loan_region); + }, + }; + debug!("loan_scope = {:?}", loan_scope); - ty::ReEarlyBound(ref br) => { - self.bccx.region_maps.early_free_extent(self.tcx(), br) - } + let borrow_scope = region::CodeExtent::Misc(borrow_id); + let loan_scope = self.compute_gen_scope(borrow_scope, loan_scope); - ty::ReFree(ref fr) => { - self.bccx.region_maps.free_extent(self.tcx(), fr) - } + let safe_loan = SafeLoan { + kind: req_kind, + loan_scope: loan_scope, + span: borrow_span, + }; + self.safe_loans.push(safe_loan); - ty::ReStatic => self.item_ub, + // No restrictions---no normal loan record necessary + return; + } - ty::ReEmpty | - ty::ReLateBound(..) | - ty::ReVar(..) | - ty::ReSkolemized(..) | - ty::ReErased => { + RestrictionResult::SafeIf(loan_path, restricted_paths) => { + let loan_scope = match self.loan_scope_from_region(loan_region) { + Some(loan_scope) => loan_scope, + None => { span_bug!( cmt.span, "invalid borrow lifetime: {:?}", loan_region); - } + }, }; debug!("loan_scope = {:?}", loan_scope); diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index c72bdd040111..3150f6e8e2db 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -22,6 +22,7 @@ pub use self::mir::elaborate_drops::ElaborateDrops; use self::InteriorKind::*; +use rustc::hir::BodyId; use rustc::hir::map as hir_map; use rustc::hir::map::blocks::FnLikeNode; use rustc::cfg; @@ -39,15 +40,18 @@ use rustc::middle::free_region::RegionRelations; use rustc::ty::{self, TyCtxt}; use rustc::ty::maps::Providers; +use std::collections::HashMap; + use std::fmt; use std::rc::Rc; use std::hash::{Hash, Hasher}; -use syntax::ast; -use syntax_pos::{MultiSpan, Span}; +use syntax::{ast, codemap}; +use syntax_pos::{MultiSpan, Span, NO_EXPANSION}; use errors::DiagnosticBuilder; use rustc::hir; use rustc::hir::intravisit::{self, Visitor}; +use borrowck::move_data::MoveData; pub mod check_loans; @@ -62,27 +66,51 @@ pub struct LoanDataFlowOperator; pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>; -pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - for body_owner_def_id in tcx.body_owners() { - tcx.borrowck(body_owner_def_id); +pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, save_analysis: bool) -> Option>> { + // FIXME: nashenas88 support this through TyCtxt + if save_analysis { + let mut map = HashMap::new(); + for body_owner_def_id in tcx.body_owners() { + let result = borrowck(tcx, body_owner_def_id, save_analysis).unwrap(); + map.insert(body_owner_def_id, result); + } + Some(map) + } else { + for body_owner_def_id in tcx.body_owners() { + tcx.borrowck(body_owner_def_id); + } + None } } pub fn provide(providers: &mut Providers) { *providers = Providers { - borrowck, + borrowck: borrowck_provider, ..*providers }; } /// Collection of conclusions determined via borrow checker analyses. pub struct AnalysisData<'a, 'tcx: 'a> { + pub safe_loans: Vec, pub all_loans: Vec>, pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>, pub move_data: move_data::FlowedMoveData<'a, 'tcx>, } -fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { +pub struct AnalysisResult<'a, 'tcx: 'a> { + pub bccx: BorrowckCtxt<'a, 'tcx>, + pub safe_loans: Vec, + pub all_loans: Vec>, + pub move_data: move_data::MoveData<'tcx>, + pub span: Option, +} + +fn borrowck_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { + borrowck(tcx, owner_def_id, false); +} + +fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId, save_analysis: bool) -> Option> { debug!("borrowck(body_owner_def_id={:?})", owner_def_id); let owner_id = tcx.hir.as_local_node_id(owner_def_id).unwrap(); @@ -94,7 +122,7 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { // those things (notably the synthesized constructors from // tuple structs/variants) do not have an associated body // and do not need borrowchecking. - return; + return None; } _ => { } } @@ -103,12 +131,12 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { let attributes = tcx.get_attrs(owner_def_id); let tables = tcx.typeck_tables_of(owner_def_id); let region_maps = tcx.region_maps(owner_def_id); - let mut bccx = &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id }; + let mut bccx = BorrowckCtxt { tcx, tables, region_maps, owner_def_id }; let body = bccx.tcx.hir.body(body_id); if bccx.tcx.has_attr(owner_def_id, "rustc_mir_borrowck") { - mir::borrowck_mir(bccx, owner_id, &attributes); + mir::borrowck_mir(&mut bccx, owner_id, &attributes); } else { // Eventually, borrowck will always read the MIR, but at the // moment we do not. So, for now, we always force MIR to be @@ -122,17 +150,30 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) { } let cfg = cfg::CFG::new(bccx.tcx, &body); - let AnalysisData { all_loans, + let AnalysisData { safe_loans, + all_loans, loans: loan_dfcx, move_data: flowed_moves } = - build_borrowck_dataflow_data(bccx, &cfg, body_id); - - check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans, body); + build_borrowck_dataflow_data(&mut bccx, &cfg, body_id); + + check_loans::check_loans(&bccx, &loan_dfcx, &flowed_moves, &all_loans[..], body); + + if save_analysis { + Some(AnalysisResult { + bccx, + safe_loans, + all_loans, + move_data: flowed_moves.move_data, + span: tcx.hir.span_if_local(owner_def_id) + }) + } else { + None + } } fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, cfg: &cfg::CFG, - body_id: hir::BodyId) + body_id: BodyId) -> AnalysisData<'a, 'tcx> { // Check the body of fn items. @@ -143,7 +184,7 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, visitor.visit_body(body); visitor.result() }; - let (all_loans, move_data) = + let (safe_loans, all_loans, move_data) = gather_loans::gather_loans_in_fn(this, body_id); let mut loan_dfcx = @@ -168,16 +209,17 @@ fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>, id_range, body); - AnalysisData { all_loans: all_loans, + AnalysisData { safe_loans: safe_loans, + all_loans: all_loans, loans: loan_dfcx, - move_data:flowed_moves } + move_data: flowed_moves } } /// Accessor for introspective clients inspecting `AnalysisData` and /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer. pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - body_id: hir::BodyId, + body_id: BodyId, cfg: &cfg::CFG) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) { @@ -209,6 +251,26 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> { /////////////////////////////////////////////////////////////////////////// // Loans and loan paths +pub struct SafeLoan { + kind: ty::BorrowKind, + loan_scope: region::CodeExtent, + span: Span, +} + +impl SafeLoan { + pub fn kind(&self) -> ty::BorrowKind { + self.kind + } + + pub fn loan_scope(&self) -> region::CodeExtent { + self.loan_scope + } + + pub fn span(&self) -> Span { + self.span + } +} + /// Record of a loan that was issued. pub struct Loan<'tcx> { index: usize, @@ -236,6 +298,47 @@ impl<'tcx> Loan<'tcx> { pub fn loan_path(&self) -> Rc> { self.loan_path.clone() } + + pub fn kind(&self) -> ty::BorrowKind { + self.kind + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn span_in_def(&self, def_id: DefId, tcx: TyCtxt) -> Option { + let fn_span = tcx.hir + .as_local_node_id(def_id) + .map(|node_id| tcx.hir.span(node_id)); + + let gen_span = self.gen_scope.span(&tcx.hir) + .and_then(|s| fn_span.and_then(|n| Self::get_unexpanded_span(s, n))); + + let kill_span = self.kill_scope.span(&tcx.hir) + .and_then(|s| fn_span.and_then(|n| Self::get_unexpanded_span(s, n))); + + match (gen_span, kill_span) { + (Some(gen_span), Some(kill_span)) => { + Some(Span { + lo: gen_span.lo, + hi: kill_span.hi, + ctxt: NO_EXPANSION + }) + }, + _ => None, + } + } + + fn get_unexpanded_span(input_span: Span, wrapping_span: Span) -> Option { + // Walk up the macro expansion chain until we reach a non-expanded span. + let span = codemap::original_sp(input_span, wrapping_span); + if span == wrapping_span { + None // We traversed to the top and couldn't find any matching span + } else { + Some(span) + } + } } #[derive(Eq)] @@ -269,6 +372,15 @@ impl<'tcx> LoanPath<'tcx> { LoanPath { kind: kind, ty: ty } } + pub fn ref_node_id<'a>(&self) -> ast::NodeId { + match self.kind { + LpVar(node_id) => node_id, + LpUpvar(upvar_id) => upvar_id.var_id, + LpDowncast(ref base, ..) | + LpExtend(ref base, ..) => base.ref_node_id(), + } + } + fn to_type(&self) -> ty::Ty<'tcx> { self.ty } } diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 617326808970..a143d2e78df3 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -43,7 +43,8 @@ extern crate core; // for NonZero pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; -pub use borrowck::{AnalysisData, BorrowckCtxt, ElaborateDrops}; +pub use borrowck::{AnalysisData, AnalysisResult, BorrowckCtxt, ElaborateDrops, Loan, SafeLoan}; +pub use borrowck::move_data::{MoveData, Move, Assignment}; // NB: This module needs to be declared first so diagnostics are // registered before they are used. diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 792be8bc6dfa..ecc7a1bc309c 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -10,6 +10,7 @@ use rustc::hir::{self, map as hir_map}; use rustc::hir::lowering::lower_crate; +use rustc::hir::def_id::DefId; use rustc::ich::Fingerprint; use rustc_data_structures::stable_hasher::StableHasher; use rustc_mir as mir; @@ -27,6 +28,7 @@ use rustc::util::common::time; use rustc::util::nodemap::NodeSet; use rustc::util::fs::rename_or_copy_remove; use rustc_borrowck as borrowck; +use rustc_borrowck::AnalysisResult; use rustc_incremental::{self, IncrementalHashesMap}; use rustc_resolve::{MakeGlobMap, Resolver}; use rustc_metadata::creader::CrateLoader; @@ -43,6 +45,7 @@ use super::Compilation; use serialize::json; +use std::collections::HashMap; use std::env; use std::ffi::{OsString, OsStr}; use std::fs; @@ -177,7 +180,7 @@ pub fn compile_input(sess: &Session, &arena, &arenas, &crate_name, - |tcx, analysis, incremental_hashes_map, result| { + |tcx, analysis, incremental_hashes_map, borrow_analysis_map, result| { { // Eventually, we will want to track plugins. let _ignore = tcx.dep_graph.in_ignore(); @@ -189,6 +192,7 @@ pub fn compile_input(sess: &Session, opt_crate, tcx.hir.krate(), &analysis, + borrow_analysis_map, tcx, &crate_name); (control.after_analysis.callback)(&mut state); @@ -354,6 +358,7 @@ pub struct CompileState<'a, 'tcx: 'a> { pub hir_map: Option<&'a hir_map::Map<'tcx>>, pub resolutions: Option<&'a Resolutions>, pub analysis: Option<&'a ty::CrateAnalysis>, + pub borrow_analysis_map: Option>>, pub tcx: Option>, pub trans: Option<&'a trans::CrateTranslation>, } @@ -380,6 +385,7 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { hir_map: None, resolutions: None, analysis: None, + borrow_analysis_map: None, tcx: None, trans: None, } @@ -455,11 +461,13 @@ impl<'a, 'tcx> CompileState<'a, 'tcx> { krate: Option<&'a ast::Crate>, hir_crate: &'a hir::Crate, analysis: &'a ty::CrateAnalysis, + borrow_analysis_map: Option>>, tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_name: &'a str) -> Self { CompileState { analysis: Some(analysis), + borrow_analysis_map: borrow_analysis_map, tcx: Some(tcx), expanded_crate: krate, hir_crate: Some(hir_crate), @@ -843,14 +851,15 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, where F: for<'a> FnOnce(TyCtxt<'a, 'tcx, 'tcx>, ty::CrateAnalysis, IncrementalHashesMap, + Option>>, CompileResult) -> R { macro_rules! try_with_f { - ($e: expr, ($t: expr, $a: expr, $h: expr)) => { + ($e: expr, ($t: expr, $a: expr, $h: expr, $b: expr)) => { match $e { Ok(x) => x, Err(x) => { - f($t, $a, $h, Err(x)); + f($t, $a, $h, $b, Err(x)); return Err(x); } } @@ -975,7 +984,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || stability::check_unstable_api_usage(tcx)); // passes are timed inside typeck - try_with_f!(typeck::check_crate(tcx), (tcx, analysis, incremental_hashes_map)); + try_with_f!(typeck::check_crate(tcx), (tcx, analysis, incremental_hashes_map, None)); time(time_passes, "const checking", @@ -1004,9 +1013,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "liveness checking", || middle::liveness::check_crate(tcx)); - time(time_passes, + let borrow_analysis_map = time(time_passes, "borrow checking", - || borrowck::check_crate(tcx)); + || borrowck::check_crate(tcx, ::save_analysis(sess))); // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids @@ -1015,7 +1024,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis if sess.err_count() > 0 { - return Ok(f(tcx, analysis, incremental_hashes_map, Err(sess.err_count()))); + return Ok(f(tcx, analysis, incremental_hashes_map, borrow_analysis_map, Err(sess.err_count()))); } analysis.reachable = @@ -1033,10 +1042,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // The above three passes generate errors w/o aborting if sess.err_count() > 0 { - return Ok(f(tcx, analysis, incremental_hashes_map, Err(sess.err_count()))); + return Ok(f(tcx, analysis, incremental_hashes_map, borrow_analysis_map, Err(sess.err_count()))); } - Ok(f(tcx, analysis, incremental_hashes_map, Ok(()))) + Ok(f(tcx, analysis, incremental_hashes_map, borrow_analysis_map, Ok(()))) }) } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6839274800ce..44c8ad9ac903 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -505,9 +505,11 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { if save_analysis(sess) { control.after_analysis.callback = box |state| { time(state.session.time_passes(), "save analysis", || { + let borrow_analysis = state.borrow_analysis_map.take().unwrap(); save::process_crate(state.tcx.unwrap(), state.expanded_crate.unwrap(), state.analysis.unwrap(), + borrow_analysis, state.crate_name.unwrap(), DumpHandler::new(save_analysis_format(state.session), state.out_dir, diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index d40a2ab0b530..d4c172585744 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -233,7 +233,7 @@ impl PpSourceMode { arena, arenas, id, - |tcx, _, _, _| { + |tcx, _, _, _, _| { let empty_tables = ty::TypeckTables::empty(); let annotation = TypedAnnotation { tcx: tcx, @@ -992,7 +992,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, arena, arenas, crate_name, - |tcx, _, _, _| { + |tcx, _, _, _, _| { match ppm { PpmMir | PpmMirCFG => { if let Some(nodeid) = nodeid { diff --git a/src/librustc_save_analysis/Cargo.toml b/src/librustc_save_analysis/Cargo.toml index 53a82cf73e95..1bc115b7a655 100644 --- a/src/librustc_save_analysis/Cargo.toml +++ b/src/librustc_save_analysis/Cargo.toml @@ -11,10 +11,11 @@ crate-type = ["dylib"] [dependencies] log = "0.3" rustc = { path = "../librustc" } +rustc_borrowck = { path = "../librustc_borrowck" } rustc_typeck = { path = "../librustc_typeck" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } -rls-data = "0.6" +rls-data = { path = "../../../rls-data", default-features = true, features = ["borrows"] } rls-span = "0.4" # FIXME(#40527) should move rustc serialize out of tree rustc-serialize = "0.3" diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index cc33d3db8eba..bcc4850b6d38 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -30,7 +30,12 @@ use rustc::hir::map::Node; use rustc::session::Session; use rustc::ty::{self, TyCtxt}; +use rustc_borrowck::{self, AnalysisResult, Loan, SafeLoan, Move, Assignment, BorrowckCtxt}; + +use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::path::Path; +use std::mem; use syntax::ast::{self, NodeId, PatKind, Attribute, CRATE_NODE_ID}; use syntax::parse::token; @@ -45,8 +50,9 @@ use {escape, generated_code, SaveContext, PathCollector, docs_for_attrs, lower_a use span_utils::SpanUtils; use sig; -use rls_data::{CratePreludeData, Import, ImportKind, SpanData, Ref, RefKind, - Def, DefKind, Relation, RelationKind}; +use rls_data::{Id, CratePreludeData, Import, ImportKind, SpanData, Ref, RefKind, + Def, DefKind, Relation, RelationKind, BorrowData, Move as MoveData, + Loan as LoanData, Scope}; macro_rules! down_cast_data { ($id:ident, $kind:ident, $sp:expr) => { @@ -139,6 +145,137 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { self.dumper.crate_prelude(data); } + pub fn dump_borrow_analysis(&mut self) { + let borrow_analysis = self.save_ctxt.borrow_analysis_map.drain().collect::>(); + for (def_id, analysis_result) in borrow_analysis { + self.process_borrow_analysis(def_id, analysis_result); + } + } + + fn process_borrow_analysis(&mut self, def_id: DefId, mut analysis_result: AnalysisResult<'l, 'tcx>) { + let loans = mem::replace(&mut analysis_result.all_loans, vec![]) + .into_iter() + .filter_map(|loan| self.process_loan(def_id, loan)) + // Chain safe loans + .chain(mem::replace(&mut analysis_result.safe_loans, vec![]) + .into_iter() + .filter_map(|safe_loan| self.process_safe_loan(safe_loan))) + .collect::>(); + + let moves = mem::replace(&mut *analysis_result.move_data.moves.borrow_mut(), vec![]) + .into_iter() + .map(|m| self.process_move(m, &analysis_result.move_data)) + .collect::>(); + + let scopes = { + let move_map: HashMap = moves.iter() + .fold(HashMap::new(), |mut acc, m| { + match acc.entry(m.ref_id) { + // We want the earliest occurring move + Entry::Occupied(mut e) => if e.get().span.byte_start > m.span.byte_start { e.insert(m); }, + Entry::Vacant(e) => { e.insert(m); }, + } + + acc + }); + mem::replace(&mut *analysis_result.move_data.var_assignments.borrow_mut(), vec![]) + .into_iter() + .filter_map(|a| { + let id = ::id_from_node_id(a.assignee_id, &self.save_ctxt); + let mov = move_map.get(&id); + self.process_assignment(a, mov, &analysis_result.move_data, &analysis_result.bccx) + }) + .collect() + }; + + let span = analysis_result.span.map(|s| self.span_from_span(s)); + + let data = BorrowData { + ref_id: ::id_from_def_id(def_id), + scopes: scopes, + loans: loans, + moves: moves, + span: span, + }; + + self.dumper.dump_per_fn_borrow_data(data); + } + + fn process_assignment( + &self, + assignment: Assignment, + mov: Option<&&MoveData>, + move_data: &rustc_borrowck::MoveData<'tcx>, + bccx: &BorrowckCtxt<'l, 'tcx>) + -> Option { + move_data.path_loan_path(assignment.path) + .kill_scope(&bccx) + .span(&self.tcx.hir) + .map(|span| { + let expanded_span = match mov.map(|m| &m.span) { + Some(move_span) => if move_span.byte_start < span.hi.0 { + Span { + lo: span.lo, + hi: BytePos(move_span.byte_end), + ctxt: NO_EXPANSION, + } + } else { + span + }, + None => span, + }; + + Scope { + ref_id: ::id_from_node_id(assignment.assignee_id, &self.save_ctxt), + span: self.span_from_span(expanded_span) + } + }) + .or_else(|| { + debug!("No span found for assignment `{:?}-{:?}`", assignment.id, assignment.assignee_id); + None + }) + } + + fn process_safe_loan(&self, safe_loan: SafeLoan) -> Option { + let node_id = safe_loan.loan_scope().node_id(); + + // may be span inside of macro + safe_loan.loan_scope().span(&self.tcx.hir) + .map(|span| LoanData { + ref_id: ::id_from_node_id(node_id, &self.save_ctxt), + kind: ::borrow_kind_from_borrow_kind(safe_loan.kind()), + span: self.span_from_span(span), + }) + .or_else(|| { + debug!("No span found for safe loan `{:?}`", safe_loan.loan_scope()); + None + }) + } + + fn process_loan(&self, def_id: DefId, loan: Loan) -> Option { + // These may be inside of macros + loan.span_in_def(def_id, self.tcx) + .map(|span| LoanData { + ref_id: ::id_from_node_id(loan.loan_path().ref_node_id(), &self.save_ctxt), + kind: ::borrow_kind_from_borrow_kind(loan.kind()), + span: self.span_from_span(span), + }) + .or_else(|| { + debug!("No span found for loan `{:?}`", loan.loan_path()); + None + }) + } + + fn process_move(&self, mov: Move, move_data: &rustc_borrowck::MoveData) -> MoveData { + let id = ::id_from_node_id( + move_data.path_loan_path(mov.path).ref_node_id(), + &self.save_ctxt); + MoveData { + ref_id: id, + span: self.span_from_span(self.tcx.hir.span(mov.id)), + } + } + // Return all non-empty prefixes of a path. // For each prefix, we return the span for the last segment in the prefix and // a str representation of the entire prefix. diff --git a/src/librustc_save_analysis/json_api_dumper.rs b/src/librustc_save_analysis/json_api_dumper.rs index 4b2301fd7f80..6e408607332f 100644 --- a/src/librustc_save_analysis/json_api_dumper.rs +++ b/src/librustc_save_analysis/json_api_dumper.rs @@ -14,7 +14,7 @@ use rustc_serialize::json::as_json; use Dump; -use rls_data::{Analysis, Import, Def, CratePreludeData, Format, Relation}; +use rls_data::{Analysis, Import, Def, CratePreludeData, Format, Relation, BorrowData}; // A dumper to dump a restricted set of JSON information, designed for use with @@ -63,4 +63,7 @@ impl<'b, W: Write + 'b> Dump for JsonApiDumper<'b, W> { self.result.defs.push(data); } } + fn dump_per_fn_borrow_data(&mut self, data: BorrowData) { + self.result.per_fn_borrows.push(data); + } } diff --git a/src/librustc_save_analysis/json_dumper.rs b/src/librustc_save_analysis/json_dumper.rs index 9cd375e98558..f719cb47f7e8 100644 --- a/src/librustc_save_analysis/json_dumper.rs +++ b/src/librustc_save_analysis/json_dumper.rs @@ -13,7 +13,7 @@ use std::io::Write; use rustc_serialize::json::as_json; use rls_data::{self, Analysis, Import, Def, DefKind, Ref, RefKind, MacroRef, - Relation, CratePreludeData}; + Relation, CratePreludeData, BorrowData}; use rls_span::{Column, Row}; use Dump; @@ -110,4 +110,8 @@ impl<'b, O: DumpOutput + 'b> Dump for JsonDumper { fn dump_relation(&mut self, data: Relation) { self.result.relations.push(data); } + + fn dump_per_fn_borrow_data(&mut self, data: BorrowData) { + self.result.per_fn_borrows.push(data); + } } diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 4e9e72fe2496..0e155df3751f 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -24,6 +24,7 @@ #![cfg_attr(stage0, feature(staged_api))] #[macro_use] extern crate rustc; +extern crate rustc_borrowck; #[macro_use] extern crate log; #[macro_use] extern crate syntax; @@ -49,8 +50,10 @@ use rustc::hir::def_id::DefId; use rustc::session::config::CrateType::CrateTypeExecutable; use rustc::session::Session; use rustc::ty::{self, TyCtxt}; +use rustc_borrowck::AnalysisResult; use rustc_typeck::hir_ty_to_ty; +use std::collections::HashMap; use std::env; use std::fs::File; use std::path::{Path, PathBuf}; @@ -71,13 +74,14 @@ use dump_visitor::DumpVisitor; use span_utils::SpanUtils; use rls_data::{Ref, RefKind, SpanData, MacroRef, Def, DefKind, Relation, RelationKind, - ExternalCrateData, Import, CratePreludeData}; + ExternalCrateData, Import, CratePreludeData, BorrowData, BorrowKind}; pub struct SaveContext<'l, 'tcx: 'l> { tcx: TyCtxt<'l, 'tcx, 'tcx>, tables: &'l ty::TypeckTables<'tcx>, analysis: &'l ty::CrateAnalysis, + borrow_analysis_map: HashMap>, span_utils: SpanUtils<'tcx>, } @@ -97,6 +101,7 @@ pub trait Dump { fn dump_ref(&mut self, _: Ref) {} fn dump_def(&mut self, _: bool, _: Def); fn dump_relation(&mut self, data: Relation); + fn dump_per_fn_borrow_data(&mut self, data: BorrowData); } macro_rules! option_try( @@ -958,6 +963,7 @@ impl<'a> SaveHandler for DumpHandler<'a> { let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); visitor.dump_crate_info(cratename, krate); + visitor.dump_borrow_analysis(); visit::walk_crate(&mut visitor, krate); }} } @@ -987,6 +993,7 @@ impl<'b> SaveHandler for CallbackHandler<'b> { let mut visitor = DumpVisitor::new(save_ctxt, &mut dumper); visitor.dump_crate_info(cratename, krate); + visitor.dump_borrow_analysis(); visit::walk_crate(&mut visitor, krate); }} } @@ -1003,6 +1010,7 @@ impl<'b> SaveHandler for CallbackHandler<'b> { pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>, krate: &ast::Crate, analysis: &'l ty::CrateAnalysis, + borrow_analysis_map: HashMap>, cratename: &str, mut handler: H) { let _ignore = tcx.dep_graph.in_ignore(); @@ -1015,6 +1023,7 @@ pub fn process_crate<'l, 'tcx, H: SaveHandler>(tcx: TyCtxt<'l, 'tcx, 'tcx>, tcx: tcx, tables: &ty::TypeckTables::empty(), analysis: analysis, + borrow_analysis_map: borrow_analysis_map, span_utils: SpanUtils::new(&tcx.sess), }; @@ -1048,6 +1057,14 @@ fn id_from_node_id(id: NodeId, scx: &SaveContext) -> rls_data::Id { def_id.map(|id| id_from_def_id(id)).unwrap_or_else(null_id) } +fn borrow_kind_from_borrow_kind(kind: ty::BorrowKind) -> BorrowKind { + match kind { + ty::BorrowKind::ImmBorrow | + ty::BorrowKind::UniqueImmBorrow => BorrowKind::ImmBorrow, + ty::BorrowKind::MutBorrow => BorrowKind::MutBorrow, + } +} + fn null_id() -> rls_data::Id { rls_data::Id { krate: u32::max_value(), diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 9a689ed079ee..c0821e7b1453 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -177,7 +177,7 @@ pub fn run_core(search_paths: SearchPaths, &arena, &arenas, &name, - |tcx, analysis, _, result| { + |tcx, analysis, _, _, result| { if let Err(_) = result { sess.fatal("Compilation failed, aborting rustdoc"); }