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.
This commit is contained in:
parent
43e52ac9e2
commit
6964a88e8c
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -486,6 +486,18 @@ name = "indexmap"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"console 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.6.1"
|
||||
@ -780,6 +792,14 @@ dependencies = [
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owning_ref"
|
||||
version = "0.4.0"
|
||||
@ -930,7 +950,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flexi_logger 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ra_batch 0.1.0",
|
||||
"ra_db 0.1.0",
|
||||
"ra_hir 0.1.0",
|
||||
"ra_ide_api_light 0.1.0",
|
||||
"ra_syntax 0.1.0",
|
||||
"tools 0.1.0",
|
||||
@ -1949,6 +1974,7 @@ dependencies = [
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum im 12.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0627d417829c1d763d602687634869f254fc79f7e22dea6c824dab993db857e4"
|
||||
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||
"checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c"
|
||||
"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
|
||||
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
|
||||
"checksum insta 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcdfb5ab565a1fc5c397722d5a9503f2095696ef07ef1a222d85a0fd6666c6aa"
|
||||
@ -1982,6 +2008,7 @@ dependencies = [
|
||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
|
||||
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||
"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238"
|
||||
"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee"
|
||||
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
|
||||
"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337"
|
||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||
|
@ -60,7 +60,11 @@ pub fn load(crate_graph: CrateGraph, vfs: &mut Vfs) -> BatchDatabase {
|
||||
match change {
|
||||
VfsChange::AddRoot { root, files } => {
|
||||
let source_root_id = vfs_root_to_id(root);
|
||||
log::debug!("loaded source root {:?} with path {:?}", source_root_id, vfs.root2path(root));
|
||||
log::debug!(
|
||||
"loaded source root {:?} with path {:?}",
|
||||
source_root_id,
|
||||
vfs.root2path(root)
|
||||
);
|
||||
let mut file_map = FxHashMap::default();
|
||||
for (vfs_file, path, text) in files {
|
||||
let file_id = vfs_file_to_id(vfs_file);
|
||||
@ -111,7 +115,8 @@ pub fn load_cargo(root: impl AsRef<Path>) -> Result<(BatchDatabase, Vec<SourceRo
|
||||
let crate_graph = ws.to_crate_graph(&mut load);
|
||||
log::debug!("crate graph: {:?}", crate_graph);
|
||||
|
||||
let local_roots = roots.into_iter()
|
||||
let local_roots = roots
|
||||
.into_iter()
|
||||
.filter(|r| vfs.root2path(*r).starts_with(&root))
|
||||
.map(vfs_root_to_id)
|
||||
.collect();
|
||||
|
@ -9,6 +9,12 @@ publish = false
|
||||
clap = "2.32.0"
|
||||
failure = "0.1.4"
|
||||
join_to_string = "0.1.1"
|
||||
flexi_logger = "0.10.0"
|
||||
indicatif = "0.11.0"
|
||||
|
||||
ra_syntax = { path = "../ra_syntax" }
|
||||
ra_ide_api_light = { path = "../ra_ide_api_light" }
|
||||
tools = { path = "../tools" }
|
||||
ra_batch = { path = "../ra_batch" }
|
||||
ra_hir = { path = "../ra_hir" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
|
100
crates/ra_cli/src/analysis_stats.rs
Normal file
100
crates/ra_cli/src/analysis_stats.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_batch::BatchDatabase;
|
||||
use ra_hir::{Crate, ModuleDef, Ty, ImplItem};
|
||||
use ra_syntax::AstNode;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub fn run(verbose: bool) -> Result<()> {
|
||||
let (db, roots) = BatchDatabase::load_cargo(".")?;
|
||||
println!("Database loaded, {} roots", roots.len());
|
||||
let mut num_crates = 0;
|
||||
let mut visited_modules = HashSet::new();
|
||||
let mut visit_queue = Vec::new();
|
||||
for root in roots {
|
||||
for krate in Crate::source_root_crates(&db, root) {
|
||||
num_crates += 1;
|
||||
let module = krate.root_module(&db).expect("crate in source root without root module");
|
||||
visit_queue.push(module);
|
||||
}
|
||||
}
|
||||
println!("Crates in this dir: {}", num_crates);
|
||||
let mut num_decls = 0;
|
||||
let mut funcs = Vec::new();
|
||||
while let Some(module) = visit_queue.pop() {
|
||||
if visited_modules.insert(module) {
|
||||
visit_queue.extend(module.children(&db));
|
||||
|
||||
for decl in module.declarations(&db) {
|
||||
num_decls += 1;
|
||||
match decl {
|
||||
ModuleDef::Function(f) => funcs.push(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
for impl_block in module.impl_blocks(&db) {
|
||||
for item in impl_block.items() {
|
||||
num_decls += 1;
|
||||
match item {
|
||||
ImplItem::Method(f) => funcs.push(*f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("Total modules found: {}", visited_modules.len());
|
||||
println!("Total declarations: {}", num_decls);
|
||||
println!("Total functions: {}", funcs.len());
|
||||
let bar = indicatif::ProgressBar::new(funcs.len() as u64);
|
||||
bar.tick();
|
||||
let mut num_exprs = 0;
|
||||
let mut num_exprs_unknown = 0;
|
||||
let mut num_exprs_partially_unknown = 0;
|
||||
for f in funcs {
|
||||
if verbose {
|
||||
let (file_id, source) = f.source(&db);
|
||||
let original_file = file_id.original_file(&db);
|
||||
let path = db.file_relative_path(original_file);
|
||||
let syntax_range = source.syntax().range();
|
||||
let name = f.name(&db);
|
||||
println!("{} ({:?} {})", name, path, syntax_range);
|
||||
}
|
||||
let body = f.body(&db);
|
||||
let inference_result = f.infer(&db);
|
||||
for (expr_id, _) in body.exprs() {
|
||||
let ty = &inference_result[expr_id];
|
||||
num_exprs += 1;
|
||||
if let Ty::Unknown = ty {
|
||||
num_exprs_unknown += 1;
|
||||
} else {
|
||||
let mut is_partially_unknown = false;
|
||||
ty.walk(&mut |ty| {
|
||||
if let Ty::Unknown = ty {
|
||||
is_partially_unknown = true;
|
||||
}
|
||||
});
|
||||
if is_partially_unknown {
|
||||
num_exprs_partially_unknown += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
bar.inc(1);
|
||||
}
|
||||
bar.finish_and_clear();
|
||||
println!("Total expressions: {}", num_exprs);
|
||||
println!(
|
||||
"Expressions of unknown type: {} ({}%)",
|
||||
num_exprs_unknown,
|
||||
(num_exprs_unknown * 100 / num_exprs)
|
||||
);
|
||||
println!(
|
||||
"Expressions of partially unknown type: {} ({}%)",
|
||||
num_exprs_partially_unknown,
|
||||
(num_exprs_partially_unknown * 100 / num_exprs)
|
||||
);
|
||||
Ok(())
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
mod analysis_stats;
|
||||
|
||||
use std::{fs, io::Read, path::Path, time::Instant};
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
@ -5,10 +7,12 @@
|
||||
use ra_ide_api_light::{extend_selection, file_structure, syntax_tree};
|
||||
use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
|
||||
use tools::collect_tests;
|
||||
use flexi_logger::Logger;
|
||||
|
||||
type Result<T> = ::std::result::Result<T, failure::Error>;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
Logger::with_env().start()?;
|
||||
let matches = App::new("ra-cli")
|
||||
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
@ -23,6 +27,9 @@ fn main() -> Result<()> {
|
||||
.arg(Arg::with_name("start"))
|
||||
.arg(Arg::with_name("end")),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("analysis-stats").arg(Arg::with_name("verbose").short("v")),
|
||||
)
|
||||
.get_matches();
|
||||
match matches.subcommand() {
|
||||
("parse", Some(matches)) => {
|
||||
@ -56,6 +63,10 @@ fn main() -> Result<()> {
|
||||
let sels = selections(&file, start, end);
|
||||
println!("{}", sels)
|
||||
}
|
||||
("analysis-stats", Some(matches)) => {
|
||||
let verbose = matches.is_present("verbose");
|
||||
analysis_stats::run(verbose)?;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
|
@ -112,6 +112,7 @@ pub fn crate_root(&self, crate_id: CrateId) -> FileId {
|
||||
self.arena[&crate_id].file_id
|
||||
}
|
||||
|
||||
// TODO: this only finds one crate with the given root; we could have multiple
|
||||
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
|
||||
let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?;
|
||||
Some(crate_id)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use relative_path::RelativePathBuf;
|
||||
use ra_db::{CrateId, FileId};
|
||||
use ra_db::{CrateId, FileId, SourceRootId};
|
||||
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
@ -16,7 +16,7 @@
|
||||
docs::{Documentation, Docs, docs_from_ast},
|
||||
module_tree::ModuleId,
|
||||
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
|
||||
impl_block::ImplId,
|
||||
impl_block::{ImplId, ImplBlock},
|
||||
resolve::Resolver,
|
||||
};
|
||||
|
||||
@ -44,6 +44,15 @@ pub fn dependencies(&self, db: &impl PersistentHirDatabase) -> Vec<CrateDependen
|
||||
pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
|
||||
self.root_module_impl(db)
|
||||
}
|
||||
|
||||
// TODO: should this be in source_binder?
|
||||
pub fn source_root_crates(
|
||||
db: &impl PersistentHirDatabase,
|
||||
source_root: SourceRootId,
|
||||
) -> Vec<Crate> {
|
||||
let crate_ids = db.source_root_crates(source_root);
|
||||
crate_ids.iter().map(|&crate_id| Crate { crate_id }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -168,6 +177,27 @@ pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||
let item_map = db.item_map(self.krate);
|
||||
Resolver::default().push_module_scope(item_map, *self)
|
||||
}
|
||||
|
||||
pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> {
|
||||
let (lowered_module, _) = db.lower_module(self);
|
||||
lowered_module
|
||||
.declarations
|
||||
.values()
|
||||
.cloned()
|
||||
.flat_map(|per_ns| {
|
||||
per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn impl_blocks(self, db: &impl HirDatabase) -> Vec<ImplBlock> {
|
||||
let module_impl_blocks = db.impls_in_module(self);
|
||||
module_impl_blocks
|
||||
.impls
|
||||
.iter()
|
||||
.map(|(impl_id, _)| ImplBlock::from_id(module_impl_blocks.clone(), impl_id))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Docs for Module {
|
||||
|
@ -70,6 +70,14 @@ pub fn owner(&self) -> Function {
|
||||
self.owner
|
||||
}
|
||||
|
||||
pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> {
|
||||
self.exprs.iter()
|
||||
}
|
||||
|
||||
pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> {
|
||||
self.pats.iter()
|
||||
}
|
||||
|
||||
pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> {
|
||||
db.body_syntax_mapping(self.owner)
|
||||
}
|
||||
|
@ -449,6 +449,41 @@ pub fn unit() -> Self {
|
||||
Ty::Tuple(Arc::new([]))
|
||||
}
|
||||
|
||||
pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
|
||||
f(self);
|
||||
match self {
|
||||
Ty::Slice(t) | Ty::Array(t) => t.walk(f),
|
||||
Ty::RawPtr(t, _) => t.walk(f),
|
||||
Ty::Ref(t, _) => t.walk(f),
|
||||
Ty::Tuple(ts) => {
|
||||
for t in ts.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::FnPtr(sig) => {
|
||||
for input in &sig.input {
|
||||
input.walk(f);
|
||||
}
|
||||
sig.output.walk(f);
|
||||
}
|
||||
Ty::FnDef { substs, sig, .. } => {
|
||||
for input in &sig.input {
|
||||
input.walk(f);
|
||||
}
|
||||
sig.output.walk(f);
|
||||
for t in substs.0.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Adt { substs, .. } => {
|
||||
for t in substs.0.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
|
||||
f(self);
|
||||
match self {
|
||||
|
Loading…
Reference in New Issue
Block a user