Report type metrics for patterns

This commit is contained in:
Lukas Wirth 2023-03-03 21:06:48 +01:00
parent 7c092a13bf
commit c12fac698f

View File

@ -12,7 +12,7 @@ use hir::{
}; };
use hir_def::{ use hir_def::{
body::{BodySourceMap, SyntheticSyntax}, body::{BodySourceMap, SyntheticSyntax},
expr::ExprId, expr::{ExprId, PatId},
FunctionId, FunctionId,
}; };
use hir_ty::{Interner, TyExt, TypeFlags}; use hir_ty::{Interner, TyExt, TypeFlags};
@ -222,7 +222,11 @@ impl flags::AnalysisStats {
let mut num_exprs = 0; let mut num_exprs = 0;
let mut num_exprs_unknown = 0; let mut num_exprs_unknown = 0;
let mut num_exprs_partially_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(); let analysis = host.analysis();
for f in funcs.iter().copied() { for f in funcs.iter().copied() {
let name = f.name(db); let name = f.name(db);
@ -255,6 +259,8 @@ impl flags::AnalysisStats {
let f_id = FunctionId::from(f); let f_id = FunctionId::from(f);
let (body, sm) = db.body_with_source_map(f_id.into()); let (body, sm) = db.body_with_source_map(f_id.into());
let inference_result = db.infer(f_id.into()); let inference_result = db.infer(f_id.into());
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) = let (previous_exprs, previous_unknown, previous_partially_unknown) =
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown); (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
for (expr_id, _) in body.exprs.iter() { for (expr_id, _) in body.exprs.iter() {
@ -307,12 +313,12 @@ impl flags::AnalysisStats {
if unknown_or_partial && self.output == Some(OutputFormat::Csv) { if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
println!( println!(
r#"{},type,"{}""#, r#"{},type,"{}""#,
location_csv(db, &analysis, vfs, &sm, expr_id), location_csv_expr(db, &analysis, vfs, &sm, expr_id),
ty.display(db) ty.display(db)
); );
} }
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) { 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 verbosity.is_verbose() {
if let Some((path, start, end)) = if let Some((path, start, end)) =
expr_syntax_range(db, &analysis, vfs, &sm, expr_id) expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
@ -339,7 +345,7 @@ impl flags::AnalysisStats {
if self.output == Some(OutputFormat::Csv) { if self.output == Some(OutputFormat::Csv) {
println!( println!(
r#"{},mismatch,"{}","{}""#, r#"{},mismatch,"{}","{}""#,
location_csv(db, &analysis, vfs, &sm, expr_id), location_csv_expr(db, &analysis, vfs, &sm, expr_id),
mismatch.expected.display(db), mismatch.expected.display(db),
mismatch.actual.display(db) mismatch.actual.display(db)
); );
@ -355,6 +361,109 @@ impl flags::AnalysisStats {
num_exprs_partially_unknown - previous_partially_unknown 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); bar.inc(1);
} }
@ -366,10 +475,19 @@ impl flags::AnalysisStats {
percentage(num_exprs_unknown, num_exprs), percentage(num_exprs_unknown, num_exprs),
num_exprs_partially_unknown, num_exprs_partially_unknown,
percentage(num_exprs_partially_unknown, num_exprs), percentage(num_exprs_partially_unknown, num_exprs),
num_type_mismatches num_expr_type_mismatches
); );
report_metric("unknown type", num_exprs_unknown, "#"); eprintln!(
report_metric("type mismatches", num_type_mismatches, "#"); " 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 + num_pats_unknown, "#");
report_metric("type mismatches", num_expr_type_mismatches + num_pat_type_mismatches, "#");
eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
} }
@ -379,7 +497,7 @@ impl flags::AnalysisStats {
} }
} }
fn location_csv( fn location_csv_expr(
db: &RootDatabase, db: &RootDatabase,
analysis: &Analysis, analysis: &Analysis,
vfs: &Vfs, vfs: &Vfs,
@ -401,6 +519,30 @@ fn location_csv(
format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col) 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( fn expr_syntax_range(
db: &RootDatabase, db: &RootDatabase,
analysis: &Analysis, analysis: &Analysis,
@ -423,6 +565,33 @@ fn expr_syntax_range(
None 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]) { fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
for i in 0..slice.len() { for i in 0..slice.len() {