From 6964a88e8c90f06220498d3e9194b7e2073c1e32 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 9 Feb 2019 18:27:11 +0100 Subject: 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. --- Cargo.lock | 27 ++++++++++ crates/ra_batch/src/lib.rs | 9 +++- crates/ra_cli/Cargo.toml | 6 +++ crates/ra_cli/src/analysis_stats.rs | 100 ++++++++++++++++++++++++++++++++++++ crates/ra_cli/src/main.rs | 11 ++++ crates/ra_db/src/input.rs | 1 + crates/ra_hir/src/code_model_api.rs | 34 +++++++++++- crates/ra_hir/src/expr.rs | 8 +++ crates/ra_hir/src/ty.rs | 35 +++++++++++++ 9 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 crates/ra_cli/src/analysis_stats.rs diff --git a/Cargo.lock b/Cargo.lock index 74a77ddc9..0a5bec2bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index ea91d88b7..014663546 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs @@ -60,7 +60,11 @@ impl 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 @@ impl BatchDatabase { 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(); diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index eb1722d5e..641ac5cbd 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml @@ -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" } diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs new file mode 100644 index 000000000..a46ac974d --- /dev/null +++ b/crates/ra_cli/src/analysis_stats.rs @@ -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(()) +} diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index a4debeb48..72e6ae4d5 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs @@ -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 join_to_string::join; 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 = ::std::result::Result; 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(()) diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 405634fe0..8decc65c5 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -112,6 +112,7 @@ impl CrateGraph { 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 { let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?; Some(crate_id) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index cafc5279d..19f103855 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -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 @@ use crate::{ 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 @@ impl Crate { pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option { self.root_module_impl(db) } + + // TODO: should this be in source_binder? + pub fn source_root_crates( + db: &impl PersistentHirDatabase, + source_root: SourceRootId, + ) -> Vec { + 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 @@ impl Module { let item_map = db.item_map(self.krate); Resolver::default().push_module_scope(item_map, *self) } + + pub fn declarations(self, db: &impl HirDatabase) -> Vec { + 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 { + 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 { diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index b30e11abb..4e73590d0 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -70,6 +70,14 @@ impl Body { self.owner } + pub fn exprs(&self) -> impl Iterator { + self.exprs.iter() + } + + pub fn pats(&self) -> impl Iterator { + self.pats.iter() + } + pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc { db.body_syntax_mapping(self.owner) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 7203a8a10..08561573b 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -449,6 +449,41 @@ impl Ty { 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 { -- cgit v1.2.3