This commit is contained in:
Aleksey Kladov
2020-06-11 11:04:09 +02:00
parent 7aa66371ee
commit dad1333b48
46 changed files with 1028 additions and 1001 deletions

View File

@@ -1,6 +1,7 @@
//! Benchmark operations like highlighting or goto definition.
use std::{
convert::TryFrom,
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
@@ -10,7 +11,7 @@ use std::{
use anyhow::{format_err, Result};
use ra_db::{
salsa::{Database, Durability},
FileId, SourceDatabaseExt,
AbsPathBuf, FileId,
};
use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
@@ -53,8 +54,7 @@ pub fn analysis_bench(
let start = Instant::now();
eprint!("loading: ");
let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let db = host.raw_database();
let (mut host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
eprintln!("{:?}\n", start.elapsed());
let file_id = {
@@ -62,22 +62,9 @@ pub fn analysis_bench(
BenchWhat::Highlight { path } => path,
BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
};
let path = std::env::current_dir()?.join(path).canonicalize()?;
roots
.iter()
.find_map(|(source_root_id, project_root)| {
if project_root.is_member() {
for file_id in db.source_root(*source_root_id).walk() {
let rel_path = db.file_relative_path(file_id);
let abs_path = rel_path.to_path(project_root.path());
if abs_path == path {
return Some(file_id);
}
}
}
None
})
.ok_or_else(|| format_err!("Can't find {}", path.display()))?
let path = AbsPathBuf::try_from(path.clone()).unwrap();
let path = path.into();
vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
};
match &what {
@@ -149,7 +136,7 @@ fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, w
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
text.push_str("\n/* Hello world */\n");
let mut change = AnalysisChange::new();
change.change_file(file_id, Arc::new(text));
change.change_file(file_id, Some(Arc::new(text)));
host.apply_change(change);
}
work(&host.analysis());

View File

@@ -28,26 +28,14 @@ pub fn analysis_stats(
with_proc_macro: bool,
) -> Result<()> {
let db_load_time = Instant::now();
let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let (mut host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let db = host.raw_database();
println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
println!("Database loaded {:?}", 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();
let members =
roots
.into_iter()
.filter_map(|(source_root_id, project_root)| {
if with_deps || project_root.is_member() {
Some(source_root_id)
} else {
None
}
})
.collect::<HashSet<_>>();
let mut krates = Crate::all(db);
if randomize {
krates.shuffle(&mut thread_rng());
@@ -55,7 +43,10 @@ pub fn analysis_stats(
for krate in krates {
let module = krate.root_module(db).expect("crate without root module");
let file_id = module.definition_source(db).file_id;
if members.contains(&db.file_source_root(file_id.original_file(db))) {
let file_id = file_id.original_file(db);
let source_root = db.file_source_root(file_id);
let source_root = db.source_root(source_root);
if !source_root.is_library || with_deps {
num_crates += 1;
visit_queue.push(module);
}
@@ -128,7 +119,7 @@ pub fn analysis_stats(
if verbosity.is_verbose() {
let src = f.source(db);
let original_file = src.file_id.original_file(db);
let path = db.file_relative_path(original_file);
let path = vfs.file_path(original_file);
let syntax_range = src.value.syntax().text_range();
format_to!(msg, " ({:?} {:?})", path, syntax_range);
}
@@ -196,7 +187,7 @@ pub fn analysis_stats(
let root = db.parse_or_expand(src.file_id).unwrap();
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = original_range(db, node.as_ref());
let path = db.file_relative_path(original_range.file_id);
let path = vfs.file_path(original_range.file_id);
let line_index =
host.analysis().file_line_index(original_range.file_id).unwrap();
let text_range = original_range.range;

View File

@@ -2,68 +2,57 @@
//! code if any errors are found.
use anyhow::anyhow;
use hir::Crate;
use ra_db::SourceDatabaseExt;
use ra_ide::Severity;
use std::{collections::HashSet, path::Path};
use crate::cli::{load_cargo::load_cargo, Result};
use hir::Semantics;
pub fn diagnostics(
path: &Path,
load_output_dirs: bool,
with_proc_macro: bool,
all: bool,
_all: bool,
) -> Result<()> {
let (host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
let db = host.raw_database();
let analysis = host.analysis();
let semantics = Semantics::new(db);
let members = roots
.into_iter()
.filter_map(|(source_root_id, project_root)| {
// filter out dependencies
if project_root.is_member() {
Some(source_root_id)
} else {
None
}
})
.collect::<HashSet<_>>();
let mut found_error = false;
let mut visited_files = HashSet::new();
for source_root_id in members {
for file_id in db.source_root(source_root_id).walk() {
// Filter out files which are not actually modules (unless `--all` flag is
// passed). In the rust-analyzer repository this filters out the parser test files.
if semantics.to_module_def(file_id).is_some() || all {
if !visited_files.contains(&file_id) {
let crate_name = if let Some(module) = semantics.to_module_def(file_id) {
if let Some(name) = module.krate().display_name(db) {
format!("{}", name)
} else {
String::from("unknown")
}
} else {
String::from("unknown")
};
println!(
"processing crate: {}, module: {}",
crate_name,
db.file_relative_path(file_id)
);
for diagnostic in analysis.diagnostics(file_id).unwrap() {
if matches!(diagnostic.severity, Severity::Error) {
found_error = true;
}
println!("{:?}", diagnostic);
}
let mut work = Vec::new();
let krates = Crate::all(db);
for krate in krates {
let module = krate.root_module(db).expect("crate without root module");
let file_id = module.definition_source(db).file_id;
let file_id = file_id.original_file(db);
let source_root = db.file_source_root(file_id);
let source_root = db.source_root(source_root);
if !source_root.is_library {
work.push(module);
}
}
visited_files.insert(file_id);
for module in work {
let file_id = module.definition_source(db).file_id.original_file(db);
if !visited_files.contains(&file_id) {
let crate_name = if let Some(name) = module.krate().display_name(db) {
format!("{}", name)
} else {
String::from("unknown")
};
println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
for diagnostic in analysis.diagnostics(file_id).unwrap() {
if matches!(diagnostic.severity, Severity::Error) {
found_error = true;
}
println!("{:?}", diagnostic);
}
visited_files.insert(file_id);
}
}

View File

@@ -1,32 +1,21 @@
//! Loads a Cargo project into a static instance of analysis, without support
//! for incorporating changes.
use std::path::{Path, PathBuf};
use std::{convert::TryFrom, path::Path, sync::Arc};
use anyhow::Result;
use crossbeam_channel::{unbounded, Receiver};
use ra_db::{ExternSourceId, FileId, SourceRootId};
use ra_db::{AbsPathBuf, CrateGraph};
use ra_ide::{AnalysisChange, AnalysisHost};
use ra_project_model::{
CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest, ProjectWorkspace,
};
use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
use rustc_hash::{FxHashMap, FxHashSet};
use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
use vfs::loader::Handle;
use crate::vfs_glob::RustPackageFilterBuilder;
fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
FileId(f.0)
}
fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
SourceRootId(r.0)
}
use crate::global_state::{ProjectFolders, SourceRootConfig};
pub fn load_cargo(
root: &Path,
load_out_dirs_from_check: bool,
with_proc_macro: bool,
) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
) -> Result<(AnalysisHost, vfs::Vfs)> {
let root = std::env::current_dir()?.join(root);
let root = ProjectManifest::discover_single(&root)?;
let ws = ProjectWorkspace::load(
@@ -35,123 +24,74 @@ pub fn load_cargo(
true,
)?;
let mut extern_dirs = FxHashSet::default();
let (sender, receiver) = unbounded();
let sender = Box::new(move |t| sender.send(t).unwrap());
let mut vfs = vfs::Vfs::default();
let mut loader = {
let loader =
vfs_notify::LoaderHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
Box::new(loader)
};
let mut roots = Vec::new();
let project_roots = ws.to_roots();
for root in &project_roots {
roots.push(RootEntry::new(
root.path().to_owned(),
RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
));
if let Some(out_dir) = root.out_dir() {
extern_dirs.insert(out_dir.to_path_buf());
roots.push(RootEntry::new(
out_dir.to_owned(),
RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
))
}
}
let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false));
let source_roots = roots
.into_iter()
.map(|vfs_root| {
let source_root_id = vfs_root_to_id(vfs_root);
let project_root = project_roots
.iter()
.find(|it| it.path() == vfs.root2path(vfs_root))
.unwrap()
.clone();
(source_root_id, project_root)
})
.collect::<FxHashMap<_, _>>();
let proc_macro_client = if !with_proc_macro {
ProcMacroClient::dummy()
} else {
let proc_macro_client = if with_proc_macro {
let path = std::env::current_exe()?;
ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap()
} else {
ProcMacroClient::dummy()
};
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
Ok((host, source_roots))
let crate_graph = ws.to_crate_graph(None, &proc_macro_client, &mut |path: &Path| {
let path = AbsPathBuf::try_from(path.to_path_buf()).unwrap();
let contents = loader.load_sync(&path);
let path = vfs::VfsPath::from(path);
vfs.set_file_contents(path.clone(), contents);
vfs.file_id(&path)
});
let project_folders = ProjectFolders::new(&[ws]);
loader.set_config(vfs::loader::Config { load: project_folders.load, watch: vec![] });
log::debug!("crate graph: {:?}", crate_graph);
let host = load(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
Ok((host, vfs))
}
pub(crate) fn load(
source_roots: &FxHashMap<SourceRootId, PackageRoot>,
ws: ProjectWorkspace,
vfs: &mut Vfs,
receiver: Receiver<VfsTask>,
extern_dirs: FxHashSet<PathBuf>,
proc_macro_client: &ProcMacroClient,
crate_graph: CrateGraph,
source_root_config: SourceRootConfig,
vfs: &mut vfs::Vfs,
receiver: &Receiver<vfs::loader::Message>,
) -> 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();
// wait until Vfs has loaded all roots
let mut roots_loaded = FxHashSet::default();
let mut extern_source_roots = FxHashMap::default();
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);
let is_local = source_roots[&source_root_id].is_member();
log::debug!(
"loaded source root {:?} with path {:?}",
source_root_id,
vfs.root2path(root)
);
analysis_change.add_root(source_root_id, is_local);
let vfs_root_path = vfs.root2path(root);
if extern_dirs.contains(&vfs_root_path) {
extern_source_roots.insert(vfs_root_path, ExternSourceId(root.0));
}
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);
}
roots_loaded.insert(source_root_id);
if roots_loaded.len() == vfs.n_roots() {
done = true;
}
match task {
vfs::loader::Message::Progress { n_entries_done, n_entries_total } => {
if n_entries_done == n_entries_total {
break;
}
VfsChange::AddFile { root, file, path, text } => {
let source_root_id = vfs_root_to_id(root);
let file_id = vfs_file_to_id(file);
analysis_change.add_file(source_root_id, file_id, path, text);
}
VfsChange::RemoveFile { .. } | VfsChange::ChangeFile { .. } => {
// We just need the first scan, so just ignore these
}
vfs::loader::Message::Loaded { files } => {
for (path, contents) in files {
vfs.set_file_contents(path.into(), contents)
}
}
}
if done {
break;
}
let changes = vfs.take_changes();
for file in changes {
if file.exists() {
let contents = vfs.file_contents(file.file_id).to_vec();
if let Ok(text) = String::from_utf8(contents) {
analysis_change.change_file(file.file_id, Some(Arc::new(text)))
}
}
}
let source_roots = source_root_config.partition(&vfs);
analysis_change.set_roots(source_roots);
let crate_graph =
ws.to_crate_graph(None, &extern_source_roots, proc_macro_client, &mut |path: &Path| {
// Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
let path = path.canonicalize().ok()?;
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);
analysis_change.set_crate_graph(crate_graph);
host.apply_change(analysis_change);
@@ -167,7 +107,7 @@ mod tests {
#[test]
fn test_loading_rust_analyzer() {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
let (host, _roots) = load_cargo(path, false, false).unwrap();
let (host, _vfs) = load_cargo(path, false, false).unwrap();
let n_crates = Crate::all(host.raw_database()).len();
// RA has quite a few crates, but the exact count doesn't matter
assert!(n_crates > 20);