1406: reuse AnalysisHost in batch analysis r=matklad a=matklad

We do some custom setup in `AnalysisHost`, like setting up LRU size. I figure it's a good idea to not duplicate this work in batch analysis, *if* we want to keep batch and non-batch close.

Long-term, I see a value in keeping batch a separate, lighter weight thing. However, because now we use batch to measure performance, keeping them closer makes more sense.

I'd also like to add ability to get completions by using batch analysis, and that will require ra_ide_api as well.

@flodiebold were there some reason why we haven't started with this approach from the start?

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-06-16 08:08:10 +00:00
commit e6fbff3246
6 changed files with 89 additions and 108 deletions

1
Cargo.lock generated
View file

@ -1023,6 +1023,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ra_db 0.1.0",
"ra_hir 0.1.0",
"ra_ide_api 0.1.0",
"ra_project_model 0.1.0",
"ra_syntax 0.1.0",
"ra_vfs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -11,6 +11,7 @@ rustc-hash = "1.0"
ra_vfs = "0.2.0"
ra_syntax = { path = "../ra_syntax" }
ra_db = { path = "../ra_db" }
ra_ide_api = { path = "../ra_ide_api" }
ra_hir = { path = "../ra_hir" }
ra_project_model = { path = "../ra_project_model" }

View file

