diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index ea87b0f700..2debbb1ee4 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -218,6 +218,22 @@ impl ItemTree { Attrs::filter(db, krate, self.raw_attrs(of).clone()) } + /// Returns a count of a few, expensive items. + /// + /// For more detail, see [`ItemTreeDataStats`]. + pub fn item_tree_stats(&self) -> ItemTreeDataStats { + match self.data { + Some(ref data) => ItemTreeDataStats { + traits: data.traits.len(), + impls: data.impls.len(), + mods: data.mods.len(), + macro_calls: data.macro_calls.len(), + macro_rules: data.macro_rules.len(), + }, + None => ItemTreeDataStats::default(), + } + } + pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String { pretty::print_item_tree(db, self, edition) } @@ -328,6 +344,15 @@ struct ItemTreeData { vis: ItemVisibilities, } +#[derive(Default, Debug, Eq, PartialEq)] +pub struct ItemTreeDataStats { + pub traits: usize, + pub impls: usize, + pub mods: usize, + pub macro_calls: usize, + pub macro_rules: usize, +} + #[derive(Default, Debug, Eq, PartialEq)] pub struct ItemTreeSourceMaps { all_concatenated: Box<[TypesSourceMap]>, diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 29331000a1..66334e7738 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,7 +2,8 @@ //! errors. use std::{ - env, + env, fmt, + ops::AddAssign, time::{SystemTime, UNIX_EPOCH}, }; @@ -118,29 +119,80 @@ impl flags::AnalysisStats { } let mut item_tree_sw = self.stop_watch(); - let mut num_item_trees = 0; let source_roots = krates .iter() .cloned() .map(|krate| db.file_source_root(krate.root_file(db)).source_root_id(db)) .unique(); + + let mut dep_loc = 0; + let mut workspace_loc = 0; + let mut dep_item_trees = 0; + let mut workspace_item_trees = 0; + + let mut workspace_item_stats = PrettyItemStats::default(); + let mut dep_item_stats = PrettyItemStats::default(); + for source_root_id in source_roots { let source_root = db.source_root(source_root_id).source_root(db); - if !source_root.is_library || self.with_deps { - for file_id in source_root.iter() { - if let Some(p) = source_root.path_for_file(&file_id) { - if let Some((_, Some("rs"))) = p.name_and_extension() { - db.file_item_tree(EditionedFileId::current_edition(file_id).into()); - num_item_trees += 1; + for file_id in source_root.iter() { + if let Some(p) = source_root.path_for_file(&file_id) { + if let Some((_, Some("rs"))) = p.name_and_extension() { + // measure workspace/project code + if !source_root.is_library || self.with_deps { + let length = db.file_text(file_id).text(db).lines().count(); + let item_stats = db + .file_item_tree(EditionedFileId::current_edition(file_id).into()) + .item_tree_stats() + .into(); + + workspace_loc += length; + workspace_item_trees += 1; + workspace_item_stats += item_stats; + } else { + let length = db.file_text(file_id).text(db).lines().count(); + let item_stats = db + .file_item_tree(EditionedFileId::current_edition(file_id).into()) + .item_tree_stats() + .into(); + + dep_loc += length; + dep_item_trees += 1; + dep_item_stats += item_stats; } } } } } - eprintln!(" item trees: {num_item_trees}"); + eprintln!(" item trees: {workspace_item_trees}"); let item_tree_time = item_tree_sw.elapsed(); + + eprintln!( + " dependency lines of code: {}, item trees: {}", + UsizeWithUnderscore(dep_loc), + UsizeWithUnderscore(dep_item_trees), + ); + eprintln!(" dependency item stats: {}", dep_item_stats); + + // FIXME(salsa-transition): bring back stats for ParseQuery (file size) + // and ParseMacroExpansionQuery (macro expansion "file") size whenever we implement + // Salsa's memory usage tracking works with tracked functions. + + // let mut total_file_size = Bytes::default(); + // for e in ide_db::base_db::ParseQuery.in_db(db).entries::>() { + // total_file_size += syntax_len(db.parse(e.key).syntax_node()) + // } + + // let mut total_macro_file_size = Bytes::default(); + // for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::>() { + // let val = db.parse_macro_expansion(e.key).value.0; + // total_macro_file_size += syntax_len(val.syntax_node()) + // } + // eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}"); + eprintln!("{:<20} {}", "Item Tree Collection:", item_tree_time); report_metric("item tree time", item_tree_time.time.as_millis() as u64, "ms"); + eprintln!(" Total Statistics:"); let mut crate_def_map_sw = self.stop_watch(); let mut num_crates = 0; @@ -163,11 +215,16 @@ impl flags::AnalysisStats { shuffle(&mut rng, &mut visit_queue); } - eprint!(" crates: {num_crates}"); + eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut bodies = Vec::new(); let mut adts = Vec::new(); let mut file_ids = Vec::new(); + + let mut num_traits = 0; + let mut num_macro_rules_macros = 0; + let mut num_proc_macros = 0; + while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { file_ids.extend(module.as_source_file_id(db)); @@ -189,6 +246,14 @@ impl flags::AnalysisStats { bodies.push(DefWithBody::from(c)); } ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)), + ModuleDef::Trait(_) => num_traits += 1, + ModuleDef::Macro(m) => match m.kind(db) { + hir::MacroKind::Declarative => num_macro_rules_macros += 1, + hir::MacroKind::Derive + | hir::MacroKind::Attr + | hir::MacroKind::ProcMacro => num_proc_macros += 1, + _ => (), + }, _ => (), }; } @@ -217,6 +282,26 @@ impl flags::AnalysisStats { .filter(|it| matches!(it, DefWithBody::Const(_) | DefWithBody::Static(_))) .count(), ); + + eprintln!(" Workspace:"); + eprintln!( + " traits: {num_traits}, macro_rules macros: {num_macro_rules_macros}, proc_macros: {num_proc_macros}" + ); + eprintln!( + " lines of code: {}, item trees: {}", + UsizeWithUnderscore(workspace_loc), + UsizeWithUnderscore(workspace_item_trees), + ); + eprintln!(" usages: {}", workspace_item_stats); + + eprintln!(" Dependencies:"); + eprintln!( + " lines of code: {}, item trees: {}", + UsizeWithUnderscore(dep_loc), + UsizeWithUnderscore(dep_item_trees), + ); + eprintln!(" declarations: {}", dep_item_stats); + let crate_def_map_time = crate_def_map_sw.elapsed(); eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time); report_metric("crate def map time", crate_def_map_time.time.as_millis() as u64, "ms"); @@ -264,24 +349,6 @@ impl flags::AnalysisStats { } report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB"); - if self.source_stats { - // FIXME(salsa-transition): bring back stats for ParseQuery (file size) - // and ParseMacroExpansionQuery (mcaro expansion "file") size whenever we implement - // Salsa's memory usage tracking works with tracked functions. - - // let mut total_file_size = Bytes::default(); - // for e in ide_db::base_db::ParseQuery.in_db(db).entries::>() { - // total_file_size += syntax_len(db.parse(e.key).syntax_node()) - // } - - // let mut total_macro_file_size = Bytes::default(); - // for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::>() { - // let val = db.parse_macro_expansion(e.key).value.0; - // total_macro_file_size += syntax_len(val.syntax_node()) - // } - // eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}"); - } - if verbosity.is_verbose() { print_memory_usage(host, vfs); } @@ -1217,6 +1284,78 @@ fn percentage(n: u64, total: u64) -> u64 { (n * 100).checked_div(total).unwrap_or(100) } +#[derive(Default, Debug, Eq, PartialEq)] +struct UsizeWithUnderscore(usize); + +impl fmt::Display for UsizeWithUnderscore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let num_str = self.0.to_string(); + + if num_str.len() <= 3 { + return write!(f, "{}", num_str); + } + + let mut result = String::new(); + + for (count, ch) in num_str.chars().rev().enumerate() { + if count > 0 && count % 3 == 0 { + result.push('_'); + } + result.push(ch); + } + + let result = result.chars().rev().collect::(); + write!(f, "{}", result) + } +} + +impl std::ops::AddAssign for UsizeWithUnderscore { + fn add_assign(&mut self, other: UsizeWithUnderscore) { + self.0 += other.0; + } +} + +#[derive(Default, Debug, Eq, PartialEq)] +struct PrettyItemStats { + traits: UsizeWithUnderscore, + impls: UsizeWithUnderscore, + mods: UsizeWithUnderscore, + macro_calls: UsizeWithUnderscore, + macro_rules: UsizeWithUnderscore, +} + +impl From for PrettyItemStats { + fn from(value: hir_def::item_tree::ItemTreeDataStats) -> Self { + Self { + traits: UsizeWithUnderscore(value.traits), + impls: UsizeWithUnderscore(value.impls), + mods: UsizeWithUnderscore(value.mods), + macro_calls: UsizeWithUnderscore(value.macro_calls), + macro_rules: UsizeWithUnderscore(value.macro_rules), + } + } +} + +impl AddAssign for PrettyItemStats { + fn add_assign(&mut self, rhs: Self) { + self.traits += rhs.traits; + self.impls += rhs.impls; + self.mods += rhs.mods; + self.macro_calls += rhs.macro_calls; + self.macro_rules += rhs.macro_rules; + } +} + +impl fmt::Display for PrettyItemStats { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "traits: {}, impl: {}, mods: {}, macro calls: {}, macro rules: {}", + self.traits, self.impls, self.mods, self.macro_calls, self.macro_rules + ) + } +} + // FIXME(salsa-transition): bring this back whenever we implement // Salsa's memory usage tracking to work with tracked functions. // fn syntax_len(node: SyntaxNode) -> usize { diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index ff24602144..13075d4994 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -62,8 +62,6 @@ xflags::xflags! { optional --randomize /// Run type inference in parallel. optional --parallel - /// Print the total length of all source and macro files (whitespace is not counted). - optional --source-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -231,7 +229,6 @@ pub struct AnalysisStats { pub output: Option, pub randomize: bool, pub parallel: bool, - pub source_stats: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool,