diff options
Diffstat (limited to 'crates/ra_cli')
-rw-r--r-- | crates/ra_cli/src/analysis_bench.rs | 92 | ||||
-rw-r--r-- | crates/ra_cli/src/analysis_stats.rs | 39 | ||||
-rw-r--r-- | crates/ra_cli/src/main.rs | 47 |
3 files changed, 159 insertions, 19 deletions
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 @@ | |||
1 | use std::{ | ||
2 | path::{PathBuf, Path}, | ||
3 | time::Instant, | ||
4 | }; | ||
5 | |||
6 | use ra_db::{SourceDatabase, salsa::Database}; | ||
7 | use ra_ide_api::{AnalysisHost, Analysis, LineCol, FilePosition}; | ||
8 | |||
9 | use crate::Result; | ||
10 | |||
11 | pub(crate) enum Op { | ||
12 | Highlight { path: PathBuf }, | ||
13 | Complete { path: PathBuf, line: u32, column: u32 }, | ||
14 | } | ||
15 | |||
16 | pub(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 | |||
71 | fn 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 c19976bd2..ed98fc7f6 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs | |||
@@ -1,25 +1,28 @@ | |||
1 | use std::{collections::HashSet, time::Instant, fmt::Write}; | 1 | use std::{collections::HashSet, time::Instant, fmt::Write, path::Path}; |
2 | 2 | ||
3 | use ra_db::SourceDatabase; | 3 | use ra_db::SourceDatabase; |
4 | use ra_batch::BatchDatabase; | ||
5 | use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; | 4 | use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; |
6 | use ra_syntax::AstNode; | 5 | use ra_syntax::AstNode; |
7 | 6 | ||
8 | use crate::Result; | 7 | use crate::Result; |
9 | 8 | ||
10 | pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { | 9 | pub fn run(verbose: bool, path: &Path, only: Option<&str>) -> Result<()> { |
11 | let db_load_time = Instant::now(); | 10 | let db_load_time = Instant::now(); |
12 | let (db, roots) = BatchDatabase::load_cargo(path)?; | 11 | let (host, roots) = ra_batch::load_cargo(path)?; |
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); |
@@ -27,17 +30,17 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { | |||
27 | let mut funcs = Vec::new(); | 30 | let mut funcs = Vec::new(); |
28 | while let Some(module) = visit_queue.pop() { | 31 | while let Some(module) = visit_queue.pop() { |
29 | if visited_modules.insert(module) { | 32 | if visited_modules.insert(module) { |
30 | visit_queue.extend(module.children(&db)); | 33 | visit_queue.extend(module.children(db)); |
31 | 34 | ||
32 | for decl in module.declarations(&db) { | 35 | for decl in module.declarations(db) { |
33 | num_decls += 1; | 36 | num_decls += 1; |
34 | if let ModuleDef::Function(f) = decl { | 37 | if let ModuleDef::Function(f) = decl { |
35 | funcs.push(f); | 38 | funcs.push(f); |
36 | } | 39 | } |
37 | } | 40 | } |
38 | 41 | ||
39 | for impl_block in module.impl_blocks(&db) { | 42 | for impl_block in module.impl_blocks(db) { |
40 | for item in impl_block.items(&db) { | 43 | for item in impl_block.items(db) { |
41 | num_decls += 1; | 44 | num_decls += 1; |
42 | if let ImplItem::Method(f) = item { | 45 | if let ImplItem::Method(f) = item { |
43 | funcs.push(f); | 46 | funcs.push(f); |
@@ -61,11 +64,11 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { | |||
61 | let mut num_exprs_unknown = 0; | 64 | let mut num_exprs_unknown = 0; |
62 | let mut num_exprs_partially_unknown = 0; | 65 | let mut num_exprs_partially_unknown = 0; |
63 | for f in funcs { | 66 | for f in funcs { |
64 | let name = f.name(&db); | 67 | let name = f.name(db); |
65 | let mut msg = format!("processing: {}", name); | 68 | let mut msg = format!("processing: {}", name); |
66 | if verbose { | 69 | if verbose { |
67 | let src = f.source(&db); | 70 | let src = f.source(db); |
68 | let original_file = src.file_id.original_file(&db); | 71 | let original_file = src.file_id.original_file(db); |
69 | let path = db.file_relative_path(original_file); | 72 | let path = db.file_relative_path(original_file); |
70 | let syntax_range = src.ast.syntax().range(); | 73 | let syntax_range = src.ast.syntax().range(); |
71 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); | 74 | write!(msg, " ({:?} {})", path, syntax_range).unwrap(); |
@@ -76,8 +79,8 @@ pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { | |||
76 | continue; | 79 | continue; |
77 | } | 80 | } |
78 | } | 81 | } |
79 | let body = f.body(&db); | 82 | let body = f.body(db); |
80 | let inference_result = f.infer(&db); | 83 | let inference_result = f.infer(db); |
81 | for (expr_id, _) in body.exprs() { | 84 | for (expr_id, _) in body.exprs() { |
82 | let ty = &inference_result[expr_id]; | 85 | let ty = &inference_result[expr_id]; |
83 | num_exprs += 1; | 86 | num_exprs += 1; |
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 @@ | |||
1 | mod analysis_stats; | 1 | mod analysis_stats; |
2 | mod analysis_bench; | ||
2 | 3 | ||
3 | use std::{io::Read, error::Error}; | 4 | use 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 | |||
112 | fn 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 | } | ||