about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-04 08:31:20 +0000
committerbors <bors@rust-lang.org>2023-03-04 08:31:20 +0000
commit5efcfe5ca983afea02b81c3e413d9064b013c514 (patch)
tree69574b931c4778ba62dbf17d264c7c0a044c31e7
parent4b330f52f6afe46839c5e24b4324969b06c3f439 (diff)
parent800ab650ac7a0833c4a7ca667a84ea8316b6cf3b (diff)
downloadrust-5efcfe5ca983afea02b81c3e413d9064b013c514.tar.gz
rust-5efcfe5ca983afea02b81c3e413d9064b013c514.zip
Auto merge of #14244 - Veykril:metrics, r=lnicola
internal: Report type metrics for patterns

```
❯ cargo run --release -p rust-analyzer -q analysis-stats --memory-usage .
Database loaded:     2.09s, 218mb (metadata 275.07ms, 21mb; build 1.29s, 504kb)
  crates: 44, mods: 916, decls: 19582, fns: 14631
Item Collection:     14.33s, 472mb
  exprs: 420422, ??ty: 68 (0%), ?ty: 141 (0%), !ty: 1
  pats: 82968, ??ty: 11 (0%), ?ty: 10 (0%), !ty: 178
Inference:           47.84s, 745mb
Total:               62.16s, 1217mb
```
We currently have 178 mismatches on r-a itself
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs187
1 files changed, 179 insertions, 8 deletions
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 93297faa664..e8c10927d62 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -12,7 +12,7 @@ use hir::{
 };
 use hir_def::{
     body::{BodySourceMap, SyntheticSyntax},
-    expr::ExprId,
+    expr::{ExprId, PatId},
     FunctionId,
 };
 use hir_ty::{Interner, TyExt, TypeFlags};
