use react_compiler_hir::{
PrunedReactiveScopeBlock, ReactiveFunction, ReactiveScopeBlock, ReactiveStatement,
ReactiveTerminal, ReactiveTerminalStatement, environment::Environment,
};
use crate::visitors::{ReactiveFunctionTransform, Transformed, transform_reactive_function};
struct State {
has_return_statement: bool,
}
pub fn prune_unused_scopes(
func: &mut ReactiveFunction,
env: &Environment,
) -> Result<(), react_compiler_diagnostics::CompilerError> {
let mut transform = Transform { env };
let mut state = State {
has_return_statement: false,
};
transform_reactive_function(func, &mut transform, &mut state)
}
struct Transform<'a> {
env: &'a Environment,
}
impl<'a> ReactiveFunctionTransform for Transform<'a> {
type State = State;
fn env(&self) -> &Environment {
self.env
}
fn visit_terminal(
&mut self,
stmt: &mut ReactiveTerminalStatement,
state: &mut State,
) -> Result<(), react_compiler_diagnostics::CompilerError> {
self.traverse_terminal(stmt, state)?;
if matches!(stmt.terminal, ReactiveTerminal::Return { .. }) {
state.has_return_statement = true;
}
Ok(())
}
fn transform_scope(
&mut self,
scope: &mut ReactiveScopeBlock,
_state: &mut State,
) -> Result<Transformed<ReactiveStatement>, react_compiler_diagnostics::CompilerError> {
let mut scope_state = State {
has_return_statement: false,
};
self.visit_scope(scope, &mut scope_state)?;
let scope_id = scope.scope;
let scope_data = &self.env.scopes[scope_id.0 as usize];
if !scope_state.has_return_statement
&& scope_data.reassignments.is_empty()
&& (scope_data.declarations.is_empty() || !has_own_declaration(scope_data, scope_id))
{
Ok(Transformed::Replace(ReactiveStatement::PrunedScope(
PrunedReactiveScopeBlock {
scope: scope.scope,
instructions: std::mem::take(&mut scope.instructions),
},
)))
} else {
Ok(Transformed::Keep)
}
}
}
fn has_own_declaration(
scope_data: &react_compiler_hir::ReactiveScope,
scope_id: react_compiler_hir::ScopeId,
) -> bool {
for (_, decl) in &scope_data.declarations {
if decl.scope == scope_id {
return true;
}
}
false
}