main
rs 100 lines 3.04 KB
Raw
1 // Copyright (c) Meta Platforms, Inc. and affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 //! PruneUnusedScopes — converts scopes without outputs into regular blocks.
7 //!
8 //! Corresponds to `src/ReactiveScopes/PruneUnusedScopes.ts`.
9
10 use react_compiler_hir::{
11 PrunedReactiveScopeBlock, ReactiveFunction, ReactiveScopeBlock, ReactiveStatement,
12 ReactiveTerminal, ReactiveTerminalStatement, environment::Environment,
13 };
14
15 use crate::visitors::{ReactiveFunctionTransform, Transformed, transform_reactive_function};
16
17 struct State {
18 has_return_statement: bool,
19 }
20
21 /// Converts scopes without outputs into pruned-scopes (regular blocks).
22 /// TS: `pruneUnusedScopes`
23 pub fn prune_unused_scopes(
24 func: &mut ReactiveFunction,
25 env: &Environment,
26 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
27 let mut transform = Transform { env };
28 let mut state = State {
29 has_return_statement: false,
30 };
31 transform_reactive_function(func, &mut transform, &mut state)
32 }
33
34 struct Transform<'a> {
35 env: &'a Environment,
36 }
37
38 impl<'a> ReactiveFunctionTransform for Transform<'a> {
39 type State = State;
40
41 fn env(&self) -> &Environment {
42 self.env
43 }
44
45 fn visit_terminal(
46 &mut self,
47 stmt: &mut ReactiveTerminalStatement,
48 state: &mut State,
49 ) -> Result<(), react_compiler_diagnostics::CompilerError> {
50 self.traverse_terminal(stmt, state)?;
51 if matches!(stmt.terminal, ReactiveTerminal::Return { .. }) {
52 state.has_return_statement = true;
53 }
54 Ok(())
55 }
56
57 fn transform_scope(
58 &mut self,
59 scope: &mut ReactiveScopeBlock,
60 _state: &mut State,
61 ) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
62 let mut scope_state = State {
63 has_return_statement: false,
64 };
65 self.visit_scope(scope, &mut scope_state)?;
66
67 let scope_id = scope.scope;
68 let scope_data = &self.env.scopes[scope_id.0 as usize];
69
70 if !scope_state.has_return_statement
71 && scope_data.reassignments.is_empty()
72 && (scope_data.declarations.is_empty() || !has_own_declaration(scope_data, scope_id))
73 {
74 // Replace with pruned scope
75 Ok(Transformed::Replace(ReactiveStatement::PrunedScope(
76 PrunedReactiveScopeBlock {
77 scope: scope.scope,
78 instructions: std::mem::take(&mut scope.instructions),
79 },
80 )))
81 } else {
82 Ok(Transformed::Keep)
83 }
84 }
85 }
86
87 /// Does the scope block declare any values of its own?
88 /// Returns false if all declarations are propagated from nested scopes.
89 /// TS: `hasOwnDeclaration`
90 fn has_own_declaration(
91 scope_data: &react_compiler_hir::ReactiveScope,
92 scope_id: react_compiler_hir::ScopeId,
93 ) -> bool {
94 for (_, decl) in &scope_data.declarations {
95 if decl.scope == scope_id {
96 return true;
97 }
98 }
99 false
100 }