@@ -25,16 +25,26 @@ const isMulti = (t: string) => /^\s*\/\/\s*@filename:/im.test(t);
2525
2626let TP = 0 , FN = 0 , FP = 0 , TN = 0 ;
2727const fns : string [ ] = [ ] , fps : string [ ] = [ ] ;
28+ // For each over-accept, remember WHY TS rejected (its first parse diagnostic) so we
29+ // can classify FPs by error kind, not just by directory — `diag` mode prints this.
30+ const fpDiag = new Map < string , { code : number ; msg : string } > ( ) ;
2831for ( const f of walk ( base ) ) {
2932 const code = readFileSync ( f , 'utf8' ) ;
3033 if ( isMulti ( code ) ) continue ;
3134 const sf = ts . createSourceFile ( 't.ts' , code , ts . ScriptTarget . Latest , true , ts . ScriptKind . TS ) ;
32- const tsAccept = ( ( sf as any ) . parseDiagnostics ?. length ?? 0 ) === 0 ;
35+ const diags = ( sf as any ) . parseDiagnostics as ts . Diagnostic [ ] | undefined ;
36+ const tsAccept = ( diags ?. length ?? 0 ) === 0 ;
3337 let weAccept = true ;
3438 try { parse ( code ) ; } catch { weAccept = false ; }
3539 if ( tsAccept && weAccept ) TP ++ ;
3640 else if ( tsAccept && ! weAccept ) { FN ++ ; fns . push ( f . replace ( base + '/' , '' ) ) ; }
37- else if ( ! tsAccept && weAccept ) { FP ++ ; fps . push ( f . replace ( base + '/' , '' ) ) ; }
41+ else if ( ! tsAccept && weAccept ) {
42+ FP ++ ;
43+ const rel = f . replace ( base + '/' , '' ) ;
44+ fps . push ( rel ) ;
45+ const d = diags ! [ 0 ] ;
46+ fpDiag . set ( rel , { code : d . code , msg : ts . flattenDiagnosticMessageText ( d . messageText , ' ' ) } ) ;
47+ }
3848 else TN ++ ;
3949}
4050const total = TP + FN + FP + TN ;
@@ -62,3 +72,22 @@ for (const [dir, files] of groups) {
6272 if ( showFiles ) for ( const f of files ) console . log ( ` ${ f . split ( '/' ) . pop ( ) } ` ) ;
6373}
6474if ( ! showFiles ) console . log ( `\n (re-run with \`fp\` to list every file: node test/conformance-matrix.ts fp)` ) ;
75+
76+ // Over-accepts grouped by TS's OWN reason for rejecting (parse-diagnostic code) → the
77+ // error *kinds* we wave through. This is the classification to triage against: a single
78+ // lexical rule ("digit expected", "invalid escape") often spans many directories, while
79+ // a directory can mix several kinds. `node test/conformance-matrix.ts diag`.
80+ if ( process . argv . includes ( 'diag' ) ) {
81+ const byCode = new Map < number , { msg : string ; files : string [ ] } > ( ) ;
82+ for ( const f of fps ) {
83+ const { code, msg } = fpDiag . get ( f ) ! ;
84+ const e = byCode . get ( code ) ?? byCode . set ( code , { msg, files : [ ] } ) . get ( code ) ! ;
85+ e . files . push ( f ) ;
86+ }
87+ const diagGroups = [ ...byCode . entries ( ) ] . sort ( ( a , b ) => b [ 1 ] . files . length - a [ 1 ] . files . length ) ;
88+ console . log ( `\n OVER-ACCEPTS by TS diagnostic (${ FP } files, ${ diagGroups . length } distinct reasons):` ) ;
89+ for ( const [ code , { msg, files } ] of diagGroups ) {
90+ console . log ( `\n ${ String ( files . length ) . padStart ( 3 ) } TS${ code } : ${ msg } ` ) ;
91+ for ( const f of files ) console . log ( ` ${ f } ` ) ;
92+ }
93+ }
0 commit comments