@ -1,36 +1,19 @@
mod vfs_filter;
use std::{sync::Arc, path::Path, collections::HashSet, error::Error};
use std::{path::Path, collections::HashSet, error::Error};
use rustc_hash::FxHashMap;
use ra_db::{
CrateGraph, FileId, SourceRoot, SourceRootId, SourceDatabase, salsa::{self, Database},
CrateGraph, FileId, SourceRootId,
};
use ra_hir::db;
use ra_ide_api::{AnalysisHost, AnalysisChange};
use ra_project_model::ProjectWorkspace;
use ra_vfs::{Vfs, VfsChange};
use vfs_filter::IncludeRustFiles;
type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
#[salsa::database(
ra_db::SourceDatabaseStorage,
db::AstDatabaseStorage,
db::DefDatabaseStorage,
db::HirDatabaseStorage
)]
#[derive(Debug)]
pub struct BatchDatabase {
runtime: salsa::Runtime<BatchDatabase>,
}
impl salsa::Database for BatchDatabase {
fn salsa_runtime(&self) -> &salsa::Runtime<BatchDatabase> {
&self.runtime
}
}
fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
FileId(f.0)
}
@ -38,86 +21,79 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
SourceRootId(r.0)
}
impl BatchDatabase {
pub fn load(crate_graph: CrateGraph, vfs: &mut Vfs) -> BatchDatabase {
let mut db = BatchDatabase { runtime: salsa::Runtime::default() };
let lru_cap = std::env::var("RA_LRU_CAP")
.ok()
.and_then(|it| it.parse::<usize>().ok())
.unwrap_or(ra_db::DEFAULT_LRU_CAP);
db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_cap);
db.query_mut(ra_hir::db::ParseMacroQuery).set_lru_capacity(lru_cap);
db.set_crate_graph(Arc::new(crate_graph));
pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> {
let root = std::env::current_dir()?.join(root);
let ws = ProjectWorkspace::discover(root.as_ref())?;
let mut roots = Vec::new();
roots.push(IncludeRustFiles::member(root.clone()));
roots.extend(IncludeRustFiles::from_roots(ws.to_roots()));
let (mut vfs, roots) = Vfs::new(roots);
let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
let vfs_file = vfs.load(path);
log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
vfs_file.map(vfs_file_to_id)
});
log::debug!("crate graph: {:?}", crate_graph);
// wait until Vfs has loaded all roots
let receiver = vfs.task_receiver().clone();
let mut roots_loaded = HashSet::new();
for task in receiver {
vfs.handle_task(task);
let mut done = false;
for change in vfs.commit_changes() {
match change {
VfsChange::AddRoot { root, files } => {
let source_root_id = vfs_root_to_id(root);
log::debug!(
"loaded source root {:?} with path {:?}",
source_root_id,
vfs.root2path(root)
);
let mut file_map = FxHashMap::default();
for (vfs_file, path, text) in files {
let file_id = vfs_file_to_id(vfs_file);
db.set_file_text(file_id, text);
db.set_file_relative_path(file_id, path.clone());
db.set_file_source_root(file_id, source_root_id);
file_map.insert(path, file_id);
}
let source_root = SourceRoot { files: file_map };
db.set_source_root(source_root_id, Arc::new(source_root));
roots_loaded.insert(source_root_id);
if roots_loaded.len() == vfs.n_roots() {
done = true;
}
let local_roots = roots
.into_iter()
.filter(|r| vfs.root2path(*r).starts_with(&root))
.map(vfs_root_to_id)
.collect();
let host = load(root.as_path(), crate_graph, &mut vfs);
Ok((host, local_roots))
}
pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> AnalysisHost {
let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
let mut host = AnalysisHost::new(lru_cap);
let mut analysis_change = AnalysisChange::new();
analysis_change.set_crate_graph(crate_graph);
// wait until Vfs has loaded all roots
let receiver = vfs.task_receiver().clone();
let mut roots_loaded = HashSet::new();
for task in receiver {
vfs.handle_task(task);
let mut done = false;
for change in vfs.commit_changes() {
match change {
VfsChange::AddRoot { root, files } => {
let is_local = vfs.root2path(root).starts_with(&project_root);
let source_root_id = vfs_root_to_id(root);
log::debug!(
"loaded source root {:?} with path {:?}",
source_root_id,
vfs.root2path(root)
);
analysis_change.add_root(source_root_id, is_local);
let mut file_map = FxHashMap::default();
for (vfs_file, path, text) in files {
let file_id = vfs_file_to_id(vfs_file);
analysis_change.add_file(source_root_id, file_id, path.clone(), text);
file_map.insert(path, file_id);
}
VfsChange::AddFile { .. }
| VfsChange::RemoveFile { .. }
| VfsChange::ChangeFile { .. } => {
// We just need the first scan, so just ignore these
roots_loaded.insert(source_root_id);
if roots_loaded.len() == vfs.n_roots() {
done = true;
}
}
}
if done {
break;
VfsChange::AddFile { .. }
| VfsChange::RemoveFile { .. }
| VfsChange::ChangeFile { .. } => {
// We just need the first scan, so just ignore these
}
}
}
db
if done {
break;
}
}
pub fn load_cargo(root: impl AsRef<Path>) -> Result<(BatchDatabase, Vec<SourceRootId>)> {
let root = std::env::current_dir()?.join(root);
let ws = ProjectWorkspace::discover(root.as_ref())?;
let mut roots = Vec::new();
roots.push(IncludeRustFiles::member(root.clone()));
roots.extend(IncludeRustFiles::from_roots(ws.to_roots()));
let (mut vfs, roots) = Vfs::new(roots);
let mut load = |path: &Path| {
let vfs_file = vfs.load(path);
log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
vfs_file.map(vfs_file_to_id)
};
let crate_graph = ws.to_crate_graph(&mut load);
log::debug!("crate graph: {:?}", crate_graph);
let local_roots = roots
.into_iter()
.filter(|r| vfs.root2path(*r).starts_with(&root))
.map(vfs_root_to_id)
.collect();
let db = BatchDatabase::load(crate_graph, &mut vfs);
Ok((db, local_roots))
}
host.apply_change(analysis_change);
host
}
#[cfg(test)]
@ -128,10 +104,10 @@ mod tests {
#[test]
fn test_loading_rust_analyzer() {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
let (db, roots) = BatchDatabase::load_cargo(path).unwrap();
let (host, roots) = load_cargo(path).unwrap();
let mut n_crates = 0;
for root in roots {
for _krate in Crate::source_root_crates(&db, root) {
for _krate in Crate::source_root_crates(host.raw_database(), root) {
n_crates += 1;
}
}

View file

@ -1,7 +1,6 @@
use std::{collections::HashSet, time::Instant, fmt::Write};
use ra_db::SourceDatabase;
use ra_batch::BatchDatabase;
use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource};
use ra_syntax::AstNode;
@ -9,16 +8,17 @@ use crate::Result;
pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
let db_load_time = Instant::now();
let (db, roots) = BatchDatabase::load_cargo(path)?;
let (host, roots) = ra_batch::load_cargo(path.as_ref())?;
let db = host.raw_database();
println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
let analysis_time = Instant::now();
let mut num_crates = 0;
let mut visited_modules = HashSet::new();
let mut visit_queue = Vec::new();
for root in roots {
for krate in Crate::source_root_crates(&db, root) {
for krate in Crate::source_root_crates(db, root) {
num_crates += 1;
let module = krate.root_module(&db).expect("crate in source root without root module");
let module = krate.root_module(db).expect("crate in source root without root module");
visit_queue.push(module);
}
}
@ -27,17 +27,17 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
let mut funcs = Vec::new();
while let Some(module) = visit_queue.pop() {
if visited_modules.insert(module) {
visit_queue.extend(module.children(&db));
visit_queue.extend(module.children(db));
for decl in module.declarations(&db) {
for decl in module.declarations(db) {
num_decls += 1;
if let ModuleDef::Function(f) = decl {
funcs.push(f);
}
}
for impl_block in module.impl_blocks(&db) {
for item in impl_block.items(&db) {
for impl_block in module.impl_blocks(db) {
for item in impl_block.items(db) {
num_decls += 1;
if let ImplItem::Method(f) = item {
funcs.push(f);
@ -61,11 +61,11 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
let mut num_exprs_unknown = 0;
let mut num_exprs_partially_unknown = 0;
for f in funcs {
let name = f.name(&db);
let name = f.name(db);
let mut msg = format!("processing: {}", name);
if verbose {
let src = f.source(&db);
let original_file = src.file_id.original_file(&db);
let src = f.source(db);
let original_file = src.file_id.original_file(db);
let path = db.file_relative_path(original_file);
let syntax_range = src.ast.syntax().range();
write!(msg, " ({:?} {})", path, syntax_range).unwrap();
@ -76,8 +76,8 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
continue;
}
}
let body = f.body(&db);
let inference_result = f.infer(&db);
let body = f.body(db);
let inference_result = f.infer(db);
for (expr_id, _) in body.exprs() {
let ty = &inference_result[expr_id];
num_exprs += 1;

View file

@ -276,6 +276,9 @@ impl AnalysisHost {
pub fn collect_garbage(&mut self) {
self.db.collect_garbage();
}
pub fn raw_database(&self) -> &impl hir::db::HirDatabase {
&self.db
}
}
/// Analysis is a snapshot of a world state at a moment in time. It is the main

View file

@ -17,7 +17,7 @@ fn main() -> Result<()> {
Err(_) => ra_prof::Filter::disabled(),
});
log::info!("lifecycle: server started");
match ::std::panic::catch_unwind(main_inner) {
match std::panic::catch_unwind(main_inner) {
Ok(res) => {
log::info!("lifecycle: terminating process with {:?}", res);
res