compiler/packages/babel-plugin-react-compiler/docs/passes/25-pruneUnusedScopes.md MARKDOWN 112 lines View on github.com → Search inside
1# pruneUnusedScopes23## File4`src/ReactiveScopes/PruneUnusedScopes.ts`56## Purpose7This pass converts reactive scopes that have no meaningful outputs into "pruned scopes". A pruned scope is no longer memoized - its instructions are executed unconditionally on every render. This optimization removes unnecessary memoization overhead for scopes that don't produce values that need to be cached.89## Input Invariants10- The input is a `ReactiveFunction` that has already been transformed into reactive scope form11- Scopes have been created and have `declarations`, `reassignments`, and potentially `earlyReturnValue` populated12- The pass is called after:13  - `pruneUnusedLabels` - cleans up unnecessary labels14  - `pruneNonEscapingScopes` - removes scopes whose outputs don't escape15  - `pruneNonReactiveDependencies` - removes non-reactive dependencies from scopes16- Scopes may already be marked as pruned by earlier passes1718## Output Guarantees19Scopes that meet ALL of the following criteria are converted to `pruned-scope`:20- No return statement within the scope21- No reassignments (`scope.reassignments.size === 0`)22- Either no declarations (`scope.declarations.size === 0`), OR all declarations "bubbled up" from inner scopes2324Pruned scopes:25- Keep their original scope metadata (for debugging/tracking)26- Keep their instructions intact27- Will be executed unconditionally during codegen (no memoization check)2829## Algorithm3031The pass uses the visitor pattern with `ReactiveFunctionTransform`:32331. **State Tracking**: A `State` object tracks whether a return statement was encountered:34   ```typescript35   type State = {36     hasReturnStatement: boolean;37   };38   ```39402. **Terminal Visitor** (`visitTerminal`): Checks if any terminal is a `return` statement41423. **Scope Transform** (`transformScope`): For each scope:43   - Creates a fresh state for this scope44   - Recursively visits the scope's contents45   - Checks pruning criteria:46     - `!scopeState.hasReturnStatement` - no early return47     - `scope.reassignments.size === 0` - no reassignments48     - `scope.declarations.size === 0` OR `!hasOwnDeclaration(scopeBlock)` - no outputs49504. **hasOwnDeclaration Helper**: Determines if a scope has "own" declarations vs declarations propagated from nested scopes5152## Edge Cases5354### Return Statements55Scopes containing return statements are preserved because early returns need memoization to avoid re-executing the return check on every render.5657### Bubbled-Up Declarations58When nested scopes are flattened or merged, their declarations may be propagated to parent scopes. The `hasOwnDeclaration` check ensures that parent scopes with only inherited declarations can still be pruned.5960### Reassignments61Scopes with reassignments are kept because the reassignment represents a side effect that needs to be tracked for memoization.6263### Already-Pruned Scopes64The pass operates on `ReactiveScopeBlock` (kind: 'scope'), not `PrunedReactiveScopeBlock`. Scopes already pruned by earlier passes are not revisited.6566### Interaction with Subsequent Passes67The `MergeReactiveScopesThatInvalidateTogether` pass explicitly handles pruned scopes - it does not merge across them.6869## TODOs70None in the source file.7172## Example7374### Fixture: `prune-scopes-whose-deps-invalidate-array.js`7576**Input:**77```javascript78function Component(props) {79  const x = [];80  useHook();81  x.push(props.value);82  const y = [x];83  return [y];84}85```8687What happens:88- The scope for `x` cannot be memoized because `useHook()` is called inside it89- `FlattenScopesWithHooksOrUseHIR` marks scope @0 as `pruned-scope`90- `PruneUnusedScopes` doesn't change it further since it's already pruned9192**Output (no memoization for x):**93```javascript94function Component(props) {95  const x = [];96  useHook();97  x.push(props.value);98  const y = [x];99  return [y];100}101```102103### Key Insight104105The `pruneUnusedScopes` pass is part of a multi-pass pruning strategy:1061. `FlattenScopesWithHooksOrUseHIR` - Prunes scopes that contain hook/use calls1072. `pruneNonEscapingScopes` - Prunes scopes whose outputs don't escape1083. `pruneNonReactiveDependencies` - Removes non-reactive dependencies1094. **`pruneUnusedScopes`** - Prunes scopes with no remaining outputs110111This pass acts as a cleanup for scopes that became "empty" after previous pruning passes removed their outputs.

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.