about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-02-25 10:57:24 +0000
committerGitHub <noreply@github.com>2022-02-25 10:57:24 +0000
commit1d5df5ea941ec814649ebe5e16c8c324fdc9cebd (patch)
treea328fd91a1b6027fcd8be96823412acc0167631e
parentf6901c952ec3847ca522c69d4c854e9e0e51d5fe (diff)
parentf807ccd6c0cce77c86cf0f21267e70f9ee89e0a4 (diff)
downloadrust-1d5df5ea941ec814649ebe5e16c8c324fdc9cebd.tar.gz
rust-1d5df5ea941ec814649ebe5e16c8c324fdc9cebd.zip
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 <flodiebold@gmail.com>
-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 55a542c3c16..f52e1e75127 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 b759d912c96..19907ebddb6 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 69b3cb9c175..993c64cceaf 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