@@ -222,7 +222,11 @@ impl flags::AnalysisStats {
         let mut num_exprs = 0;
         let mut num_exprs_unknown = 0;
         let mut num_exprs_partially_unknown = 0;
-        let mut num_type_mismatches = 0;
+        let mut num_expr_type_mismatches = 0;
+        let mut num_pats = 0;
+        let mut num_pats_unknown = 0;
+        let mut num_pats_partially_unknown = 0;
+        let mut num_pat_type_mismatches = 0;
         let analysis = host.analysis();
         for f in funcs.iter().copied() {
             let name = f.name(db);
@@ -255,6 +259,8 @@ impl flags::AnalysisStats {
             let f_id = FunctionId::from(f);
             let (body, sm) = db.body_with_source_map(f_id.into());
             let inference_result = db.infer(f_id.into());
+
+            // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
                 (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
             for (expr_id, _) in body.exprs.iter() {
@@ -307,12 +313,12 @@ impl flags::AnalysisStats {
                 if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
                     println!(
                         r#"{},type,"{}""#,
-                        location_csv(db, &analysis, vfs, &sm, expr_id),
+                        location_csv_expr(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;
+                    num_expr_type_mismatches += 1;
                     if verbosity.is_verbose() {
                         if let Some((path, start, end)) =
                             expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
@@ -339,7 +345,7 @@ impl flags::AnalysisStats {
                     if self.output == Some(OutputFormat::Csv) {
                         println!(
                             r#"{},mismatch,"{}","{}""#,
-                            location_csv(db, &analysis, vfs, &sm, expr_id),
+                            location_csv_expr(db, &analysis, vfs, &sm, expr_id),
                             mismatch.expected.display(db),
                             mismatch.actual.display(db)
                         );
@@ -355,6 +361,109 @@ impl flags::AnalysisStats {
                     num_exprs_partially_unknown - previous_partially_unknown
                 ));
             }
+            // endregion:expressions
+
+            // region:patterns
+            let (previous_pats, previous_unknown, previous_partially_unknown) =
+                (num_pats, num_pats_unknown, num_pats_partially_unknown);
+            for (pat_id, _) in body.pats.iter() {
+                let ty = &inference_result[pat_id];
+                num_pats += 1;
+                let unknown_or_partial = if ty.is_unknown() {
+                    num_pats_unknown += 1;
+                    if verbosity.is_spammy() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Unknown type",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                            ));
+                        } else {
+                            bar.println(format!("{name}: Unknown type",));
+                        }
+                    }
+                    true
+                } else {
+                    let is_partially_unknown =
+                        ty.data(Interner).flags.contains(TypeFlags::HAS_ERROR);
+                    if is_partially_unknown {
+                        num_pats_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 pattern
+                    if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                    {
+                        bar.println(format!(
+                            "{}:{}-{}:{}: {}",
+                            start.line + 1,
+                            start.col,
+                            end.line + 1,
+                            end.col,
+                            ty.display(db)
+                        ));
+                    } else {
+                        bar.println(format!("unknown location: {}", ty.display(db)));
+                    }
+                }
+                if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
+                    println!(
+                        r#"{},type,"{}""#,
+                        location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                        ty.display(db)
+                    );
+                }
+                if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
+                    num_pat_type_mismatches += 1;
+                    if verbosity.is_verbose() {
+                        if let Some((path, start, end)) =
+                            pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
+                        {
+                            bar.println(format!(
+                                "{} {}:{}-{}:{}: Expected {}, got {}",
+                                path,
+                                start.line + 1,
+                                start.col,
+                                end.line + 1,
+                                end.col,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        } else {
+                            bar.println(format!(
+                                "{}: Expected {}, got {}",
+                                name,
+                                mismatch.expected.display(db),
+                                mismatch.actual.display(db)
+                            ));
+                        }
+                    }
+                    if self.output == Some(OutputFormat::Csv) {
+                        println!(
+                            r#"{},mismatch,"{}","{}""#,
+                            location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+                            mismatch.expected.display(db),
+                            mismatch.actual.display(db)
+                        );
+                    }
+                }
+            }
+            if verbosity.is_spammy() {
+                bar.println(format!(
+                    "In {}: {} pats, {} unknown, {} partial",
+                    full_name,
+                    num_pats - previous_pats,
+                    num_pats_unknown - previous_unknown,
+                    num_pats_partially_unknown - previous_partially_unknown
+                ));
+            }
+            // endregion:patterns
             bar.inc(1);
         }
 
@@ -366,10 +475,21 @@ impl flags::AnalysisStats {
             percentage(num_exprs_unknown, num_exprs),
             num_exprs_partially_unknown,
             percentage(num_exprs_partially_unknown, num_exprs),
-            num_type_mismatches
+            num_expr_type_mismatches
+        );
+        eprintln!(
+            "  pats: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
+            num_pats,
+            num_pats_unknown,
+            percentage(num_pats_unknown, num_pats),
+            num_pats_partially_unknown,
+            percentage(num_pats_partially_unknown, num_pats),
+            num_pat_type_mismatches
         );
         report_metric("unknown type", num_exprs_unknown, "#");
-        report_metric("type mismatches", num_type_mismatches, "#");
+        report_metric("type mismatches", num_expr_type_mismatches, "#");
+        report_metric("pattern unknown type", num_pats_unknown, "#");
+        report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
 
         eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
     }
@@ -379,7 +499,7 @@ impl flags::AnalysisStats {
     }
 }
 
-fn location_csv(
+fn location_csv_expr(
     db: &RootDatabase,
     analysis: &Analysis,
     vfs: &Vfs,
@@ -401,6 +521,30 @@ fn location_csv(
     format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
 }
 
+fn location_csv_pat(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> String {
+    let src = match sm.pat_syntax(pat_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.either(|it| it.to_node(&root).syntax().clone(), |it| it.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,
@@ -423,6 +567,33 @@ fn expr_syntax_range(
         None
     }
 }
+fn pat_syntax_range(
+    db: &RootDatabase,
+    analysis: &Analysis,
+    vfs: &Vfs,
+    sm: &BodySourceMap,
+    pat_id: PatId,
+) -> Option<(VfsPath, LineCol, LineCol)> {
+    let src = sm.pat_syntax(pat_id);
+    if let Ok(src) = src {
+        let root = db.parse_or_expand(src.file_id).unwrap();
+        let node = src.map(|e| {
+            e.either(
+                |it| it.to_node(&root).syntax().clone(),
+                |it| it.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()));
+        Some((path, start, end))
+    } else {
+        None
+    }
+}
 
 fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
     for i in 0..slice.len() {