From 51e8b8ff14de3507d5e21d80b750577da52b6fdd Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Sun, 14 May 2023 17:12:28 +0330 Subject: [PATCH] Add metrics for unevaluated constants, failed mir bodies, and failed data layouts --- .../rust-analyzer/src/cli/analysis_stats.rs | 99 ++++++++++++++++--- crates/rust-analyzer/src/cli/flags.rs | 6 +- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index cdf40777ba38..c04691ff11ac 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -2,7 +2,6 @@ //! errors. use std::{ - collections::HashMap, env, time::{SystemTime, UNIX_EPOCH}, }; @@ -16,7 +15,7 @@ use hir_def::{ hir::{ExprId, PatId}, FunctionId, }; -use hir_ty::{Interner, TyExt, TypeFlags}; +use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; use ide::{Analysis, AnalysisHost, LineCol, RootDatabase}; use ide_db::base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -122,14 +121,19 @@ impl flags::AnalysisStats { eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut funcs = Vec::new(); + let mut adts = Vec::new(); + let mut consts = 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; - if let ModuleDef::Function(f) = decl { - funcs.push(f); + match decl { + ModuleDef::Function(f) => funcs.push(f), + ModuleDef::Adt(a) => adts.push(a), + ModuleDef::Const(c) => consts.push(c), + _ => (), } } @@ -154,10 +158,13 @@ impl flags::AnalysisStats { self.run_inference(&host, db, &vfs, &funcs, verbosity); } - if self.mir_stats { - self.lower_mir(db, &funcs); + if !self.skip_mir_stats { + self.run_mir_lowering(db, &funcs, verbosity); } + self.run_data_layout(db, &adts, verbosity); + self.run_const_eval(db, &consts, verbosity); + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -193,22 +200,82 @@ impl flags::AnalysisStats { Ok(()) } - fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) { - let all = funcs.len(); + fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) { + let mut all = 0; let mut fail = 0; - let mut h: HashMap = HashMap::new(); - for f in funcs { - let f = FunctionId::from(*f); - let Err(e) = db.mir_body(f.into()) else { + for &a in adts { + if db.generic_params(a.into()).iter().next().is_some() { + // Data types with generics don't have layout. + continue; + } + all += 1; + let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner)) else { continue; }; - let es = format!("{:?}", e); - *h.entry(es).or_default() += 1; + if verbosity.is_spammy() { + let full_name = a + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(a.name(db))) + .join("::"); + println!("Data layout for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all); + report_metric("failed data layouts", fail, "#"); + } + + fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + let mut all = 0; + let mut fail = 0; + for &c in consts { + all += 1; + let Err(e) = c.render_eval(db) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = c + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(c.name(db)) + .join("::"); + println!("Const eval for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all); + report_metric("failed const evals", fail, "#"); + } + + fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) { + let all = funcs.len() as u64; + let mut fail = 0; + for f in funcs { + let Err(e) = db.mir_body(FunctionId::from(*f).into()) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = f + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(f.name(db))) + .join("::"); + println!("Mir body for {full_name} failed due {e:?}"); + } fail += 1; } - let h = h.into_iter().sorted_by_key(|x| x.1).collect::>(); - eprintln!("Mir failed reasons: {:#?}", h); eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); + report_metric("mir failed bodies", fail, "#"); } fn run_inference( diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 891d99c5ad59..6b5a79b431f1 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -66,8 +66,8 @@ xflags::xflags! { optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats - /// Print the number of bodies that fail to lower to mir, in addition to failed reasons. - optional --mir-stats + /// Only type check, skip lowering to mir + optional --skip-mir-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -171,7 +171,7 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, - pub mir_stats: bool, + pub skip_mir_stats: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool,