Skip to content

Commit 6964a88

Browse files
committed
Add an ra_cli command that analyses all crates in the current workspace
... and prints various stats about how many expressions have a type etc.
1 parent 43e52ac commit 6964a88

File tree

9 files changed

+227
-4
lines changed

9 files changed

+227
-4
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_batch/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ impl BatchDatabase {
6060
match change {
6161
VfsChange::AddRoot { root, files } => {
6262
let source_root_id = vfs_root_to_id(root);
63-
log::debug!("loaded source root {:?} with path {:?}", source_root_id, vfs.root2path(root));
63+
log::debug!(
64+
"loaded source root {:?} with path {:?}",
65+
source_root_id,
66+
vfs.root2path(root)
67+
);
6468
let mut file_map = FxHashMap::default();
6569
for (vfs_file, path, text) in files {
6670
let file_id = vfs_file_to_id(vfs_file);
@@ -111,7 +115,8 @@ impl BatchDatabase {
111115
let crate_graph = ws.to_crate_graph(&mut load);
112116
log::debug!("crate graph: {:?}", crate_graph);
113117

114-
let local_roots = roots.into_iter()
118+
let local_roots = roots
119+
.into_iter()
115120
.filter(|r| vfs.root2path(*r).starts_with(&root))
116121
.map(vfs_root_to_id)
117122
.collect();

crates/ra_cli/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ publish = false
99
clap = "2.32.0"
1010
failure = "0.1.4"
1111
join_to_string = "0.1.1"
12+
flexi_logger = "0.10.0"
13+
indicatif = "0.11.0"
14+
1215
ra_syntax = { path = "../ra_syntax" }
1316
ra_ide_api_light = { path = "../ra_ide_api_light" }
1417
tools = { path = "../tools" }
18+
ra_batch = { path = "../ra_batch" }
19+
ra_hir = { path = "../ra_hir" }
20+
ra_db = { path = "../ra_db" }

crates/ra_cli/src/analysis_stats.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use std::collections::HashSet;
2+
3+
use ra_db::SourceDatabase;
4+
use ra_batch::BatchDatabase;
5+
use ra_hir::{Crate, ModuleDef, Ty, ImplItem};
6+
use ra_syntax::AstNode;
7+
8+
use crate::Result;
9+
10+
pub fn run(verbose: bool) -> Result<()> {
11+
let (db, roots) = BatchDatabase::load_cargo(".")?;
12+
println!("Database loaded, {} roots", roots.len());
13+
let mut num_crates = 0;
14+
let mut visited_modules = HashSet::new();
15+
let mut visit_queue = Vec::new();
16+
for root in roots {
17+
for krate in Crate::source_root_crates(&db, root) {
18+
num_crates += 1;
19+
let module = krate.root_module(&db).expect("crate in source root without root module");
20+
visit_queue.push(module);
21+
}
22+
}
23+
println!("Crates in this dir: {}", num_crates);
24+
let mut num_decls = 0;
25+
let mut funcs = Vec::new();
26+
while let Some(module) = visit_queue.pop() {
27+
if visited_modules.insert(module) {
28+
visit_queue.extend(module.children(&db));
29+
30+
for decl in module.declarations(&db) {
31+
num_decls += 1;
32+
match decl {
33+
ModuleDef::Function(f) => funcs.push(f),
34+
_ => {}
35+
}
36+
}
37+
38+
for impl_block in module.impl_blocks(&db) {
39+
for item in impl_block.items() {
40+
num_decls += 1;
41+
match item {
42+
ImplItem::Method(f) => funcs.push(*f),
43+
_ => {}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
println!("Total modules found: {}", visited_modules.len());
50+
println!("Total declarations: {}", num_decls);
51+
println!("Total functions: {}", funcs.len());
52+
let bar = indicatif::ProgressBar::new(funcs.len() as u64);
53+
bar.tick();
54+
let mut num_exprs = 0;
55+
let mut num_exprs_unknown = 0;
56+
let mut num_exprs_partially_unknown = 0;
57+
for f in funcs {
58+
if verbose {
59+
let (file_id, source) = f.source(&db);
60+
let original_file = file_id.original_file(&db);
61+
let path = db.file_relative_path(original_file);
62+
let syntax_range = source.syntax().range();
63+
let name = f.name(&db);
64+
println!("{} ({:?} {})", name, path, syntax_range);
65+
}
66+
let body = f.body(&db);
67+
let inference_result = f.infer(&db);
68+
for (expr_id, _) in body.exprs() {
69+
let ty = &inference_result[expr_id];
70+
num_exprs += 1;
71+
if let Ty::Unknown = ty {
72+
num_exprs_unknown += 1;
73+
} else {
74+
let mut is_partially_unknown = false;
75+
ty.walk(&mut |ty| {
76+
if let Ty::Unknown = ty {
77+
is_partially_unknown = true;
78+
}
79+
});
80+
if is_partially_unknown {
81+
num_exprs_partially_unknown += 1;
82+
}
83+
}
84+
}
85+
bar.inc(1);
86+
}
87+
bar.finish_and_clear();
88+
println!("Total expressions: {}", num_exprs);
89+
println!(
90+
"Expressions of unknown type: {} ({}%)",
91+
num_exprs_unknown,
92+
(num_exprs_unknown * 100 / num_exprs)
93+
);
94+
println!(
95+
"Expressions of partially unknown type: {} ({}%)",
96+
num_exprs_partially_unknown,
97+
(num_exprs_partially_unknown * 100 / num_exprs)
98+
);
99+
Ok(())
100+
}

crates/ra_cli/src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
mod analysis_stats;
2+
13
use std::{fs, io::Read, path::Path, time::Instant};
24

35
use clap::{App, Arg, SubCommand};
46
use join_to_string::join;
57
use ra_ide_api_light::{extend_selection, file_structure, syntax_tree};
68
use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
79
use tools::collect_tests;
10+
use flexi_logger::Logger;
811

912
type Result<T> = ::std::result::Result<T, failure::Error>;
1013

1114
fn main() -> Result<()> {
15+
Logger::with_env().start()?;
1216
let matches = App::new("ra-cli")
1317
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
1418
.subcommand(
@@ -23,6 +27,9 @@ fn main() -> Result<()> {
2327
.arg(Arg::with_name("start"))
2428
.arg(Arg::with_name("end")),
2529
)
30+
.subcommand(
31+
SubCommand::with_name("analysis-stats").arg(Arg::with_name("verbose").short("v")),
32+
)
2633
.get_matches();
2734
match matches.subcommand() {
2835
("parse", Some(matches)) => {
@@ -56,6 +63,10 @@ fn main() -> Result<()> {
5663
let sels = selections(&file, start, end);
5764
println!("{}", sels)
5865
}
66+
("analysis-stats", Some(matches)) => {
67+
let verbose = matches.is_present("verbose");
68+
analysis_stats::run(verbose)?;
69+
}
5970
_ => unreachable!(),
6071
}
6172
Ok(())

crates/ra_db/src/input.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl CrateGraph {
112112
self.arena[&crate_id].file_id
113113
}
114114

115+
// TODO: this only finds one crate with the given root; we could have multiple
115116
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
116117
let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;
117118
Some(crate_id)

crates/ra_hir/src/code_model_api.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::sync::Arc;
22

33
use relative_path::RelativePathBuf;
4-
use ra_db::{CrateId, FileId};
4+
use ra_db::{CrateId, FileId, SourceRootId};
55
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
66

77
use crate::{
@@ -16,7 +16,7 @@ use crate::{
1616
docs::{Documentation, Docs, docs_from_ast},
1717
module_tree::ModuleId,
1818
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
19-
impl_block::ImplId,
19+
impl_block::{ImplId, ImplBlock},
2020
resolve::Resolver,
2121
};
2222

@@ -44,6 +44,15 @@ impl Crate {
4444
pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
4545
self.root_module_impl(db)
4646
}
47+
48+
// TODO: should this be in source_binder?
49+
pub fn source_root_crates(
50+
db: &impl PersistentHirDatabase,
51+
source_root: SourceRootId,
52+
) -> Vec<Crate> {
53+
let crate_ids = db.source_root_crates(source_root);
54+
crate_ids.iter().map(|&crate_id| Crate { crate_id }).collect()
55+
}
4756
}
4857

4958
#[derive(Debug)]
@@ -168,6 +177,27 @@ impl Module {
168177
let item_map = db.item_map(self.krate);
169178
Resolver::default().push_module_scope(item_map, *self)
170179
}
180+
181+
pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> {
182+
let (lowered_module, _) = db.lower_module(self);
183+
lowered_module
184+
.declarations
185+
.values()
186+
.cloned()
187+
.flat_map(|per_ns| {
188+
per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
189+
})
190+
.collect()
191+
}
192+
193+
pub fn impl_blocks(self, db: &impl HirDatabase) -> Vec<ImplBlock> {
194+
let module_impl_blocks = db.impls_in_module(self);
195+
module_impl_blocks
196+
.impls
197+
.iter()
198+
.map(|(impl_id, _)| ImplBlock::from_id(module_impl_blocks.clone(), impl_id))
199+
.collect()
200+
}
171201
}
172202

173203
impl Docs for Module {

crates/ra_hir/src/expr.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ impl Body {
7070
self.owner
7171
}
7272

73+
pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> {
74+
self.exprs.iter()
75+
}
76+
77+
pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> {
78+
self.pats.iter()
79+
}
80+
7381
pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> {
7482
db.body_syntax_mapping(self.owner)
7583
}

crates/ra_hir/src/ty.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,41 @@ impl Ty {
449449
Ty::Tuple(Arc::new([]))
450450
}
451451

452+
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
453+
f(self);
454+
match self {
455+
Ty::Slice(t) | Ty::Array(t) => t.walk(f),
456+
Ty::RawPtr(t, _) => t.walk(f),
457+
Ty::Ref(t, _) => t.walk(f),
458+
Ty::Tuple(ts) => {
459+
for t in ts.iter() {
460+
t.walk(f);
461+
}
462+
}
463+
Ty::FnPtr(sig) => {
464+
for input in &sig.input {
465+
input.walk(f);
466+
}
467+
sig.output.walk(f);
468+
}
469+
Ty::FnDef { substs, sig, .. } => {
470+
for input in &sig.input {
471+
input.walk(f);
472+
}
473+
sig.output.walk(f);
474+
for t in substs.0.iter() {
475+
t.walk(f);
476+
}
477+
}
478+
Ty::Adt { substs, .. } => {
479+
for t in substs.0.iter() {
480+
t.walk(f);
481+
}
482+
}
483+
_ => {}
484+
}
485+
}
486+
452487
fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
453488
f(self);
454489
match self {

0 commit comments

Comments
 (0)