@@ -12,7 +12,7 @@ use hir::{
1212} ;
1313use hir_def:: {
1414 body:: { BodySourceMap , SyntheticSyntax } ,
15- expr:: ExprId ,
15+ expr:: { ExprId , PatId } ,
1616 FunctionId ,
1717} ;
1818use hir_ty:: { Interner , TyExt , TypeFlags } ;
@@ -222,7 +222,11 @@ impl flags::AnalysisStats {
222222 let mut num_exprs = 0 ;
223223 let mut num_exprs_unknown = 0 ;
224224 let mut num_exprs_partially_unknown = 0 ;
225- let mut num_type_mismatches = 0 ;
225+ let mut num_expr_type_mismatches = 0 ;
226+ let mut num_pats = 0 ;
227+ let mut num_pats_unknown = 0 ;
228+ let mut num_pats_partially_unknown = 0 ;
229+ let mut num_pat_type_mismatches = 0 ;
226230 let analysis = host. analysis ( ) ;
227231 for f in funcs. iter ( ) . copied ( ) {
228232 let name = f. name ( db) ;
@@ -255,6 +259,8 @@ impl flags::AnalysisStats {
255259 let f_id = FunctionId :: from ( f) ;
256260 let ( body, sm) = db. body_with_source_map ( f_id. into ( ) ) ;
257261 let inference_result = db. infer ( f_id. into ( ) ) ;
262+
263+ // region:expressions
258264 let ( previous_exprs, previous_unknown, previous_partially_unknown) =
259265 ( num_exprs, num_exprs_unknown, num_exprs_partially_unknown) ;
260266 for ( expr_id, _) in body. exprs . iter ( ) {
@@ -307,12 +313,12 @@ impl flags::AnalysisStats {
307313 if unknown_or_partial && self . output == Some ( OutputFormat :: Csv ) {
308314 println ! (
309315 r#"{},type,"{}""# ,
310- location_csv ( db, & analysis, vfs, & sm, expr_id) ,
316+ location_csv_expr ( db, & analysis, vfs, & sm, expr_id) ,
311317 ty. display( db)
312318 ) ;
313319 }
314320 if let Some ( mismatch) = inference_result. type_mismatch_for_expr ( expr_id) {
315- num_type_mismatches += 1 ;
321+ num_expr_type_mismatches += 1 ;
316322 if verbosity. is_verbose ( ) {
317323 if let Some ( ( path, start, end) ) =
318324 expr_syntax_range ( db, & analysis, vfs, & sm, expr_id)
@@ -339,7 +345,7 @@ impl flags::AnalysisStats {
339345 if self . output == Some ( OutputFormat :: Csv ) {
340346 println ! (
341347 r#"{},mismatch,"{}","{}""# ,
342- location_csv ( db, & analysis, vfs, & sm, expr_id) ,
348+ location_csv_expr ( db, & analysis, vfs, & sm, expr_id) ,
343349 mismatch. expected. display( db) ,
344350 mismatch. actual. display( db)
345351 ) ;
@@ -355,6 +361,109 @@ impl flags::AnalysisStats {
355361 num_exprs_partially_unknown - previous_partially_unknown
356362 ) ) ;
357363 }
364+ // endregion:expressions
365+
366+ // region:patterns
367+ let ( previous_pats, previous_unknown, previous_partially_unknown) =
368+ ( num_pats, num_pats_unknown, num_pats_partially_unknown) ;
369+ for ( pat_id, _) in body. pats . iter ( ) {
370+ let ty = & inference_result[ pat_id] ;
371+ num_pats += 1 ;
372+ let unknown_or_partial = if ty. is_unknown ( ) {
373+ num_pats_unknown += 1 ;
374+ if verbosity. is_spammy ( ) {
375+ if let Some ( ( path, start, end) ) =
376+ pat_syntax_range ( db, & analysis, vfs, & sm, pat_id)
377+ {
378+ bar. println ( format ! (
379+ "{} {}:{}-{}:{}: Unknown type" ,
380+ path,
381+ start. line + 1 ,
382+ start. col,
383+ end. line + 1 ,
384+ end. col,
385+ ) ) ;
386+ } else {
387+ bar. println ( format ! ( "{name}: Unknown type" , ) ) ;
388+ }
389+ }
390+ true
391+ } else {
392+ let is_partially_unknown =
393+ ty. data ( Interner ) . flags . contains ( TypeFlags :: HAS_ERROR ) ;
394+ if is_partially_unknown {
395+ num_pats_partially_unknown += 1 ;
396+ }
397+ is_partially_unknown
398+ } ;
399+ if self . only . is_some ( ) && verbosity. is_spammy ( ) {
400+ // in super-verbose mode for just one function, we print every single pattern
401+ if let Some ( ( _, start, end) ) = pat_syntax_range ( db, & analysis, vfs, & sm, pat_id)
402+ {
403+ bar. println ( format ! (
404+ "{}:{}-{}:{}: {}" ,
405+ start. line + 1 ,
406+ start. col,
407+ end. line + 1 ,
408+ end. col,
409+ ty. display( db)
410+ ) ) ;
411+ } else {
412+ bar. println ( format ! ( "unknown location: {}" , ty. display( db) ) ) ;
413+ }
414+ }
415+ if unknown_or_partial && self . output == Some ( OutputFormat :: Csv ) {
416+ println ! (
417+ r#"{},type,"{}""# ,
418+ location_csv_pat( db, & analysis, vfs, & sm, pat_id) ,
419+ ty. display( db)
420+ ) ;
421+ }
422+ if let Some ( mismatch) = inference_result. type_mismatch_for_pat ( pat_id) {
423+ num_pat_type_mismatches += 1 ;
424+ if verbosity. is_verbose ( ) {
425+ if let Some ( ( path, start, end) ) =
426+ pat_syntax_range ( db, & analysis, vfs, & sm, pat_id)
427+ {
428+ bar. println ( format ! (
429+ "{} {}:{}-{}:{}: Expected {}, got {}" ,
430+ path,
431+ start. line + 1 ,
432+ start. col,
433+ end. line + 1 ,
434+ end. col,
435+ mismatch. expected. display( db) ,
436+ mismatch. actual. display( db)
437+ ) ) ;
438+ } else {
439+ bar. println ( format ! (
440+ "{}: Expected {}, got {}" ,
441+ name,
442+ mismatch. expected. display( db) ,
443+ mismatch. actual. display( db)
444+ ) ) ;
445+ }
446+ }
447+ if self . output == Some ( OutputFormat :: Csv ) {
448+ println ! (
449+ r#"{},mismatch,"{}","{}""# ,
450+ location_csv_pat( db, & analysis, vfs, & sm, pat_id) ,
451+ mismatch. expected. display( db) ,
452+ mismatch. actual. display( db)
453+ ) ;
454+ }
455+ }
456+ }
457+ if verbosity. is_spammy ( ) {
458+ bar. println ( format ! (
459+ "In {}: {} pats, {} unknown, {} partial" ,
460+ full_name,
461+ num_pats - previous_pats,
462+ num_pats_unknown - previous_unknown,
463+ num_pats_partially_unknown - previous_partially_unknown
464+ ) ) ;
465+ }
466+ // endregion:patterns
358467 bar. inc ( 1 ) ;
359468 }
360469
@@ -366,10 +475,21 @@ impl flags::AnalysisStats {
366475 percentage( num_exprs_unknown, num_exprs) ,
367476 num_exprs_partially_unknown,
368477 percentage( num_exprs_partially_unknown, num_exprs) ,
369- num_type_mismatches
478+ num_expr_type_mismatches
479+ ) ;
480+ eprintln ! (
481+ " pats: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}" ,
482+ num_pats,
483+ num_pats_unknown,
484+ percentage( num_pats_unknown, num_pats) ,
485+ num_pats_partially_unknown,
486+ percentage( num_pats_partially_unknown, num_pats) ,
487+ num_pat_type_mismatches
370488 ) ;
371489 report_metric ( "unknown type" , num_exprs_unknown, "#" ) ;
372- report_metric ( "type mismatches" , num_type_mismatches, "#" ) ;
490+ report_metric ( "type mismatches" , num_expr_type_mismatches, "#" ) ;
491+ report_metric ( "pattern unknown type" , num_pats_unknown, "#" ) ;
492+ report_metric ( "pattern type mismatches" , num_pat_type_mismatches, "#" ) ;
373493
374494 eprintln ! ( "{:<20} {}" , "Inference:" , inference_sw. elapsed( ) ) ;
375495 }
@@ -379,7 +499,7 @@ impl flags::AnalysisStats {
379499 }
380500}
381501
382- fn location_csv (
502+ fn location_csv_expr (
383503 db : & RootDatabase ,
384504 analysis : & Analysis ,
385505 vfs : & Vfs ,
@@ -401,6 +521,30 @@ fn location_csv(
401521 format ! ( "{path},{}:{},{}:{}" , start. line + 1 , start. col, end. line + 1 , end. col)
402522}
403523
524+ fn location_csv_pat (
525+ db : & RootDatabase ,
526+ analysis : & Analysis ,
527+ vfs : & Vfs ,
528+ sm : & BodySourceMap ,
529+ pat_id : PatId ,
530+ ) -> String {
531+ let src = match sm. pat_syntax ( pat_id) {
532+ Ok ( s) => s,
533+ Err ( SyntheticSyntax ) => return "synthetic,," . to_string ( ) ,
534+ } ;
535+ let root = db. parse_or_expand ( src. file_id ) . unwrap ( ) ;
536+ let node = src. map ( |e| {
537+ e. either ( |it| it. to_node ( & root) . syntax ( ) . clone ( ) , |it| it. to_node ( & root) . syntax ( ) . clone ( ) )
538+ } ) ;
539+ let original_range = node. as_ref ( ) . original_file_range ( db) ;
540+ let path = vfs. file_path ( original_range. file_id ) ;
541+ let line_index = analysis. file_line_index ( original_range. file_id ) . unwrap ( ) ;
542+ let text_range = original_range. range ;
543+ let ( start, end) =
544+ ( line_index. line_col ( text_range. start ( ) ) , line_index. line_col ( text_range. end ( ) ) ) ;
545+ format ! ( "{path},{}:{},{}:{}" , start. line + 1 , start. col, end. line + 1 , end. col)
546+ }
547+
404548fn expr_syntax_range (
405549 db : & RootDatabase ,
406550 analysis : & Analysis ,
@@ -423,6 +567,33 @@ fn expr_syntax_range(
423567 None
424568 }
425569}
570+ fn pat_syntax_range (
571+ db : & RootDatabase ,
572+ analysis : & Analysis ,
573+ vfs : & Vfs ,
574+ sm : & BodySourceMap ,
575+ pat_id : PatId ,
576+ ) -> Option < ( VfsPath , LineCol , LineCol ) > {
577+ let src = sm. pat_syntax ( pat_id) ;
578+ if let Ok ( src) = src {
579+ let root = db. parse_or_expand ( src. file_id ) . unwrap ( ) ;
580+ let node = src. map ( |e| {
581+ e. either (
582+ |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
583+ |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
584+ )
585+ } ) ;
586+ let original_range = node. as_ref ( ) . original_file_range ( db) ;
587+ let path = vfs. file_path ( original_range. file_id ) ;
588+ let line_index = analysis. file_line_index ( original_range. file_id ) . unwrap ( ) ;
589+ let text_range = original_range. range ;
590+ let ( start, end) =
591+ ( line_index. line_col ( text_range. start ( ) ) , line_index. line_col ( text_range. end ( ) ) ) ;
592+ Some ( ( path, start, end) )
593+ } else {
594+ None
595+ }
596+ }
426597
427598fn shuffle < T > ( rng : & mut Rand32 , slice : & mut [ T ] ) {
428599 for i in 0 ..slice. len ( ) {
0 commit comments