aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-06-16 17:45:30 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-06-16 17:45:30 +0100
commit924d4d7ca8bce948b8cbb30ef6da17354e48bad3 (patch)
tree022742121ea273b6e90f6fbfa342660f46e9f68e /crates
parentb81caed43f1886024ededad41a1baa8a03f1d2f4 (diff)
parent6314e62cfb06ea7bbe5f530f2824010be0ffa4c7 (diff)
Merge #1411
1411: add analysis-bench to benchmark incremental analysis r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_batch/src/lib.rs42
-rw-r--r--crates/ra_batch/src/vfs_filter.rs108
-rw-r--r--crates/ra_cli/src/analysis_bench.rs92
-rw-r--r--crates/ra_cli/src/analysis_stats.rs19
-rw-r--r--crates/ra_cli/src/main.rs47
-rw-r--r--crates/ra_ide_api/src/lib.rs2
-rw-r--r--crates/ra_ide_api/src/line_index.rs2
-rw-r--r--crates/ra_project_model/src/lib.rs1
8 files changed, 232 insertions, 81 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs
index fa244e86c..43d3fb7e3 100644
--- a/crates/ra_batch/src/lib.rs
+++ b/crates/ra_batch/src/lib.rs
@@ -8,7 +8,7 @@ use ra_db::{
8 CrateGraph, FileId, SourceRootId, 8 CrateGraph, FileId, SourceRootId,
9}; 9};
10use ra_ide_api::{AnalysisHost, AnalysisChange}; 10use ra_ide_api::{AnalysisHost, AnalysisChange};
11use ra_project_model::ProjectWorkspace; 11use ra_project_model::{ProjectWorkspace, ProjectRoot};
12use ra_vfs::{Vfs, VfsChange}; 12use ra_vfs::{Vfs, VfsChange};
13use vfs_filter::IncludeRustFiles; 13use vfs_filter::IncludeRustFiles;
14 14
@@ -21,13 +21,11 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
21 SourceRootId(r.0) 21 SourceRootId(r.0)
22} 22}
23 23
24pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> { 24pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, ProjectRoot>)> {
25 let root = std::env::current_dir()?.join(root); 25 let root = std::env::current_dir()?.join(root);
26 let ws = ProjectWorkspace::discover(root.as_ref())?; 26 let ws = ProjectWorkspace::discover(root.as_ref())?;
27 let mut roots = Vec::new(); 27 let project_roots = ws.to_roots();
28 roots.push(IncludeRustFiles::member(root.clone())); 28 let (mut vfs, roots) = Vfs::new(IncludeRustFiles::from_roots(project_roots.clone()).collect());
29 roots.extend(IncludeRustFiles::from_roots(ws.to_roots()));
30 let (mut vfs, roots) = Vfs::new(roots);
31 let crate_graph = ws.to_crate_graph(&mut |path: &Path| { 29 let crate_graph = ws.to_crate_graph(&mut |path: &Path| {
32 let vfs_file = vfs.load(path); 30 let vfs_file = vfs.load(path);
33 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 31 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
@@ -35,17 +33,27 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> {
35 }); 33 });
36 log::debug!("crate graph: {:?}", crate_graph); 34 log::debug!("crate graph: {:?}", crate_graph);
37 35
38 let local_roots = roots 36 let source_roots = roots
39 .into_iter() 37 .iter()
40 .filter(|r| vfs.root2path(*r).starts_with(&root)) 38 .map(|&vfs_root| {
41 .map(vfs_root_to_id) 39 let source_root_id = vfs_root_to_id(vfs_root);
42 .collect(); 40 let project_root = project_roots
43 41 .iter()
44 let host = load(root.as_path(), crate_graph, &mut vfs); 42 .find(|it| it.path() == &vfs.root2path(vfs_root))
45 Ok((host, local_roots)) 43 .unwrap()
44 .clone();
45 (source_root_id, project_root)
46 })
47 .collect::<FxHashMap<_, _>>();
48 let host = load(&source_roots, crate_graph, &mut vfs);
49 Ok((host, source_roots))
46} 50}
47 51
48pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> AnalysisHost { 52pub fn load(
53 source_roots: &FxHashMap<SourceRootId, ProjectRoot>,
54 crate_graph: CrateGraph,
55 vfs: &mut Vfs,
56) -> AnalysisHost {
49 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 57 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
50 let mut host = AnalysisHost::new(lru_cap); 58 let mut host = AnalysisHost::new(lru_cap);
51 let mut analysis_change = AnalysisChange::new(); 59 let mut analysis_change = AnalysisChange::new();
@@ -60,8 +68,8 @@ pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> Anal
60 for change in vfs.commit_changes() { 68 for change in vfs.commit_changes() {
61 match change { 69 match change {
62 VfsChange::AddRoot { root, files } => { 70 VfsChange::AddRoot { root, files } => {
63 let is_local = vfs.root2path(root).starts_with(&project_root);
64 let source_root_id = vfs_root_to_id(root); 71 let source_root_id = vfs_root_to_id(root);
72 let is_local = source_roots[&source_root_id].is_member();
65 log::debug!( 73 log::debug!(
66 "loaded source root {:?} with path {:?}", 74 "loaded source root {:?} with path {:?}",
67 source_root_id, 75 source_root_id,
@@ -106,7 +114,7 @@ mod tests {
106 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 114 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
107 let (host, roots) = load_cargo(path).unwrap(); 115 let (host, roots) = load_cargo(path).unwrap();
108 let mut n_crates = 0; 116 let mut n_crates = 0;
109 for root in roots { 117 for (root, _) in roots {
110 for _krate in Crate::source_root_crates(host.raw_database(), root) { 118 for _krate in Crate::source_root_crates(host.raw_database(), root) {
111 n_crates += 1; 119 n_crates += 1;
112 } 120 }
diff --git a/crates/ra_batch/src/vfs_filter.rs b/crates/ra_batch/src/vfs_filter.rs
index dd20c1203..8552ac999 100644
--- a/crates/ra_batch/src/vfs_filter.rs
+++ b/crates/ra_batch/src/vfs_filter.rs
@@ -1,54 +1,54 @@
1use std::path::PathBuf; 1use std::path::PathBuf;
2use ra_project_model::ProjectRoot; 2use ra_project_model::ProjectRoot;
3use ra_vfs::{RootEntry, Filter, RelativePath}; 3use ra_vfs::{RootEntry, Filter, RelativePath};
4 4
5/// `IncludeRustFiles` is used to convert 5/// `IncludeRustFiles` is used to convert
6/// from `ProjectRoot` to `RootEntry` for VFS 6/// from `ProjectRoot` to `RootEntry` for VFS
7pub struct IncludeRustFiles { 7pub struct IncludeRustFiles {
8 root: ProjectRoot, 8 root: ProjectRoot,
9} 9}
10 10
11impl IncludeRustFiles { 11impl IncludeRustFiles {
12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry> 12 pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry>
13 where 13 where
14 R: IntoIterator<Item = ProjectRoot>, 14 R: IntoIterator<Item = ProjectRoot>,
15 { 15 {
16 roots.into_iter().map(IncludeRustFiles::from_root) 16 roots.into_iter().map(IncludeRustFiles::from_root)
17 } 17 }
18 18
19 pub fn from_root(root: ProjectRoot) -> RootEntry { 19 pub fn from_root(root: ProjectRoot) -> RootEntry {
20 IncludeRustFiles::from(root).into() 20 IncludeRustFiles::from(root).into()
21 } 21 }
22 22
23 #[allow(unused)] 23 #[allow(unused)]
24 pub fn external(path: PathBuf) -> RootEntry { 24 pub fn external(path: PathBuf) -> RootEntry {
25 IncludeRustFiles::from_root(ProjectRoot::new(path, false)) 25 IncludeRustFiles::from_root(ProjectRoot::new(path, false))
26 } 26 }
27 27
28 pub fn member(path: PathBuf) -> RootEntry { 28 pub fn member(path: PathBuf) -> RootEntry {
29 IncludeRustFiles::from_root(ProjectRoot::new(path, true)) 29 IncludeRustFiles::from_root(ProjectRoot::new(path, true))
30 } 30 }
31} 31}
32 32
33impl Filter for IncludeRustFiles { 33impl Filter for IncludeRustFiles {
34 fn include_dir(&self, dir_path: &RelativePath) -> bool { 34 fn include_dir(&self, dir_path: &RelativePath) -> bool {
35 self.root.include_dir(dir_path) 35 self.root.include_dir(dir_path)
36 } 36 }
37 37
38 fn include_file(&self, file_path: &RelativePath) -> bool { 38 fn include_file(&self, file_path: &RelativePath) -> bool {
39 self.root.include_file(file_path) 39 self.root.include_file(file_path)
40 } 40 }
41} 41}
42 42
43impl std::convert::From<ProjectRoot> for IncludeRustFiles { 43impl From<ProjectRoot> for IncludeRustFiles {
44 fn from(v: ProjectRoot) -> IncludeRustFiles { 44 fn from(v: ProjectRoot) -> IncludeRustFiles {
45 IncludeRustFiles { root: v } 45 IncludeRustFiles { root: v }
46 } 46 }
47} 47}
48 48
49impl std::convert::From<IncludeRustFiles> for RootEntry { 49impl From<IncludeRustFiles> for RootEntry {
50 fn from(v: IncludeRustFiles) -> RootEntry { 50 fn from(v: IncludeRustFiles) -> RootEntry {
51 let path = v.root.path().clone(); 51 let path = v.root.path().clone();
52 RootEntry::new(path, Box::new(v)) 52 RootEntry::new(path, Box::new(v))
53 } 53 }
54} 54}
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs
new file mode 100644
index 000000000..33d472838
--- /dev/null
+++ b/crates/ra_cli/src/analysis_bench.rs
@@ -0,0 +1,92 @@
1use std::{
2 path::{PathBuf, Path},
3 time::Instant,
4};
5
6use ra_db::{SourceDatabase, salsa::Database};
7use ra_ide_api::{AnalysisHost, Analysis, LineCol, FilePosition};
8
9use crate::Result;
10
11pub(crate) enum Op {
12 Highlight { path: PathBuf },
13 Complete { path: PathBuf, line: u32, column: u32 },
14}
15
16pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
17 let start = Instant::now();
18 eprint!("loading: ");
19 let (host, roots) = ra_batch::load_cargo(path)?;
20 let db = host.raw_database();
21 eprintln!("{:?}\n", start.elapsed());
22
23 let file_id = {
24 let path = match &op {
25 Op::Highlight { path } => path,
26 Op::Complete { path, .. } => path,
27 };
28 let path = std::env::current_dir()?.join(path).canonicalize()?;
29 roots
30 .iter()
31 .find_map(|(source_root_id, project_root)| {
32 if project_root.is_member() {
33 for (rel_path, file_id) in &db.source_root(*source_root_id).files {
34 let abs_path = rel_path.to_path(project_root.path());
35 if abs_path == path {
36 return Some(*file_id);
37 }
38 }
39 }
40 None
41 })
42 .ok_or_else(|| format!("Can't find {:?}", path))?
43 };
44
45 match op {
46 Op::Highlight { .. } => {
47 let res = do_work(&host, |analysis| {
48 analysis.diagnostics(file_id).unwrap();
49 analysis.highlight_as_html(file_id, false).unwrap()
50 });
51 if verbose {
52 println!("\n{}", res);
53 }
54 }
55 Op::Complete { line, column, .. } => {
56 let offset = host
57 .analysis()
58 .file_line_index(file_id)
59 .offset(LineCol { line, col_utf16: column });
60 let file_postion = FilePosition { file_id, offset };
61
62 let res = do_work(&host, |analysis| analysis.completions(file_postion));
63 if verbose {
64 println!("\n{:#?}", res);
65 }
66 }
67 }
68 Ok(())
69}
70
71fn do_work<F: Fn(&Analysis) -> T, T>(host: &AnalysisHost, work: F) -> T {
72 {
73 let start = Instant::now();
74 eprint!("from scratch: ");
75 work(&host.analysis());
76 eprintln!("{:?}", start.elapsed());
77 }
78 {
79 let start = Instant::now();
80 eprint!("no change: ");
81 work(&host.analysis());
82 eprintln!("{:?}", start.elapsed());
83 }
84 {
85 let start = Instant::now();
86 eprint!("trivial change: ");
87 host.raw_database().salsa_runtime().next_revision();
88 let res = work(&host.analysis());
89 eprintln!("{:?}", start.elapsed());
90 res
91 }
92}
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs
index d76c37d84..ed98fc7f6 100644
--- a/crates/ra_cli/src/analysis_stats.rs
+++ b/crates/ra_cli/src/analysis_stats.rs
@@ -1,4 +1,4 @@
1use std::{collections::HashSet, time::Instant, fmt::Write}; 1use std::{collections::HashSet, time::Instant, fmt::Write, path::Path};
2 2
3use ra_db::SourceDatabase; 3use ra_db::SourceDatabase;
4use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; 4use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource};
@@ -6,20 +6,23 @@ use ra_syntax::AstNode;
6 6
7use crate::Result; 7use crate::Result;
8 8
9pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { 9pub fn run(verbose: bool, path: &Path, only: Option<&str>) -> Result<()> {
10 let db_load_time = Instant::now(); 10 let db_load_time = Instant::now();
11 let (host, roots) = ra_batch::load_cargo(path.as_ref())?; 11 let (host, roots) = ra_batch::load_cargo(path)?;
12 let db = host.raw_database(); 12 let db = host.raw_database();
13 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); 13 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
14 let analysis_time = Instant::now(); 14 let analysis_time = Instant::now();
15 let mut num_crates = 0; 15 let mut num_crates = 0;
16 let mut visited_modules = HashSet::new(); 16 let mut visited_modules = HashSet::new();
17 let mut visit_queue = Vec::new(); 17 let mut visit_queue = Vec::new();
18 for root in roots { 18 for (source_root_id, project_root) in roots {
19 for krate in Crate::source_root_crates(db, root) { 19 if project_root.is_member() {
20 num_crates += 1; 20 for krate in Crate::source_root_crates(db, source_root_id) {
21 let module = krate.root_module(db).expect("crate in source root without root module"); 21 num_crates += 1;
22 visit_queue.push(module); 22 let module =
23 krate.root_module(db).expect("crate in source root without root module");
24 visit_queue.push(module);
25 }
23 } 26 }
24 } 27 }
25 println!("Crates in this dir: {}", num_crates); 28 println!("Crates in this dir: {}", num_crates);
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 1db98aec1..5adf8b096 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -1,4 +1,5 @@
1mod analysis_stats; 1mod analysis_stats;
2mod analysis_bench;
2 3
3use std::{io::Read, error::Error}; 4use std::{io::Read, error::Error};
4 5
@@ -26,6 +27,27 @@ fn main() -> Result<()> {
26 .arg(Arg::with_name("only").short("o").takes_value(true)) 27 .arg(Arg::with_name("only").short("o").takes_value(true))
27 .arg(Arg::with_name("path")), 28 .arg(Arg::with_name("path")),
28 ) 29 )
30 .subcommand(
31 SubCommand::with_name("analysis-bench")
32 .arg(Arg::with_name("verbose").short("v").long("verbose"))
33 .arg(
34 Arg::with_name("highlight")
35 .long("highlight")
36 .takes_value(true)
37 .conflicts_with("complete")
38 .value_name("PATH")
39 .help("highlight this file"),
40 )
41 .arg(
42 Arg::with_name("complete")
43 .long("complete")
44 .takes_value(true)
45 .conflicts_with("highlight")
46 .value_name("PATH:LINE:COLUMN")
47 .help("compute completions at this location"),
48 )
49 .arg(Arg::with_name("path").value_name("PATH").help("project to analyze")),
50 )
29 .get_matches(); 51 .get_matches();
30 match matches.subcommand() { 52 match matches.subcommand() {
31 ("parse", Some(matches)) => { 53 ("parse", Some(matches)) => {
@@ -51,7 +73,25 @@ fn main() -> Result<()> {
51 let verbose = matches.is_present("verbose"); 73 let verbose = matches.is_present("verbose");
52 let path = matches.value_of("path").unwrap_or(""); 74 let path = matches.value_of("path").unwrap_or("");
53 let only = matches.value_of("only"); 75 let only = matches.value_of("only");
54 analysis_stats::run(verbose, path, only)?; 76 analysis_stats::run(verbose, path.as_ref(), only)?;
77 }
78 ("analysis-bench", Some(matches)) => {
79 let verbose = matches.is_present("verbose");
80 let path = matches.value_of("path").unwrap_or("");
81 let op = if let Some(path) = matches.value_of("highlight") {
82 analysis_bench::Op::Highlight { path: path.into() }
83 } else if let Some(path_line_col) = matches.value_of("complete") {
84 let (path_line, column) = rsplit_at_char(path_line_col, ':')?;
85 let (path, line) = rsplit_at_char(path_line, ':')?;
86 analysis_bench::Op::Complete {
87 path: path.into(),
88 line: line.parse()?,
89 column: column.parse()?,
90 }
91 } else {
92 panic!("either --highlight or --complete must be set")
93 };
94 analysis_bench::run(verbose, path.as_ref(), op)?;
55 } 95 }
56 _ => unreachable!(), 96 _ => unreachable!(),
57 } 97 }
@@ -68,3 +108,8 @@ fn read_stdin() -> Result<String> {
68 std::io::stdin().read_to_string(&mut buff)?; 108 std::io::stdin().read_to_string(&mut buff)?;
69 Ok(buff) 109 Ok(buff)
70} 110}
111
112fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
113 let idx = s.rfind(":").ok_or_else(|| format!("no `{}` in {}", c, s))?;
114 Ok((&s[..idx], &s[idx + 1..]))
115}
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index a68c5e2a5..e61d5627e 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -276,7 +276,7 @@ impl AnalysisHost {
276 pub fn collect_garbage(&mut self) { 276 pub fn collect_garbage(&mut self) {
277 self.db.collect_garbage(); 277 self.db.collect_garbage();
278 } 278 }
279 pub fn raw_database(&self) -> &impl hir::db::HirDatabase { 279 pub fn raw_database(&self) -> &(impl hir::db::HirDatabase + salsa::Database) {
280 &self.db 280 &self.db
281 } 281 }
282} 282}
diff --git a/crates/ra_ide_api/src/line_index.rs b/crates/ra_ide_api/src/line_index.rs
index 087dfafed..a53cf9ee0 100644
--- a/crates/ra_ide_api/src/line_index.rs
+++ b/crates/ra_ide_api/src/line_index.rs
@@ -10,7 +10,9 @@ pub struct LineIndex {
10 10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub struct LineCol { 12pub struct LineCol {
13 /// Zero-based
13 pub line: u32, 14 pub line: u32,
15 /// Zero-based
14 pub col_utf16: u32, 16 pub col_utf16: u32,
15} 17}
16 18
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a3af153f1..42156bea6 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -37,6 +37,7 @@ pub enum ProjectWorkspace {
37/// `ProjectRoot` describes a workspace root folder. 37/// `ProjectRoot` describes a workspace root folder.
38/// Which may be an external dependency, or a member of 38/// Which may be an external dependency, or a member of
39/// the current workspace. 39/// the current workspace.
40#[derive(Clone)]
40pub struct ProjectRoot { 41pub struct ProjectRoot {
41 /// Path to the root folder 42 /// Path to the root folder
42 path: PathBuf, 43 path: PathBuf,