| 1 | # Rust Port Gap Analysis |
| 2 | |
| 3 | Comprehensive comparison of the TypeScript and Rust compiler implementations. |
| 4 | Generated 2026-03-30 from systematic review of all major subsystems. |
| 5 | |
| 6 | Current test status: Pass 1717/1717, Code 1716/1717, Snap 1717/1718. |
| 7 | |
| 8 | --- |
| 9 | |
| 10 | ## Critical Gaps (incorrect compilation possible) |
| 11 | |
| 12 | ### 3. Hardcoded `useMemoCache` identifier name |
| 13 | - **TS**: `ReactiveScopes/CodegenReactiveFunction.ts:166-178` |
| 14 | ```typescript |
| 15 | const useMemoCacheIdentifier = fn.env.programContext.addMemoCacheImport().name; |
| 16 | ``` |
| 17 | - **Rust**: `react_compiler_reactive_scopes/src/codegen_reactive_function.rs:179` |
| 18 | ```rust |
| 19 | callee: Box::new(Expression::Identifier(make_identifier("useMemoCache"))), |
| 20 | ``` |
| 21 | - The TS dynamically resolves the `useMemoCache` import to `_c` (from `react/compiler-runtime` import specifier `c`). The Rust hardcodes `"useMemoCache"`. The BabelPlugin.ts wrapper handles the rename to `_c` during AST application, so this works in practice, but it means the codegen output has an incorrect intermediate identifier. |
| 22 | |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Moderate Gaps (feature gaps or edge cases) |
| 27 | |
| 28 | ### 6. Missing `optimizeForSSR` pass |
| 29 | - **TS**: `Entrypoint/Pipeline.ts:223-226` |
| 30 | - **Rust**: MISSING entirely |
| 31 | - Inlines useState/useReducer, removes effects, strips event handlers from JSX, removes refs. Without it, SSR-mode compilation produces unoptimized output. |
| 32 | |
| 33 | ### 7. Missing `enableForest` codegen path |
| 34 | - **TS**: `ReactiveScopes/CodegenReactiveFunction.ts:1527-1536` |
| 35 | - **Rust**: MISSING |
| 36 | - Skips hook guard wrapping and emits `typeArguments` on call expressions. Missing in Rust means forest mode has incorrect hook wrapping and dropped type arguments. |
| 37 | |
| 38 | ### 8. Function name inference from AssignmentExpression and Property |
| 39 | - **TS**: `Entrypoint/Program.ts:1226-1268` — three cases: `parent.isAssignmentExpression()`, `parent.isProperty()`, `parent.isAssignmentPattern()` |
| 40 | - **Rust**: `program.rs:1483-1488` — only handles `VariableDeclarator` |
| 41 | - Functions in `Foo = () => {}`, `{useFoo: () => {}}`, or `{useFoo = () => {}} = {}` positions are nameless in Rust, preventing component/hook detection via name heuristics. |
| 42 | |
| 43 | ### 9. Missing validations in outlined function pipeline |
| 44 | - **TS**: Outlined functions go through full `compileFn` → all validations |
| 45 | - **Rust**: `run_pipeline_passes` skips: `validateContextVariableLValues`, `validateUseMemo`, `validateNoDerivedComputationsInEffects/_exp`, `validateNoSetStateInEffects`, `validateNoJSXInTryStatement`, `validateNoCapitalizedCalls`, `validateStaticComponents` |
| 46 | - Also missing: `has_errors()` check at end, `memo_cache_import` registration |
| 47 | |
| 48 | ### 10. Reanimated flag injection missing |
| 49 | - **TS**: `Babel/BabelPlugin.ts:48-53` — `injectReanimatedFlag(opts)` sets `enableCustomTypeDefinitionForReanimated = true` |
| 50 | - **Rust**: Detects reanimated but doesn't inject the flag into environment config |
| 51 | - Custom type definitions for reanimated shared values won't activate. |
| 52 | |
| 53 | ### 11. Dev-mode `enableResetCacheOnSourceFileChanges` injection missing |
| 54 | - **TS**: `Babel/BabelPlugin.ts:54-65` — auto-enables in dev mode |
| 55 | - **Rust**: MISSING |
| 56 | - Fast refresh cache-reset code won't generate in dev mode unless explicitly configured. |
| 57 | |
| 58 | ### 12. Outlined functions not re-queued for compilation |
| 59 | - **TS**: `Entrypoint/Program.ts:476-501` — outlined functions with a React function type are pushed back into the compilation queue |
| 60 | - **Rust**: `program.rs:2244-2262` — only does AST insertion |
| 61 | - Outlined functions don't receive full compilation treatment (memoization). |
| 62 | |
| 63 | ### 13. Missing `addNewReference` in RenameVariables |
| 64 | - **TS**: `ReactiveScopes/RenameVariables.ts:163` — `this.#programContext.addNewReference(name)` |
| 65 | - **Rust**: MISSING |
| 66 | - Newly created variable names aren't registered with the program context, risking import binding conflicts. |
| 67 | |
| 68 | ### 14. `known_incompatible` not checked for legacy signatures without aliasing config |
| 69 | - **TS**: `Inference/InferMutationAliasingEffects.ts:2351-2370` |
| 70 | - **Rust**: `infer_mutation_aliasing_effects.rs:2099-2100` — TODO comment, only checked in the `Apply` path with aliasing configs |
| 71 | - If any legacy signatures (without aliasing configs) have `known_incompatible` set, Rust silently continues. |
| 72 | |
| 73 | --- |
| 74 | |
| 75 | ## Minor Gaps (cosmetic, defensive, or unlikely to trigger) |
| 76 | |
| 77 | ### 15. Missing `assertValidMutableRanges` pass |
| 78 | - **TS**: `Pipeline.ts:246-249` — gated behind `config.assertValidMutableRanges` (defaults false) |
| 79 | - **Rust**: Config flag exists but pass never called |
| 80 | - Debugging-only validation, no production impact. |
| 81 | |
| 82 | ### 16. Missing `ValidateNoDerivedComputationsInEffects_exp` experimental variant |
| 83 | - **TS**: 842-line experimental validation pass |
| 84 | - **Rust**: MISSING — only the non-experimental version is ported |
| 85 | - Only affects users who explicitly enable the experimental flag in lint mode. |
| 86 | |
| 87 | ### 17. Missing `CompileUnexpectedThrow` event |
| 88 | - **TS**: `Program.ts:755-769` — logs when a pass incorrectly throws |
| 89 | - **Rust**: Event type defined but never emitted |
| 90 | - Development-time detection of misbehaving passes. |
| 91 | |
| 92 | ### 18. Missing error for `sources`-specified-without-filename |
| 93 | - **TS**: Creates a Config error via `handleError` |
| 94 | - **Rust**: Silently sets `shouldCompile = false` |
| 95 | |
| 96 | ### 19. Missing `codegen_block` temporary invariant check |
| 97 | - **TS**: `CodegenReactiveFunction.ts:474-492` — verifies no temporary was overwritten |
| 98 | - **Rust**: Restores snapshot without checking |
| 99 | |
| 100 | ### 20. Extra `NullLiteralTypeAnnotation` rejection |
| 101 | - **Rust** rejects Flow `null` type annotation on first param (over-conservative vs TS) |
| 102 | |
| 103 | ### 21. `UnsignedShiftRight` (`>>>`) not classified as primitive binary op |
| 104 | - **Rust**: `infer_types.rs:140-159` — missing from `is_primitive_binary_op` |
| 105 | - Operands won't be constrained to Primitive type, but result is still Primitive. |
| 106 | |
| 107 | ### 22. Post-dominator frontier not cached in InferReactivePlaces |
| 108 | - **Rust**: Recomputes frontier on every call instead of caching per block ID |
| 109 | - Performance issue only, not correctness. |
| 110 | |
| 111 | ### 23. `Math.random` missing `restParam` |
| 112 | - **TS**: `restParam: Effect.Read` |
| 113 | - **Rust**: `rest_param: None` (uses default `..Default::default()`) |
| 114 | - Affects extra-argument fallback effect only. |
| 115 | |
| 116 | ### 24. `WeakSet.has` / `WeakMap.has` wrong signature shape |
| 117 | - **TS**: `positionalParams: [Effect.Read], restParam: null` |
| 118 | - **Rust**: Uses `pure_primitive_fn` → `positional_params: [], rest_param: Some(Effect::Read)` |
| 119 | - Difference in extra-argument fallback behavior only. |
| 120 | |
| 121 | ### 25. Missing `throwUnknownException__testonly` in outlined function pipeline |
| 122 | - Test-only feature. |