Unnamed repository; edit this file 'description' to name the repository.
Merge #11548
11548: Add CSV output to analysis-stats r=flodiebold a=flodiebold For easy diffing, to find changes in unknown types / type mismatches. Co-authored-by: Florian Diebold <[email protected]>
bors[bot] 2022-02-25
parent f6901c9 · parent f807ccd · commit 1d5df5e
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs53
-rw-r--r--crates/rust-analyzer/src/cli/flags.rs21
-rw-r--r--xtask/src/flags.rs6
3 files changed, 74 insertions, 6 deletions
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 55a542c3c1..f52e1e7512 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -10,7 +10,11 @@ use hir::{
db::{AstDatabase, DefDatabase, HirDatabase},
AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
};
-use hir_def::{body::BodySourceMap, expr::ExprId, FunctionId};
+use hir_def::{
+ body::{BodySourceMap, SyntheticSyntax},
+ expr::ExprId,
+ FunctionId,
+};
use hir_ty::{TyExt, TypeWalk};
use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
use ide_db::base_db::{
@@ -28,7 +32,7 @@ use syntax::{AstNode, SyntaxNode};
use vfs::{AbsPathBuf, Vfs, VfsPath};
use crate::cli::{
- flags,
+ flags::{self, OutputFormat},
load_cargo::{load_workspace, LoadCargoConfig},
print_memory_usage,
progress_report::ProgressReport,
@@ -191,7 +195,7 @@ impl flags::AnalysisStats {
) {
let mut bar = match verbosity {
Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
- _ if self.parallel => ProgressReport::hidden(),
+ _ if self.parallel || self.output.is_some() => ProgressReport::hidden(),
_ => ProgressReport::new(funcs.len() as u64),
};
@@ -252,7 +256,7 @@ impl flags::AnalysisStats {
for (expr_id, _) in body.exprs.iter() {
let ty = &inference_result[expr_id];
num_exprs += 1;
- if ty.is_unknown() {
+ let unknown_or_partial = if ty.is_unknown() {
num_exprs_unknown += 1;
if verbosity.is_spammy() {
if let Some((path, start, end)) =
@@ -270,6 +274,7 @@ impl flags::AnalysisStats {
bar.println(format!("{}: Unknown type", name,));
}
}
+ true
} else {
let mut is_partially_unknown = false;
ty.walk(&mut |ty| {
@@ -280,7 +285,8 @@ impl flags::AnalysisStats {
if is_partially_unknown {
num_exprs_partially_unknown += 1;
}
- }
+ is_partially_unknown
+ };
if self.only.is_some() && verbosity.is_spammy() {
// in super-verbose mode for just one function, we print every single expression
if let Some((_, start, end)) =
@@ -298,6 +304,13 @@ impl flags::AnalysisStats {
bar.println(format!("unknown location: {}", ty.display(db)));
}
}
+ if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
+ println!(
+ r#"{},type,"{}""#,
+ location_csv(db, &analysis, vfs, &sm, expr_id),
+ ty.display(db)
+ );
+ }
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
num_type_mismatches += 1;
if verbosity.is_verbose() {
@@ -323,6 +336,14 @@ impl flags::AnalysisStats {
));
}
}
+ if self.output == Some(OutputFormat::Csv) {
+ println!(
+ r#"{},mismatch,"{}","{}""#,
+ location_csv(db, &analysis, vfs, &sm, expr_id),
+ mismatch.expected.display(db),
+ mismatch.actual.display(db)
+ );
+ }
}
}
if verbosity.is_spammy() {
@@ -358,6 +379,28 @@ impl flags::AnalysisStats {
}
}
+fn location_csv(
+ db: &RootDatabase,
+ analysis: &Analysis,
+ vfs: &Vfs,
+ sm: &BodySourceMap,
+ expr_id: ExprId,
+) -> String {
+ let src = match sm.expr_syntax(expr_id) {
+ Ok(s) => s,
+ Err(SyntheticSyntax) => return "synthetic,,".to_string(),
+ };
+ let root = db.parse_or_expand(src.file_id).unwrap();
+ let node = src.map(|e| e.to_node(&root).syntax().clone());
+ let original_range = node.as_ref().original_file_range(db);
+ let path = vfs.file_path(original_range.file_id);
+ let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let text_range = original_range.range;
+ let (start, end) =
+ (line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
+ format!("{},{}:{},{}:{}", path, start.line + 1, start.col, end.line + 1, end.col)
+}
+
fn expr_syntax_range(
db: &RootDatabase,
analysis: &Analysis,
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index b759d912c9..19907ebddb 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -1,6 +1,6 @@
//! Grammar for the command-line arguments.
#![allow(unreachable_pub)]
-use std::path::PathBuf;
+use std::{path::PathBuf, str::FromStr};
use ide_ssr::{SsrPattern, SsrRule};
@@ -54,6 +54,8 @@ xflags::xflags! {
/// Directory with Cargo.toml.
required path: PathBuf
{
+ optional --output format: OutputFormat
+
/// Randomize order in which crates, modules, and items are processed.
optional --randomize
/// Run type inference in parallel.
@@ -160,6 +162,7 @@ pub struct Highlight {
pub struct AnalysisStats {
pub path: PathBuf,
+ pub output: Option<OutputFormat>,
pub randomize: bool,
pub parallel: bool,
pub memory_usage: bool,
@@ -215,6 +218,11 @@ impl RustAnalyzer {
}
// generated end
+#[derive(Debug, PartialEq, Eq)]
+pub enum OutputFormat {
+ Csv,
+}
+
impl RustAnalyzer {
pub fn verbosity(&self) -> Verbosity {
if self.quiet {
@@ -227,3 +235,14 @@ impl RustAnalyzer {
}
}
}
+
+impl FromStr for OutputFormat {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "csv" => Ok(Self::Csv),
+ _ => Err(format!("unknown output format `{}`", s)),
+ }
+ }
+}
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 69b3cb9c17..993c64ccea 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -113,9 +113,15 @@ pub struct Bb {
impl Xtask {
pub const HELP: &'static str = Self::HELP_;
+ #[allow(dead_code)]
pub fn from_env() -> xflags::Result<Self> {
Self::from_env_()
}
+
+ #[allow(dead_code)]
+ pub fn from_vec(args: Vec<std::ffi::OsString>) -> xflags::Result<Self> {
+ Self::from_vec_(args)
+ }
}
// generated end