Skip to content

Commit 2afe50b

Browse files
authored
Merge pull request #19359 from davidbarsky/davidbarsky/more-stats-in-analysis-stats
analysis-stats: emit lines of code and item tree counts for workspace; dependencies
2 parents 3ed13b4 + 19b62b2 commit 2afe50b

File tree

3 files changed

+192
-31
lines changed

3 files changed

+192
-31
lines changed

crates/hir-def/src/item_tree.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,22 @@ impl ItemTree {
218218
Attrs::filter(db, krate, self.raw_attrs(of).clone())
219219
}
220220

221+
/// Returns a count of a few, expensive items.
222+
///
223+
/// For more detail, see [`ItemTreeDataStats`].
224+
pub fn item_tree_stats(&self) -> ItemTreeDataStats {
225+
match self.data {
226+
Some(ref data) => ItemTreeDataStats {
227+
traits: data.traits.len(),
228+
impls: data.impls.len(),
229+
mods: data.mods.len(),
230+
macro_calls: data.macro_calls.len(),
231+
macro_rules: data.macro_rules.len(),
232+
},
233+
None => ItemTreeDataStats::default(),
234+
}
235+
}
236+
221237
pub fn pretty_print(&self, db: &dyn DefDatabase, edition: Edition) -> String {
222238
pretty::print_item_tree(db, self, edition)
223239
}
@@ -328,6 +344,15 @@ struct ItemTreeData {
328344
vis: ItemVisibilities,
329345
}
330346

347+
#[derive(Default, Debug, Eq, PartialEq)]
348+
pub struct ItemTreeDataStats {
349+
pub traits: usize,
350+
pub impls: usize,
351+
pub mods: usize,
352+
pub macro_calls: usize,
353+
pub macro_rules: usize,
354+
}
355+
331356
#[derive(Default, Debug, Eq, PartialEq)]
332357
pub struct ItemTreeSourceMaps {
333358
all_concatenated: Box<[TypesSourceMap]>,

crates/rust-analyzer/src/cli/analysis_stats.rs

Lines changed: 167 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
//! errors.
33
44
use std::{
5-
env,
5+
env, fmt,
6+
ops::AddAssign,
67
time::{SystemTime, UNIX_EPOCH},
78
};
89

@@ -118,29 +119,80 @@ impl flags::AnalysisStats {
118119
}
119120

120121
let mut item_tree_sw = self.stop_watch();
121-
let mut num_item_trees = 0;
122122
let source_roots = krates
123123
.iter()
124124
.cloned()
125125
.map(|krate| db.file_source_root(krate.root_file(db)).source_root_id(db))
126126
.unique();
127+
128+
let mut dep_loc = 0;
129+
let mut workspace_loc = 0;
130+
let mut dep_item_trees = 0;
131+
let mut workspace_item_trees = 0;
132+
133+
let mut workspace_item_stats = PrettyItemStats::default();
134+
let mut dep_item_stats = PrettyItemStats::default();
135+
127136
for source_root_id in source_roots {
128137
let source_root = db.source_root(source_root_id).source_root(db);
129-
if !source_root.is_library || self.with_deps {
130-
for file_id in source_root.iter() {
131-
if let Some(p) = source_root.path_for_file(&file_id) {
132-
if let Some((_, Some("rs"))) = p.name_and_extension() {
133-
db.file_item_tree(EditionedFileId::current_edition(file_id).into());
134-
num_item_trees += 1;
138+
for file_id in source_root.iter() {
139+
if let Some(p) = source_root.path_for_file(&file_id) {
140+
if let Some((_, Some("rs"))) = p.name_and_extension() {
141+
// measure workspace/project code
142+
if !source_root.is_library || self.with_deps {
143+
let length = db.file_text(file_id).text(db).lines().count();
144+
let item_stats = db
145+
.file_item_tree(EditionedFileId::current_edition(file_id).into())
146+
.item_tree_stats()
147+
.into();
148+
149+
workspace_loc += length;
150+
workspace_item_trees += 1;
151+
workspace_item_stats += item_stats;
152+
} else {
153+
let length = db.file_text(file_id).text(db).lines().count();
154+
let item_stats = db
155+
.file_item_tree(EditionedFileId::current_edition(file_id).into())
156+
.item_tree_stats()
157+
.into();
158+
159+
dep_loc += length;
160+
dep_item_trees += 1;
161+
dep_item_stats += item_stats;
135162
}
136163
}
137164
}
138165
}
139166
}
140-
eprintln!(" item trees: {num_item_trees}");
167+
eprintln!(" item trees: {workspace_item_trees}");
141168
let item_tree_time = item_tree_sw.elapsed();
169+
170+
eprintln!(
171+
" dependency lines of code: {}, item trees: {}",
172+
UsizeWithUnderscore(dep_loc),
173+
UsizeWithUnderscore(dep_item_trees),
174+
);
175+
eprintln!(" dependency item stats: {}", dep_item_stats);
176+
177+
// FIXME(salsa-transition): bring back stats for ParseQuery (file size)
178+
// and ParseMacroExpansionQuery (macro expansion "file") size whenever we implement
179+
// Salsa's memory usage tracking works with tracked functions.
180+
181+
// let mut total_file_size = Bytes::default();
182+
// for e in ide_db::base_db::ParseQuery.in_db(db).entries::<Vec<_>>() {
183+
// total_file_size += syntax_len(db.parse(e.key).syntax_node())
184+
// }
185+
186+
// let mut total_macro_file_size = Bytes::default();
187+
// for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::<Vec<_>>() {
188+
// let val = db.parse_macro_expansion(e.key).value.0;
189+
// total_macro_file_size += syntax_len(val.syntax_node())
190+
// }
191+
// eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
192+
142193
eprintln!("{:<20} {}", "Item Tree Collection:", item_tree_time);
143194
report_metric("item tree time", item_tree_time.time.as_millis() as u64, "ms");
195+
eprintln!(" Total Statistics:");
144196

145197
let mut crate_def_map_sw = self.stop_watch();
146198
let mut num_crates = 0;
@@ -163,11 +215,16 @@ impl flags::AnalysisStats {
163215
shuffle(&mut rng, &mut visit_queue);
164216
}
165217

166-
eprint!(" crates: {num_crates}");
218+
eprint!(" crates: {num_crates}");
167219
let mut num_decls = 0;
168220
let mut bodies = Vec::new();
169221
let mut adts = Vec::new();
170222
let mut file_ids = Vec::new();
223+
224+
let mut num_traits = 0;
225+
let mut num_macro_rules_macros = 0;
226+
let mut num_proc_macros = 0;
227+
171228
while let Some(module) = visit_queue.pop() {
172229
if visited_modules.insert(module) {
173230
file_ids.extend(module.as_source_file_id(db));
@@ -189,6 +246,14 @@ impl flags::AnalysisStats {
189246
bodies.push(DefWithBody::from(c));
190247
}
191248
ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
249+
ModuleDef::Trait(_) => num_traits += 1,
250+
ModuleDef::Macro(m) => match m.kind(db) {
251+
hir::MacroKind::Declarative => num_macro_rules_macros += 1,
252+
hir::MacroKind::Derive
253+
| hir::MacroKind::Attr
254+
| hir::MacroKind::ProcMacro => num_proc_macros += 1,
255+
_ => (),
256+
},
192257
_ => (),
193258
};
194259
}
@@ -217,6 +282,26 @@ impl flags::AnalysisStats {
217282
.filter(|it| matches!(it, DefWithBody::Const(_) | DefWithBody::Static(_)))
218283
.count(),
219284
);
285+
286+
eprintln!(" Workspace:");
287+
eprintln!(
288+
" traits: {num_traits}, macro_rules macros: {num_macro_rules_macros}, proc_macros: {num_proc_macros}"
289+
);
290+
eprintln!(
291+
" lines of code: {}, item trees: {}",
292+
UsizeWithUnderscore(workspace_loc),
293+
UsizeWithUnderscore(workspace_item_trees),
294+
);
295+
eprintln!(" usages: {}", workspace_item_stats);
296+
297+
eprintln!(" Dependencies:");
298+
eprintln!(
299+
" lines of code: {}, item trees: {}",
300+
UsizeWithUnderscore(dep_loc),
301+
UsizeWithUnderscore(dep_item_trees),
302+
);
303+
eprintln!(" declarations: {}", dep_item_stats);
304+
220305
let crate_def_map_time = crate_def_map_sw.elapsed();
221306
eprintln!("{:<20} {}", "Item Collection:", crate_def_map_time);
222307
report_metric("crate def map time", crate_def_map_time.time.as_millis() as u64, "ms");
@@ -264,24 +349,6 @@ impl flags::AnalysisStats {
264349
}
265350
report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB");
266351

267-
if self.source_stats {
268-
// FIXME(salsa-transition): bring back stats for ParseQuery (file size)
269-
// and ParseMacroExpansionQuery (mcaro expansion "file") size whenever we implement
270-
// Salsa's memory usage tracking works with tracked functions.
271-
272-
// let mut total_file_size = Bytes::default();
273-
// for e in ide_db::base_db::ParseQuery.in_db(db).entries::<Vec<_>>() {
274-
// total_file_size += syntax_len(db.parse(e.key).syntax_node())
275-
// }
276-
277-
// let mut total_macro_file_size = Bytes::default();
278-
// for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::<Vec<_>>() {
279-
// let val = db.parse_macro_expansion(e.key).value.0;
280-
// total_macro_file_size += syntax_len(val.syntax_node())
281-
// }
282-
// eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
283-
}
284-
285352
if verbosity.is_verbose() {
286353
print_memory_usage(host, vfs);
287354
}
@@ -1217,6 +1284,78 @@ fn percentage(n: u64, total: u64) -> u64 {
12171284
(n * 100).checked_div(total).unwrap_or(100)
12181285
}
12191286

1287+
#[derive(Default, Debug, Eq, PartialEq)]
1288+
struct UsizeWithUnderscore(usize);
1289+
1290+
impl fmt::Display for UsizeWithUnderscore {
1291+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1292+
let num_str = self.0.to_string();
1293+
1294+
if num_str.len() <= 3 {
1295+
return write!(f, "{}", num_str);
1296+
}
1297+
1298+
let mut result = String::new();
1299+
1300+
for (count, ch) in num_str.chars().rev().enumerate() {
1301+
if count > 0 && count % 3 == 0 {
1302+
result.push('_');
1303+
}
1304+
result.push(ch);
1305+
}
1306+
1307+
let result = result.chars().rev().collect::<String>();
1308+
write!(f, "{}", result)
1309+
}
1310+
}
1311+
1312+
impl std::ops::AddAssign for UsizeWithUnderscore {
1313+
fn add_assign(&mut self, other: UsizeWithUnderscore) {
1314+
self.0 += other.0;
1315+
}
1316+
}
1317+
1318+
#[derive(Default, Debug, Eq, PartialEq)]
1319+
struct PrettyItemStats {
1320+
traits: UsizeWithUnderscore,
1321+
impls: UsizeWithUnderscore,
1322+
mods: UsizeWithUnderscore,
1323+
macro_calls: UsizeWithUnderscore,
1324+
macro_rules: UsizeWithUnderscore,
1325+
}
1326+
1327+
impl From<hir_def::item_tree::ItemTreeDataStats> for PrettyItemStats {
1328+
fn from(value: hir_def::item_tree::ItemTreeDataStats) -> Self {
1329+
Self {
1330+
traits: UsizeWithUnderscore(value.traits),
1331+
impls: UsizeWithUnderscore(value.impls),
1332+
mods: UsizeWithUnderscore(value.mods),
1333+
macro_calls: UsizeWithUnderscore(value.macro_calls),
1334+
macro_rules: UsizeWithUnderscore(value.macro_rules),
1335+
}
1336+
}
1337+
}
1338+
1339+
impl AddAssign for PrettyItemStats {
1340+
fn add_assign(&mut self, rhs: Self) {
1341+
self.traits += rhs.traits;
1342+
self.impls += rhs.impls;
1343+
self.mods += rhs.mods;
1344+
self.macro_calls += rhs.macro_calls;
1345+
self.macro_rules += rhs.macro_rules;
1346+
}
1347+
}
1348+
1349+
impl fmt::Display for PrettyItemStats {
1350+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1351+
write!(
1352+
f,
1353+
"traits: {}, impl: {}, mods: {}, macro calls: {}, macro rules: {}",
1354+
self.traits, self.impls, self.mods, self.macro_calls, self.macro_rules
1355+
)
1356+
}
1357+
}
1358+
12201359
// FIXME(salsa-transition): bring this back whenever we implement
12211360
// Salsa's memory usage tracking to work with tracked functions.
12221361
// fn syntax_len(node: SyntaxNode) -> usize {

crates/rust-analyzer/src/cli/flags.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ xflags::xflags! {
6262
optional --randomize
6363
/// Run type inference in parallel.
6464
optional --parallel
65-
/// Print the total length of all source and macro files (whitespace is not counted).
66-
optional --source-stats
6765

6866
/// Only analyze items matching this path.
6967
optional -o, --only path: String
@@ -231,7 +229,6 @@ pub struct AnalysisStats {
231229
pub output: Option<OutputFormat>,
232230
pub randomize: bool,
233231
pub parallel: bool,
234-
pub source_stats: bool,
235232
pub only: Option<String>,
236233
pub with_deps: bool,
237234
pub no_sysroot: bool,

0 commit comments

Comments
 (0)