Skip to main content

tsz_solver/
unsoundness_audit.rs

1//! TypeScript Unsoundness Catalog Implementation Audit
2//!
3//! This module tracks the implementation status of all 44 TypeScript unsoundness
4//! rules from `docs/specs/TS_UNSOUNDNESS_CATALOG.md` against the actual solver implementation.
5//!
6//! ## Audit Matrix
7//!
8//! The audit maps each catalog rule to its implementation status:
9//! - ✅ `FULLY_IMPLEMENTED`: Rule is complete and tested
10//! - ⚠️ `PARTIALLY_IMPLEMENTED`: Partial implementation with gaps
11//! - ❌ `NOT_IMPLEMENTED`: Rule is missing
12//! - 🚫 BLOCKED: Rule cannot be implemented yet (dependencies missing)
13//!
14//! ## Usage
15//!
16//! ```rust,ignore
17//! use wasm::solver::unsoundness_audit::UnsoundnessAudit;
18//!
19//! let audit = UnsoundnessAudit::new();
20//! let status = audit.get_rule_status(7); // Open Numeric Enums
21//! println!("Rule #7: {:?}", status);
22//! ```
23
24use rustc_hash::FxHashMap;
25use std::fmt;
26
27/// Implementation status of a single catalog rule
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ImplementationStatus {
30    /// Rule is fully implemented with comprehensive tests
31    FullyImplemented,
32    /// Rule is partially implemented - core logic exists but gaps remain
33    PartiallyImplemented,
34    /// Rule is not implemented at all
35    NotImplemented,
36    /// Rule is blocked by missing dependencies (e.g., type system features)
37    Blocked,
38    /// Rule is N/A for this compiler (different architecture)
39    NotApplicable,
40}
41
42impl ImplementationStatus {
43    /// Returns true if the rule is at least partially implemented
44    pub const fn is_implemented(self) -> bool {
45        matches!(self, Self::FullyImplemented | Self::PartiallyImplemented)
46    }
47
48    /// Returns the completion percentage (0.0, 0.5, or 1.0)
49    pub const fn completion_ratio(self) -> f32 {
50        match self {
51            Self::FullyImplemented | Self::NotApplicable => 1.0,
52            Self::PartiallyImplemented => 0.5,
53            Self::NotImplemented | Self::Blocked => 0.0,
54        }
55    }
56
57    /// Returns the emoji representation for display
58    pub const fn emoji(self) -> &'static str {
59        match self {
60            Self::FullyImplemented => "✅",
61            Self::PartiallyImplemented => "⚠️",
62            Self::NotImplemented => "❌",
63            Self::Blocked => "🚫",
64            Self::NotApplicable => "➖",
65        }
66    }
67}
68
69/// Phase categorization from the catalog
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ImplementationPhase {
72    /// Phase 1: Hello World Barrier (Bootstrapping)
73    /// Required to compile lib.d.ts and basic variables
74    Phase1,
75    /// Phase 2: Business Logic Barrier (Common Patterns)
76    /// Required for standard application code
77    Phase2,
78    /// Phase 3: Library Barrier (Complex Types)
79    /// Required for modern npm packages
80    Phase3,
81    /// Phase 4: Feature Barrier (Edge Cases)
82    /// Required for 100% test suite compliance
83    Phase4,
84}
85
86impl fmt::Display for ImplementationPhase {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            Self::Phase1 => write!(f, "Phase 1: Hello World"),
90            Self::Phase2 => write!(f, "Phase 2: Business Logic"),
91            Self::Phase3 => write!(f, "Phase 3: Library"),
92            Self::Phase4 => write!(f, "Phase 4: Feature"),
93        }
94    }
95}
96
97/// Information about a single catalog rule implementation
98#[derive(Debug, Clone)]
99pub struct RuleImplementation {
100    /// Rule number from the catalog (1-44)
101    pub rule_number: u8,
102    /// Rule name/title
103    pub name: &'static str,
104    /// Implementation phase from catalog
105    pub phase: ImplementationPhase,
106    /// Current implementation status
107    pub status: ImplementationStatus,
108    /// File(s) where this rule is implemented
109    pub implementation_files: Vec<&'static str>,
110    /// Test coverage percentage (estimated)
111    pub test_coverage: f32,
112    /// Dependencies on other rules (by rule number)
113    pub dependencies: Vec<u8>,
114    /// Notes about implementation gaps
115    pub notes: &'static str,
116}
117
118/// Audit report for the TypeScript unsoundness catalog
119#[derive(Debug, Clone)]
120pub struct UnsoundnessAudit {
121    rules: FxHashMap<u8, RuleImplementation>,
122}
123
124impl UnsoundnessAudit {
125    /// Create a new audit with the current implementation status
126    pub fn new() -> Self {
127        let mut rules = FxHashMap::default();
128
129        // =========================================================================
130        // PHASE 1: Hello World Barrier (Bootstrapping)
131        // =========================================================================
132
133        rules.insert(1, RuleImplementation {
134            rule_number: 1,
135            name: "The \"Any\" Type",
136            phase: ImplementationPhase::Phase1,
137            status: ImplementationStatus::FullyImplemented,
138            implementation_files: vec!["src/solver/lawyer.rs", "src/solver/compat.rs"],
139            test_coverage: 0.95,
140            dependencies: vec![],
141            notes: "Fully implemented in lawyer.rs with AnyPropagationRules. Handles top/bottom semantics and suppression rules.",
142        });
143
144        rules.insert(20, RuleImplementation {
145            rule_number: 20,
146            name: "The `Object` vs `object` vs `{}` Trifecta",
147            phase: ImplementationPhase::Phase1,
148            status: ImplementationStatus::FullyImplemented,
149            implementation_files: vec!["src/solver/compat.rs", "src/solver/subtype.rs"],
150            test_coverage: 0.85,
151            dependencies: vec![12],
152            notes: "FULLY IMPLEMENTED. All three variants: {} accepts everything except null/undefined, lowercase object rejects primitives, global Object interface accepts everything (including primitives). Tests: test_object_trifecta_assignability, test_object_trifecta_subtyping",
153        });
154
155        rules.insert(6, RuleImplementation {
156            rule_number: 6,
157            name: "Void Return Exception",
158            phase: ImplementationPhase::Phase1,
159            status: ImplementationStatus::FullyImplemented,
160            implementation_files: vec!["src/solver/subtype.rs"],
161            test_coverage: 0.90,
162            dependencies: vec![],
163            notes: "allow_void_return flag in SubtypeChecker. Functions returning void accept non-void returns.",
164        });
165
166        rules.insert(11, RuleImplementation {
167            rule_number: 11,
168            name: "Error Poisoning",
169            phase: ImplementationPhase::Phase1,
170            status: ImplementationStatus::FullyImplemented,
171            implementation_files: vec!["src/solver/intern.rs", "src/solver/compat.rs", "src/solver/subtype.rs"],
172            test_coverage: 0.90,
173            dependencies: vec![],
174            notes: "FULLY IMPLEMENTED. Union(Error, T) suppression in intern.rs (lines 664-666). Error types don't silently pass checks in compat.rs. Prevents cascading errors.",
175        });
176
177        rules.insert(3, RuleImplementation {
178            rule_number: 3,
179            name: "Covariant Mutable Arrays",
180            phase: ImplementationPhase::Phase1,
181            status: ImplementationStatus::FullyImplemented,
182            implementation_files: vec!["src/solver/subtype.rs"],
183            test_coverage: 0.95,
184            dependencies: vec![],
185            notes: "Array covariance implemented in subtype.rs (line 572-575). Dog[] assignable to Animal[] despite unsafety.",
186        });
187
188        // =========================================================================
189        // PHASE 2: Business Logic Barrier (Common Patterns)
190        // =========================================================================
191
192        rules.insert(2, RuleImplementation {
193            rule_number: 2,
194            name: "Function Bivariance",
195            phase: ImplementationPhase::Phase2,
196            status: ImplementationStatus::FullyImplemented,
197            implementation_files: vec!["src/solver/subtype.rs", "src/solver/subtype_rules/functions.rs", "src/solver/types.rs"],
198            test_coverage: 0.85,
199            dependencies: vec![],
200            notes: "FULLY IMPLEMENTED. CallSignature has is_method field. Methods use bivariant parameter checking while standalone functions use contravariant checking under strictFunctionTypes.",
201        });
202
203        rules.insert(4, RuleImplementation {
204            rule_number: 4,
205            name: "Freshness / Excess Property Checks",
206            phase: ImplementationPhase::Phase2,
207            status: ImplementationStatus::FullyImplemented,
208            implementation_files: vec!["src/solver/lawyer.rs", "src/checker/state.rs"],
209            test_coverage: 0.85,
210            dependencies: vec![],
211            notes: "FULLY IMPLEMENTED. Freshness is tracked on the TypeId via ObjectFlags, with object literals interning to fresh shapes and widening removing freshness on assignment. Sound Mode uses StickyFreshnessTracker (checker-side) for binding-level tracking.",
212        });
213
214        rules.insert(10, RuleImplementation {
215            rule_number: 10,
216            name: "Literal Widening",
217            phase: ImplementationPhase::Phase2,
218            status: ImplementationStatus::FullyImplemented,
219            implementation_files: vec!["src/solver/subtype_rules/literals.rs"],
220            test_coverage: 0.85,
221            dependencies: vec![],
222            notes: "Implemented in check_literal_to_intrinsic(). String literals widen to string, number literals to number, etc.",
223        });
224
225        rules.insert(19, RuleImplementation {
226            rule_number: 19,
227            name: "Covariant `this` Types",
228            phase: ImplementationPhase::Phase2,
229            status: ImplementationStatus::FullyImplemented,
230            implementation_files: vec!["src/solver/subtype.rs", "src/solver/subtype_rules/functions.rs"],
231            test_coverage: 0.80,
232            dependencies: vec![],
233            notes: "Implemented via type_contains_this_type() detection. When `this` is in method parameters, covariance is used.",
234        });
235
236        rules.insert(14, RuleImplementation {
237            rule_number: 14,
238            name: "Optionality vs Undefined",
239            phase: ImplementationPhase::Phase2,
240            status: ImplementationStatus::FullyImplemented,
241            implementation_files: vec!["src/solver/compat.rs", "src/solver/subtype.rs"],
242            test_coverage: 0.85,
243            dependencies: vec![],
244            notes: "exact_optional_property_types flag in SubtypeChecker. Distinguishes {k?: T} from {k: T | undefined}.",
245        });
246
247        // =========================================================================
248        // PHASE 3: Library Barrier (Complex Types)
249        // =========================================================================
250
251        rules.insert(25, RuleImplementation {
252            rule_number: 25,
253            name: "Index Signature Consistency",
254            phase: ImplementationPhase::Phase3,
255            status: ImplementationStatus::FullyImplemented,
256            implementation_files: vec!["src/solver/subtype_rules/objects.rs"],
257            test_coverage: 0.85,
258            dependencies: vec![],
259            notes: "Implemented in check_object_with_index_subtype(). Validates string/number index compatibility and property-vs-index satisfaction.",
260        });
261
262        rules.insert(40, RuleImplementation {
263            rule_number: 40,
264            name: "Distributivity Disabling (`[T] extends [U]`)",
265            phase: ImplementationPhase::Phase3,
266            status: ImplementationStatus::FullyImplemented,
267            implementation_files: vec!["src/solver/lower.rs", "src/solver/evaluate_rules/conditional.rs"],
268            test_coverage: 0.85,
269            dependencies: vec![],
270            notes: "FULLY IMPLEMENTED. is_naked_type_param() in lower.rs (lines 1410-1456) returns false for Tuple types, preventing distributivity. Test: test_conditional_tuple_wrapper_no_distribution_assignable()",
271        });
272
273        rules.insert(30, RuleImplementation {
274            rule_number: 30,
275            name: "`keyof` Contravariance (Set Inversion)",
276            phase: ImplementationPhase::Phase3,
277            status: ImplementationStatus::FullyImplemented,
278            implementation_files: vec!["src/solver/evaluate.rs", "src/solver/evaluate_rules/keyof.rs", "src/solver/subtype.rs"],
279            test_coverage: 0.85,
280            dependencies: vec![],
281            notes: "FULLY IMPLEMENTED. keyof evaluation in keyof.rs. Union -> Intersection inversion: keyof (A | B) becomes (keyof A) & (keyof B).",
282        });
283
284        rules.insert(21, RuleImplementation {
285            rule_number: 21,
286            name: "Intersection Reduction (Reduction to `never`)",
287            phase: ImplementationPhase::Phase3,
288            status: ImplementationStatus::FullyImplemented,
289            implementation_files: vec!["src/solver/evaluate.rs", "src/solver/intern.rs"],
290            test_coverage: 0.85,
291            dependencies: vec![],
292            notes: "FULLY IMPLEMENTED. Primitive intersection reduces to never. property_types_disjoint() in intern.rs detects incompatible object property types (e.g., {a: string} & {a: number} -> never).",
293        });
294
295        rules.insert(41, RuleImplementation {
296            rule_number: 41,
297            name: "Key Remapping & Filtering (`as never`)",
298            phase: ImplementationPhase::Phase3,
299            status: ImplementationStatus::FullyImplemented,
300            implementation_files: vec!["src/solver/evaluate_rules/mapped.rs"],
301            test_coverage: 0.85,
302            dependencies: vec![],
303            notes: "FULLY IMPLEMENTED. remap_key_type() in mapped.rs (lines 65-78) returns Ok(None) when key evaluates to Never, which causes property to be skipped (line 151). This implements Omit utility type.",
304        });
305
306        // =========================================================================
307        // PHASE 4: Feature Barrier (Edge Cases)
308        // =========================================================================
309
310        // ENUMS
311        rules.insert(7, RuleImplementation {
312            rule_number: 7,
313            name: "Open Numeric Enums",
314            phase: ImplementationPhase::Phase4,
315            status: ImplementationStatus::FullyImplemented,
316            implementation_files: vec!["src/checker/state.rs", "src/checker/enum_checker.rs"],
317            test_coverage: 0.85,
318            dependencies: vec![],
319            notes: "Implemented in enum_assignability_override(). Numeric enums are bidirectionally assignable to number.",
320        });
321
322        rules.insert(24, RuleImplementation {
323            rule_number: 24,
324            name: "Cross-Enum Incompatibility (The Nominal Enum Rule)",
325            phase: ImplementationPhase::Phase4,
326            status: ImplementationStatus::FullyImplemented,
327            implementation_files: vec!["src/checker/state.rs"],
328            test_coverage: 0.85,
329            dependencies: vec![7],
330            notes: "Implemented in enum_assignability_override(). Different enum types with same values are rejected via nominal symbol comparison.",
331        });
332
333        rules.insert(34, RuleImplementation {
334            rule_number: 34,
335            name: "String Enums (Strict Opaque Types)",
336            phase: ImplementationPhase::Phase4,
337            status: ImplementationStatus::FullyImplemented,
338            implementation_files: vec!["src/checker/state.rs"],
339            test_coverage: 0.85,
340            dependencies: vec![],
341            notes: "Implemented in enum_assignability_override(). String enums are opaque - string literals and STRING are NOT assignable to string enum types.",
342        });
343
344        // CLASSES
345        rules.insert(5, RuleImplementation {
346            rule_number: 5,
347            name: "Nominal Classes (Private Members)",
348            phase: ImplementationPhase::Phase4,
349            status: ImplementationStatus::FullyImplemented,
350            implementation_files: vec!["src/checker/class_type.rs", "src/solver/compat.rs"],
351            test_coverage: 0.85,
352            dependencies: vec![],
353            notes: "Implemented via __private_brand_ properties. Classes with private/protected members get brand properties for nominal comparison.",
354        });
355
356        rules.insert(18, RuleImplementation {
357            rule_number: 18,
358            name: "Class \"Static Side\" Rules",
359            phase: ImplementationPhase::Phase4,
360            status: ImplementationStatus::FullyImplemented,
361            implementation_files: vec!["src/checker/class_type.rs"],
362            test_coverage: 0.80,
363            dependencies: vec![],
364            notes: "Implemented in get_class_constructor_type(). Static members collected separately with construct signatures.",
365        });
366
367        rules.insert(43, RuleImplementation {
368            rule_number: 43,
369            name: "Abstract Class Instantiation",
370            phase: ImplementationPhase::Phase4,
371            status: ImplementationStatus::FullyImplemented,
372            implementation_files: vec!["src/checker/class_type.rs", "src/checker/state.rs"],
373            test_coverage: 0.85,
374            dependencies: vec![],
375            notes: "Implemented via abstract_constructor_types set. Abstract classes tracked and checked in abstract_constructor_assignability_override().",
376        });
377
378        // MODULE INTEROP
379        rules.insert(39, RuleImplementation {
380            rule_number: 39,
381            name: "`import type` Erasure (Value vs Type Space)",
382            phase: ImplementationPhase::Phase4,
383            status: ImplementationStatus::FullyImplemented,
384            implementation_files: vec!["src/checker/type_checking.rs", "src/checker/type_computation.rs", "src/checker/symbol_resolver.rs"],
385            test_coverage: 0.90,
386            dependencies: vec![],
387            notes: "FULLY IMPLEMENTED. alias_resolves_to_type_only() checks import type symbols. error_type_only_value_at() emits TS2693. Applied in get_type_of_identifier, new expressions, property access.",
388        });
389
390        rules.insert(44, RuleImplementation {
391            rule_number: 44,
392            name: "Module Augmentation Merging",
393            phase: ImplementationPhase::Phase4,
394            status: ImplementationStatus::FullyImplemented,
395            implementation_files: vec!["src/binder/state.rs", "src/checker/interface_type.rs", "src/checker/state.rs"],
396            test_coverage: 85.0,
397            dependencies: vec![],
398            notes: "FULLY IMPLEMENTED. Binder tracks module augmentations via module_augmentations field. Checker merges augmented interface/type declarations with target module symbols using get_module_augmentation_members() and apply_module_augmentations(). Augmentations are applied during import resolution for both named imports and namespace imports.",
399        });
400
401        // JSX
402        rules.insert(36, RuleImplementation {
403            rule_number: 36,
404            name: "JSX Intrinsic Lookup (Case Sensitivity)",
405            phase: ImplementationPhase::Phase4,
406            status: ImplementationStatus::FullyImplemented,
407            implementation_files: vec!["src/checker/state.rs", "src/checker/jsx.rs"],
408            test_coverage: 85.0,
409            dependencies: vec![],
410            notes: "FULLY IMPLEMENTED. Case detection (lowercase=intrinsic, uppercase=component). Component tag resolution via compute_type_of_node. Intrinsic elements resolve JSX.IntrinsicElements[tagName] via IndexAccess type. JSX.Element type for fragments. Falls back to ANY when JSX namespace unavailable.",
411        });
412
413        // OTHER PHASE 4 RULES
414        rules.insert(8, RuleImplementation {
415            rule_number: 8,
416            name: "Unchecked Indexed Access",
417            phase: ImplementationPhase::Phase4,
418            status: ImplementationStatus::FullyImplemented,
419            implementation_files: vec!["src/solver/subtype.rs"],
420            test_coverage: 0.85,
421            dependencies: vec![],
422            notes: "no_unchecked_indexed_access flag in SubtypeChecker. T[K] returns T without undefined by default.",
423        });
424
425        rules.insert(9, RuleImplementation {
426            rule_number: 9,
427            name: "Legacy Null/Undefined",
428            phase: ImplementationPhase::Phase4,
429            status: ImplementationStatus::FullyImplemented,
430            implementation_files: vec!["src/solver/compat.rs", "src/solver/subtype.rs"],
431            test_coverage: 0.95,
432            dependencies: vec![],
433            notes: "strict_null_checks flag in both CompatChecker and SubtypeChecker. Legacy mode allows null/undefined everywhere.",
434        });
435
436        rules.insert(13, RuleImplementation {
437            rule_number: 13,
438            name: "Weak Type Detection",
439            phase: ImplementationPhase::Phase4,
440            status: ImplementationStatus::FullyImplemented,
441            implementation_files: vec!["src/solver/compat.rs"],
442            test_coverage: 0.90,
443            dependencies: vec![],
444            notes: "violates_weak_type() in compat.rs. Objects with only optional properties require overlap check.",
445        });
446
447        rules.insert(15, RuleImplementation {
448            rule_number: 15,
449            name: "Tuple-Array Assignment",
450            phase: ImplementationPhase::Phase4,
451            status: ImplementationStatus::FullyImplemented,
452            implementation_files: vec!["src/solver/subtype.rs", "src/solver/subtype_rules/tuples.rs"],
453            test_coverage: 0.85,
454            dependencies: vec![],
455            notes: "FULLY IMPLEMENTED. Tuple to Array covariance in subtype.rs. Array to Tuple rejection for fixed tuples. Empty array [] handled as never[].",
456        });
457
458        rules.insert(16, RuleImplementation {
459            rule_number: 16,
460            name: "Rest Parameter Bivariance",
461            phase: ImplementationPhase::Phase4,
462            status: ImplementationStatus::FullyImplemented,
463            implementation_files: vec!["src/solver/subtype.rs", "src/solver/compat.rs", "src/solver/evaluate_rules/conditional.rs"],
464            test_coverage: 0.85,
465            dependencies: vec![],
466            notes: "FULLY IMPLEMENTED. allow_bivariant_rest flag in SubtypeChecker and CompatChecker. Conditional type checks use bivariant rest parameters. (...args: any[]) => void is universal callable supertype.",
467        });
468
469        rules.insert(17, RuleImplementation {
470            rule_number: 17,
471            name: "The Instantiation Depth Limit",
472            phase: ImplementationPhase::Phase4,
473            status: ImplementationStatus::FullyImplemented,
474            implementation_files: vec!["src/solver/subtype.rs"],
475            test_coverage: 0.80,
476            dependencies: vec![],
477            notes: "Recursion depth check at line 320-326 in subtype.rs. depth > 100 returns False with depth_exceeded flag.",
478        });
479
480        rules.insert(22, RuleImplementation {
481            rule_number: 22,
482            name: "Template String Expansion Limits",
483            phase: ImplementationPhase::Phase4,
484            status: ImplementationStatus::FullyImplemented,
485            implementation_files: vec!["src/solver/intern.rs", "src/solver/evaluate_rules/template_literal.rs"],
486            test_coverage: 0.90,
487            dependencies: vec![],
488            notes: "FULLY IMPLEMENTED. TEMPLATE_LITERAL_EXPANSION_LIMIT = 100,000. Pre-computes cardinality before expansion. Aborts and widens to string with diagnostic logging when limit exceeded. Tests: template_expansion_tests.rs",
489        });
490
491        rules.insert(23, RuleImplementation {
492            rule_number: 23,
493            name: "Comparison Operator Overlap (Expression Logic)",
494            phase: ImplementationPhase::Phase4,
495            status: ImplementationStatus::FullyImplemented,
496            implementation_files: vec!["src/solver/intern.rs"],
497            test_coverage: 0.85,
498            dependencies: vec![],
499            notes: "FULLY IMPLEMENTED. has_overlap() in TypeInterner checks if types intersect. Used for === and !== comparisons to detect always-true/false conditions.",
500        });
501
502        rules.insert(26, RuleImplementation {
503            rule_number: 26,
504            name: "Split Accessors (Getter/Setter Variance)",
505            phase: ImplementationPhase::Phase4,
506            status: ImplementationStatus::FullyImplemented,
507            implementation_files: vec!["src/solver/types.rs", "src/solver/subtype_rules/objects.rs"],
508            test_coverage: 0.90,
509            dependencies: vec![],
510            notes: "FULLY IMPLEMENTED. PropertyInfo has type_id (read) and write_type (write). Subtype checking: read types are covariant, write types are contravariant (target_write <: source_write).",
511        });
512
513        rules.insert(27, RuleImplementation {
514            rule_number: 27,
515            name: "Homomorphic Mapped Types over Primitives",
516            phase: ImplementationPhase::Phase4,
517            status: ImplementationStatus::FullyImplemented,
518            implementation_files: vec!["src/solver/evaluate_rules/keyof.rs", "src/solver/evaluate_rules/apparent.rs", "src/solver/evaluate_rules/mapped.rs"],
519            test_coverage: 0.85,
520            dependencies: vec![12],
521            notes: "FULLY IMPLEMENTED. keyof of primitives calls apparent_primitive_keyof() which returns union of apparent member names. Mapped types then iterate over these keys.",
522        });
523
524        rules.insert(28, RuleImplementation {
525            rule_number: 28,
526            name: "The \"Constructor Void\" Exception",
527            phase: ImplementationPhase::Phase4,
528            status: ImplementationStatus::FullyImplemented,
529            implementation_files: vec!["src/solver/subtype_rules/functions.rs"],
530            test_coverage: 0.85,
531            dependencies: vec![6],
532            notes: "Implemented via allow_void_return flag in check_return_compat(). Constructors with void return accept concrete implementations.",
533        });
534
535        rules.insert(29, RuleImplementation {
536            rule_number: 29,
537            name: "The Global `Function` Type (The Untyped Callable)",
538            phase: ImplementationPhase::Phase4,
539            status: ImplementationStatus::FullyImplemented,
540            implementation_files: vec!["src/solver/subtype.rs", "src/solver/subtype_rules/intrinsics.rs"],
541            test_coverage: 0.85,
542            dependencies: vec![],
543            notes: "Implemented via is_callable_type(). TypeId::FUNCTION accepts any callable type as subtype.",
544        });
545
546        rules.insert(31, RuleImplementation {
547            rule_number: 31,
548            name: "Base Constraint Assignability (Generic Erasure)",
549            phase: ImplementationPhase::Phase4,
550            status: ImplementationStatus::FullyImplemented,
551            implementation_files: vec!["src/solver/subtype.rs"],
552            test_coverage: 0.85,
553            dependencies: vec![],
554            notes: "FULLY IMPLEMENTED. TypeParameter as both source and target in subtype checks. Constraint(T) <: U and U <: Constraint(T) logic for generic assignability.",
555        });
556
557        rules.insert(32, RuleImplementation {
558            rule_number: 32,
559            name: "Best Common Type (BCT) Inference",
560            phase: ImplementationPhase::Phase4,
561            status: ImplementationStatus::FullyImplemented,
562            implementation_files: vec!["src/solver/infer.rs", "src/checker/type_computation.rs"],
563            test_coverage: 0.85,
564            dependencies: vec![],
565            notes: "FULLY IMPLEMENTED. Array literal type inference uses best_common_type() from TypeInterner. Algorithm: 1) Filter duplicates/never, 2) Find common base, 3) Find supertype of all, 4) Fall back to union.",
566        });
567
568        rules.insert(33, RuleImplementation {
569            rule_number: 33,
570            name: "The \"Object\" vs \"Primitive\" boxing behavior",
571            phase: ImplementationPhase::Phase4,
572            status: ImplementationStatus::FullyImplemented,
573            implementation_files: vec!["src/solver/subtype.rs", "src/solver/subtype_rules/intrinsics.rs"],
574            test_coverage: 0.85,
575            dependencies: vec![20],
576            notes: "FULLY IMPLEMENTED. TypeResolver.get_boxed_type() enables primitives to be subtypes of boxed interfaces. is_boxed_primitive_subtype() checks number <: Number, string <: String, etc.",
577        });
578
579        rules.insert(35, RuleImplementation {
580            rule_number: 35,
581            name: "The Recursion Depth Limiter (\"The Circuit Breaker\")",
582            phase: ImplementationPhase::Phase4,
583            status: ImplementationStatus::FullyImplemented,
584            implementation_files: vec!["src/solver/subtype.rs"],
585            test_coverage: 0.85,
586            dependencies: vec![17],
587            notes: "Recursion depth limiter same as Rule #17. Implemented at line 320-326. depth_exceeded flag.",
588        });
589
590        rules.insert(37, RuleImplementation {
591            rule_number: 37,
592            name: "`unique symbol` (Nominal Primitives)",
593            phase: ImplementationPhase::Phase4,
594            status: ImplementationStatus::FullyImplemented,
595            implementation_files: vec!["src/solver/subtype.rs"],
596            test_coverage: 0.85,
597            dependencies: vec![],
598            notes: "Implemented via TypeData::UniqueSymbol. Only identical unique symbols match; they widen to generic Symbol.",
599        });
600
601        rules.insert(38, RuleImplementation {
602            rule_number: 38,
603            name: "Correlated Unions (The Cross-Product limitation)",
604            phase: ImplementationPhase::Phase4,
605            status: ImplementationStatus::FullyImplemented,
606            implementation_files: vec!["src/solver/evaluate_rules/index_access.rs"],
607            test_coverage: 0.85,
608            dependencies: vec![],
609            notes: "FULLY IMPLEMENTED. IndexAccess(Union, Union) cross-product expansion at top level of evaluate_index_access(). T[A | B] -> T[A] | T[B], enabling full Cartesian product for (X | Y)[A | B].",
610        });
611
612        rules.insert(42, RuleImplementation {
613            rule_number: 42,
614            name: "CFA Invalidation in Closures",
615            phase: ImplementationPhase::Phase4,
616            status: ImplementationStatus::FullyImplemented,
617            implementation_files: vec!["src/checker/control_flow.rs"],
618            test_coverage: 0.85,
619            dependencies: vec![],
620            notes: "FULLY IMPLEMENTED. check_flow() handles flow_flags::START for closure boundaries. is_mutable_variable() checks node_flags::CONST. Mutable let/var lose narrowing, const keeps it.",
621        });
622
623        rules.insert(12, RuleImplementation {
624            rule_number: 12,
625            name: "Apparent Members of Primitives",
626            phase: ImplementationPhase::Phase4,
627            status: ImplementationStatus::FullyImplemented,
628            implementation_files: vec!["src/solver/apparent.rs", "src/solver/evaluate_rules/apparent.rs", "src/solver/subtype.rs"],
629            test_coverage: 0.85,
630            dependencies: vec![],
631            notes: "FULLY IMPLEMENTED. apparent_primitive_members() maps primitives to their wrapper types (string -> String, number -> Number). Used in property access and keyof.",
632        });
633
634        Self { rules }
635    }
636
637    /// Get the implementation status of a specific rule by number
638    pub fn get_rule_status(&self, rule_number: u8) -> Option<&RuleImplementation> {
639        self.rules.get(&rule_number)
640    }
641
642    /// Get all rules
643    pub fn all_rules(&self) -> impl Iterator<Item = &RuleImplementation> {
644        self.rules.values().collect::<Vec<_>>().into_iter()
645    }
646
647    /// Get rules by phase
648    pub fn rules_by_phase(&self, phase: ImplementationPhase) -> Vec<&RuleImplementation> {
649        self.all_rules().filter(|r| r.phase == phase).collect()
650    }
651
652    /// Get rules by status
653    pub fn rules_by_status(&self, status: ImplementationStatus) -> Vec<&RuleImplementation> {
654        self.all_rules().filter(|r| r.status == status).collect()
655    }
656
657    /// Count rules with given status
658    pub fn count_by_status(&self, status: ImplementationStatus) -> usize {
659        self.rules_by_status(status).len()
660    }
661
662    /// Calculate overall completion percentage
663    pub fn overall_completion(&self) -> f32 {
664        let total = self.rules.len() as f32;
665        let completed = self
666            .rules
667            .values()
668            .map(|r| r.status.completion_ratio())
669            .sum::<f32>();
670        if total > 0.0 { completed / total } else { 0.0 }
671    }
672
673    /// Calculate completion by phase
674    pub fn completion_by_phase(&self, phase: ImplementationPhase) -> f32 {
675        let rules = self.rules_by_phase(phase);
676        if rules.is_empty() {
677            return 0.0;
678        }
679        let sum = rules
680            .iter()
681            .map(|r| r.status.completion_ratio())
682            .sum::<f32>();
683        sum / rules.len() as f32
684    }
685
686    /// Get missing rules (not implemented)
687    pub fn missing_rules(&self) -> Vec<&RuleImplementation> {
688        self.rules_by_status(ImplementationStatus::NotImplemented)
689    }
690
691    /// Get blocked rules
692    pub fn blocked_rules(&self) -> Vec<&RuleImplementation> {
693        self.rules_by_status(ImplementationStatus::Blocked)
694    }
695
696    /// Generate a summary report
697    pub fn summary_report(&self) -> String {
698        let mut report = String::new();
699        report.push_str("# TypeScript Unsoundness Catalog - Implementation Audit\n\n");
700
701        // Overall stats
702        report.push_str("## Overall Status\n\n");
703        report.push_str("- **Total Rules:** 44\n");
704        report.push_str(&format!(
705            "- **Fully Implemented:** {} ({:.1}%)\n",
706            self.count_by_status(ImplementationStatus::FullyImplemented),
707            self.count_by_status(ImplementationStatus::FullyImplemented) as f32 / 44.0 * 100.0
708        ));
709        report.push_str(&format!(
710            "- **Partially Implemented:** {} ({:.1}%)\n",
711            self.count_by_status(ImplementationStatus::PartiallyImplemented),
712            self.count_by_status(ImplementationStatus::PartiallyImplemented) as f32 / 44.0 * 100.0
713        ));
714        report.push_str(&format!(
715            "- **Not Implemented:** {} ({:.1}%)\n",
716            self.count_by_status(ImplementationStatus::NotImplemented),
717            self.count_by_status(ImplementationStatus::NotImplemented) as f32 / 44.0 * 100.0
718        ));
719        report.push_str(&format!(
720            "- **Overall Completion:** {:.1}%\n\n",
721            self.overall_completion() * 100.0
722        ));
723
724        // Phase breakdown
725        report.push_str("## Completion by Phase\n\n");
726        for phase in [
727            ImplementationPhase::Phase1,
728            ImplementationPhase::Phase2,
729            ImplementationPhase::Phase3,
730            ImplementationPhase::Phase4,
731        ] {
732            report.push_str(&format!(
733                "- **{}:** {:.1}%\n",
734                phase,
735                self.completion_by_phase(phase) * 100.0
736            ));
737        }
738        report.push('\n');
739
740        // Critical gaps
741        report.push_str("## Critical Gaps (High Priority)\n\n");
742        report.push_str("### Enum Rules (Phase 4)\n");
743        for rule in [7u8, 24, 34] {
744            if let Some(r) = self.get_rule_status(rule) {
745                report.push_str(&format!(
746                    "- **Rule #{} ({}):** {} {}\n",
747                    rule,
748                    r.name,
749                    r.status.emoji(),
750                    r.notes
751                ));
752            }
753        }
754        report.push('\n');
755
756        report.push_str("### Class Rules (Phase 4)\n");
757        for rule in [5u8, 18, 43] {
758            if let Some(r) = self.get_rule_status(rule) {
759                report.push_str(&format!(
760                    "- **Rule #{} ({}):** {} {}\n",
761                    rule,
762                    r.name,
763                    r.status.emoji(),
764                    r.notes
765                ));
766            }
767        }
768        report.push('\n');
769
770        report.push_str("### Phase 2 Gaps (Blockers)\n");
771        for rule in [10u8, 19] {
772            if let Some(r) = self.get_rule_status(rule) {
773                report.push_str(&format!(
774                    "- **Rule #{} ({}):** {} {}\n",
775                    rule,
776                    r.name,
777                    r.status.emoji(),
778                    r.notes
779                ));
780            }
781        }
782        report.push('\n');
783
784        // Interdependencies
785        report.push_str("## Key Interdependencies\n\n");
786        report.push_str("- **Weak Type Detection (#13)** ↔ **Excess Properties (#4)** ↔ **Freshness**: All three work together for object literal checks\n");
787        report.push_str("- **Apparent Types (#12)** → **Object Trifecta (#20)** → **Primitive Boxing (#33)**: Primitive type handling chain\n");
788        report.push_str("- **Void Return (#6)** → **Constructor Void (#28)**: Void exception applies to both functions and constructors\n");
789        report.push_str("- **Enum Open (#7)** → **Cross-Enum (#24)** → **String Enum (#34)**: Enum assignability rules build on each other\n\n");
790
791        report
792    }
793
794    /// Generate a detailed matrix table
795    pub fn matrix_table(&self) -> String {
796        let mut table = String::new();
797        table.push_str("| # | Rule | Phase | Status | Files | Coverage | Notes |\n");
798        table.push_str("|---|------|-------|--------|-------|----------|-------|\n");
799
800        let mut rules: Vec<_> = self.rules.values().collect();
801        rules.sort_by_key(|r| r.rule_number);
802
803        for rule in rules {
804            let files = rule.implementation_files.join(", ");
805            let status = format!("{} {:?}", rule.status.emoji(), rule.status);
806            table.push_str(&format!(
807                "| {} | {} | {} | {} | {} | {:.0}% | {} |\n",
808                rule.rule_number,
809                rule.name,
810                match rule.phase {
811                    ImplementationPhase::Phase1 => "P1",
812                    ImplementationPhase::Phase2 => "P2",
813                    ImplementationPhase::Phase3 => "P3",
814                    ImplementationPhase::Phase4 => "P4",
815                },
816                status,
817                if files.is_empty() { "N/A" } else { &files },
818                rule.test_coverage * 100.0,
819                rule.notes
820            ));
821        }
822
823        table
824    }
825}
826
827impl Default for UnsoundnessAudit {
828    fn default() -> Self {
829        Self::new()
830    }
831}
832
833#[cfg(test)]
834#[path = "../tests/unsoundness_audit_tests.rs"]
835mod tests;