1use rustc_hash::FxHashMap;
25use std::fmt;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ImplementationStatus {
30 FullyImplemented,
32 PartiallyImplemented,
34 NotImplemented,
36 Blocked,
38 NotApplicable,
40}
41
42impl ImplementationStatus {
43 pub const fn is_implemented(self) -> bool {
45 matches!(self, Self::FullyImplemented | Self::PartiallyImplemented)
46 }
47
48 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ImplementationPhase {
72 Phase1,
75 Phase2,
78 Phase3,
81 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#[derive(Debug, Clone)]
99pub struct RuleImplementation {
100 pub rule_number: u8,
102 pub name: &'static str,
104 pub phase: ImplementationPhase,
106 pub status: ImplementationStatus,
108 pub implementation_files: Vec<&'static str>,
110 pub test_coverage: f32,
112 pub dependencies: Vec<u8>,
114 pub notes: &'static str,
116}
117
118#[derive(Debug, Clone)]
120pub struct UnsoundnessAudit {
121 rules: FxHashMap<u8, RuleImplementation>,
122}
123
124impl UnsoundnessAudit {
125 pub fn new() -> Self {
127 let mut rules = FxHashMap::default();
128
129 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 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 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 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 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 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 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 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 pub fn get_rule_status(&self, rule_number: u8) -> Option<&RuleImplementation> {
639 self.rules.get(&rule_number)
640 }
641
642 pub fn all_rules(&self) -> impl Iterator<Item = &RuleImplementation> {
644 self.rules.values().collect::<Vec<_>>().into_iter()
645 }
646
647 pub fn rules_by_phase(&self, phase: ImplementationPhase) -> Vec<&RuleImplementation> {
649 self.all_rules().filter(|r| r.phase == phase).collect()
650 }
651
652 pub fn rules_by_status(&self, status: ImplementationStatus) -> Vec<&RuleImplementation> {
654 self.all_rules().filter(|r| r.status == status).collect()
655 }
656
657 pub fn count_by_status(&self, status: ImplementationStatus) -> usize {
659 self.rules_by_status(status).len()
660 }
661
662 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 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 pub fn missing_rules(&self) -> Vec<&RuleImplementation> {
688 self.rules_by_status(ImplementationStatus::NotImplemented)
689 }
690
691 pub fn blocked_rules(&self) -> Vec<&RuleImplementation> {
693 self.rules_by_status(ImplementationStatus::Blocked)
694 }
695
696 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 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 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 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 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 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;