mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Merge #1406
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:
commit
e6fbff3246
6 changed files with 89 additions and 108 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1023,6 +1023,7 @@ dependencies = [
|
||||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_db 0.1.0",
|
"ra_db 0.1.0",
|
||||||
"ra_hir 0.1.0",
|
"ra_hir 0.1.0",
|
||||||
|
"ra_ide_api 0.1.0",
|
||||||
"ra_project_model 0.1.0",
|
"ra_project_model 0.1.0",
|
||||||
"ra_syntax 0.1.0",
|
"ra_syntax 0.1.0",
|
||||||
"ra_vfs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ra_vfs 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -11,6 +11,7 @@ rustc-hash = "1.0"
|
||||||
ra_vfs = "0.2.0"
|
ra_vfs = "0.2.0"
|
||||||
ra_syntax = { path = "../ra_syntax" }
|
ra_syntax = { path = "../ra_syntax" }
|
||||||
ra_db = { path = "../ra_db" }
|
ra_db = { path = "../ra_db" }
|
||||||
|
ra_ide_api = { path = "../ra_ide_api" }
|
||||||
ra_hir = { path = "../ra_hir" }
|
ra_hir = { path = "../ra_hir" }
|
||||||
ra_project_model = { path = "../ra_project_model" }
|
ra_project_model = { path = "../ra_project_model" }
|
||||||
|
|
||||||
|
|
|
@ -1,36 +1,19 @@
|
||||||
mod vfs_filter;
|
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 rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ra_db::{
|
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_project_model::ProjectWorkspace;
|
||||||
use ra_vfs::{Vfs, VfsChange};
|
use ra_vfs::{Vfs, VfsChange};
|
||||||
use vfs_filter::IncludeRustFiles;
|
use vfs_filter::IncludeRustFiles;
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
|
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 {
|
fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
|
||||||
FileId(f.0)
|
FileId(f.0)
|
||||||
}
|
}
|
||||||
|
@ -38,86 +21,79 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
|
||||||
SourceRootId(r.0)
|
SourceRootId(r.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatchDatabase {
|
pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> {
|
||||||
pub fn load(crate_graph: CrateGraph, vfs: &mut Vfs) -> BatchDatabase {
|
let root = std::env::current_dir()?.join(root);
|
||||||
let mut db = BatchDatabase { runtime: salsa::Runtime::default() };
|
let ws = ProjectWorkspace::discover(root.as_ref())?;
|
||||||
let lru_cap = std::env::var("RA_LRU_CAP")
|
let mut roots = Vec::new();
|
||||||
.ok()
|
roots.push(IncludeRustFiles::member(root.clone()));
|
||||||
.and_then(|it| it.parse::<usize>().ok())
|
roots.extend(IncludeRustFiles::from_roots(ws.to_roots()));
|
||||||
.unwrap_or(ra_db::DEFAULT_LRU_CAP);
|
let (mut vfs, roots) = Vfs::new(roots);
|
||||||
db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_cap);
|
let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
|
||||||
db.query_mut(ra_hir::db::ParseMacroQuery).set_lru_capacity(lru_cap);
|
let vfs_file = vfs.load(path);
|
||||||
db.set_crate_graph(Arc::new(crate_graph));
|
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 local_roots = roots
|
||||||
let receiver = vfs.task_receiver().clone();
|
.into_iter()
|
||||||
let mut roots_loaded = HashSet::new();
|
.filter(|r| vfs.root2path(*r).starts_with(&root))
|
||||||
for task in receiver {
|
.map(vfs_root_to_id)
|
||||||
vfs.handle_task(task);
|
.collect();
|
||||||
let mut done = false;
|
|
||||||
for change in vfs.commit_changes() {
|
let host = load(root.as_path(), crate_graph, &mut vfs);
|
||||||
match change {
|
Ok((host, local_roots))
|
||||||
VfsChange::AddRoot { root, files } => {
|
}
|
||||||
let source_root_id = vfs_root_to_id(root);
|
|
||||||
log::debug!(
|
pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> AnalysisHost {
|
||||||
"loaded source root {:?} with path {:?}",
|
let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
|
||||||
source_root_id,
|
let mut host = AnalysisHost::new(lru_cap);
|
||||||
vfs.root2path(root)
|
let mut analysis_change = AnalysisChange::new();
|
||||||
);
|
analysis_change.set_crate_graph(crate_graph);
|
||||||
let mut file_map = FxHashMap::default();
|
|
||||||
for (vfs_file, path, text) in files {
|
// wait until Vfs has loaded all roots
|
||||||
let file_id = vfs_file_to_id(vfs_file);
|
let receiver = vfs.task_receiver().clone();
|
||||||
db.set_file_text(file_id, text);
|
let mut roots_loaded = HashSet::new();
|
||||||
db.set_file_relative_path(file_id, path.clone());
|
for task in receiver {
|
||||||
db.set_file_source_root(file_id, source_root_id);
|
vfs.handle_task(task);
|
||||||
file_map.insert(path, file_id);
|
let mut done = false;
|
||||||
}
|
for change in vfs.commit_changes() {
|
||||||
let source_root = SourceRoot { files: file_map };
|
match change {
|
||||||
db.set_source_root(source_root_id, Arc::new(source_root));
|
VfsChange::AddRoot { root, files } => {
|
||||||
roots_loaded.insert(source_root_id);
|
let is_local = vfs.root2path(root).starts_with(&project_root);
|
||||||
if roots_loaded.len() == vfs.n_roots() {
|
let source_root_id = vfs_root_to_id(root);
|
||||||
done = true;
|
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 { .. }
|
roots_loaded.insert(source_root_id);
|
||||||
| VfsChange::RemoveFile { .. }
|
if roots_loaded.len() == vfs.n_roots() {
|
||||||
| VfsChange::ChangeFile { .. } => {
|
done = true;
|
||||||
// We just need the first scan, so just ignore these
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
VfsChange::AddFile { .. }
|
||||||
if done {
|
| VfsChange::RemoveFile { .. }
|
||||||
break;
|
| VfsChange::ChangeFile { .. } => {
|
||||||
|
// We just need the first scan, so just ignore these
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if done {
|
||||||
db
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_cargo(root: impl AsRef<Path>) -> Result<(BatchDatabase, Vec<SourceRootId>)> {
|
host.apply_change(analysis_change);
|
||||||
let root = std::env::current_dir()?.join(root);
|
host
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -128,10 +104,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_loading_rust_analyzer() {
|
fn test_loading_rust_analyzer() {
|
||||||
let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
|
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;
|
let mut n_crates = 0;
|
||||||
for root in roots {
|
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;
|
n_crates += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{collections::HashSet, time::Instant, fmt::Write};
|
use std::{collections::HashSet, time::Instant, fmt::Write};
|
||||||
|
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::SourceDatabase;
|
||||||
use ra_batch::BatchDatabase;
|
|
||||||
use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource};
|
use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource};
|
||||||
use ra_syntax::AstNode;
|
use ra_syntax::AstNode;
|
||||||
|
|
||||||
|
@ -9,16 +8,17 @@ use crate::Result;
|
||||||
|
|
||||||
pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
|
pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
|
||||||
let db_load_time = Instant::now();
|
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());
|
println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
|
||||||
let analysis_time = Instant::now();
|
let analysis_time = Instant::now();
|
||||||
let mut num_crates = 0;
|
let mut num_crates = 0;
|
||||||
let mut visited_modules = HashSet::new();
|
let mut visited_modules = HashSet::new();
|
||||||
let mut visit_queue = Vec::new();
|
let mut visit_queue = Vec::new();
|
||||||
for root in roots {
|
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;
|
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);
|
visit_queue.push(module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,17 +27,17 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
|
||||||
let mut funcs = Vec::new();
|
let mut funcs = Vec::new();
|
||||||
while let Some(module) = visit_queue.pop() {
|
while let Some(module) = visit_queue.pop() {
|
||||||
if visited_modules.insert(module) {
|
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;
|
num_decls += 1;
|
||||||
if let ModuleDef::Function(f) = decl {
|
if let ModuleDef::Function(f) = decl {
|
||||||
funcs.push(f);
|
funcs.push(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for impl_block in module.impl_blocks(&db) {
|
for impl_block in module.impl_blocks(db) {
|
||||||
for item in impl_block.items(&db) {
|
for item in impl_block.items(db) {
|
||||||
num_decls += 1;
|
num_decls += 1;
|
||||||
if let ImplItem::Method(f) = item {
|
if let ImplItem::Method(f) = item {
|
||||||
funcs.push(f);
|
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_unknown = 0;
|
||||||
let mut num_exprs_partially_unknown = 0;
|
let mut num_exprs_partially_unknown = 0;
|
||||||
for f in funcs {
|
for f in funcs {
|
||||||
let name = f.name(&db);
|
let name = f.name(db);
|
||||||
let mut msg = format!("processing: {}", name);
|
let mut msg = format!("processing: {}", name);
|
||||||
if verbose {
|
if verbose {
|
||||||
let src = f.source(&db);
|
let src = f.source(db);
|
||||||
let original_file = src.file_id.original_file(&db);
|
let original_file = src.file_id.original_file(db);
|
||||||
let path = db.file_relative_path(original_file);
|
let path = db.file_relative_path(original_file);
|
||||||
let syntax_range = src.ast.syntax().range();
|
let syntax_range = src.ast.syntax().range();
|
||||||
write!(msg, " ({:?} {})", path, syntax_range).unwrap();
|
write!(msg, " ({:?} {})", path, syntax_range).unwrap();
|
||||||
|
@ -76,8 +76,8 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let body = f.body(&db);
|
let body = f.body(db);
|
||||||
let inference_result = f.infer(&db);
|
let inference_result = f.infer(db);
|
||||||
for (expr_id, _) in body.exprs() {
|
for (expr_id, _) in body.exprs() {
|
||||||
let ty = &inference_result[expr_id];
|
let ty = &inference_result[expr_id];
|
||||||
num_exprs += 1;
|
num_exprs += 1;
|
||||||
|
|
|
@ -276,6 +276,9 @@ impl AnalysisHost {
|
||||||
pub fn collect_garbage(&mut self) {
|
pub fn collect_garbage(&mut self) {
|
||||||
self.db.collect_garbage();
|
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
|
/// Analysis is a snapshot of a world state at a moment in time. It is the main
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn main() -> Result<()> {
|
||||||
Err(_) => ra_prof::Filter::disabled(),
|
Err(_) => ra_prof::Filter::disabled(),
|
||||||
});
|
});
|
||||||
log::info!("lifecycle: server started");
|
log::info!("lifecycle: server started");
|
||||||
match ::std::panic::catch_unwind(main_inner) {
|
match std::panic::catch_unwind(main_inner) {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
log::info!("lifecycle: terminating process with {:?}", res);
|
log::info!("lifecycle: terminating process with {:?}", res);
|
||||||
res
|
res
|
||||||
|
|
Loading…
Reference in a new issue