1// Copyright (c) Meta Platforms, Inc. and affiliates.2//3// This source code is licensed under the MIT license found in the4// LICENSE file in the root directory of this source tree.56//! Visitor and transform traits for ReactiveFunction.7//!8//! Corresponds to `src/ReactiveScopes/visitors.ts` in the TypeScript compiler.910use react_compiler_diagnostics::CompilerError;11use react_compiler_hir::{12 EvaluationOrder, FunctionId, InstructionValue, ParamPattern, Place, PrunedReactiveScopeBlock,13 ReactiveBlock, ReactiveFunction, ReactiveInstruction, ReactiveScopeBlock, ReactiveStatement,14 ReactiveTerminal, ReactiveTerminalStatement, ReactiveValue, environment::Environment,15};1617// =============================================================================18// ReactiveFunctionVisitor trait19// =============================================================================2021/// Visitor trait for walking a ReactiveFunction tree.22///23/// Override individual `visit_*` methods to customize behavior; call the24/// corresponding `traverse_*` to continue the default recursion.25///26/// TS: `class ReactiveFunctionVisitor<TState>`27pub trait ReactiveFunctionVisitor {28 type State;2930 /// Provide Environment access. The default traversal uses this to include31 /// FunctionExpression/ObjectMethod context places as operands (matching the32 /// TS `eachInstructionValueOperand` behavior).33 fn env(&self) -> &Environment;3435 fn visit_id(&self, _id: EvaluationOrder, _state: &mut Self::State) {}3637 fn visit_place(&self, _id: EvaluationOrder, _place: &Place, _state: &mut Self::State) {}3839 fn visit_lvalue(&self, _id: EvaluationOrder, _lvalue: &Place, _state: &mut Self::State) {}4041 fn visit_param(&self, _place: &Place, _state: &mut Self::State) {}4243 /// Walk an inner HIR function, visiting params, instructions (with lvalues,44 /// value-lvalues, operands, and nested functions), and terminal operands.45 /// TS: `visitHirFunction`46 fn visit_hir_function(&self, func_id: FunctionId, state: &mut Self::State) {47 let inner_func = &self.env().functions[func_id.0 as usize];48 for param in &inner_func.params {49 let place = match param {50 ParamPattern::Place(p) => p,51 ParamPattern::Spread(s) => &s.place,52 };53 self.visit_param(place, state);54 }55 let block_ids: Vec<_> = inner_func.body.blocks.keys().copied().collect();56 for block_id in block_ids {57 let inner_func = &self.env().functions[func_id.0 as usize];58 let block = &inner_func.body.blocks[&block_id];59 let instr_ids: Vec<_> = block.instructions.clone();60 let terminal_operands: Vec<Place> =61 react_compiler_hir::visitors::each_terminal_operand(&block.terminal);62 let terminal_id = block.terminal.evaluation_order();6364 for instr_id in &instr_ids {65 let inner_func = &self.env().functions[func_id.0 as usize];66 let instr = &inner_func.instructions[instr_id.0 as usize];67 // Build a temporary ReactiveInstruction for the visitor68 let reactive_instr = ReactiveInstruction {69 id: instr.id,70 lvalue: Some(instr.lvalue.clone()),71 value: ReactiveValue::Instruction(instr.value.clone()),72 effects: None,73 loc: instr.loc,74 };75 self.visit_instruction(&reactive_instr, state);76 // Recurse into nested functions77 match &instr.value {78 InstructionValue::FunctionExpression { lowered_func, .. }79 | InstructionValue::ObjectMethod { lowered_func, .. } => {80 self.visit_hir_function(lowered_func.func, state);81 }82 _ => {}83 }84 }85 for operand in &terminal_operands {86 self.visit_place(terminal_id, operand, state);87 }88 }89 }9091 fn visit_value(&self, id: EvaluationOrder, value: &ReactiveValue, state: &mut Self::State) {92 self.traverse_value(id, value, state);93 }9495 fn traverse_value(&self, id: EvaluationOrder, value: &ReactiveValue, state: &mut Self::State) {96 match value {97 ReactiveValue::OptionalExpression { value: inner, .. } => {98 self.visit_value(id, inner, state);99 }100 ReactiveValue::LogicalExpression { left, right, .. } => {101 self.visit_value(id, left, state);102 self.visit_value(id, right, state);103 }104 ReactiveValue::ConditionalExpression {105 test,106 consequent,107 alternate,108 ..109 } => {110 self.visit_value(id, test, state);111 self.visit_value(id, consequent, state);112 self.visit_value(id, alternate, state);113 }114 ReactiveValue::SequenceExpression {115 instructions,116 id: seq_id,117 value: inner,118 ..119 } => {120 for instr in instructions {121 self.visit_instruction(instr, state);122 }123 self.visit_value(*seq_id, inner, state);124 }125 ReactiveValue::Instruction(instr_value) => {126 let operands = react_compiler_hir::visitors::each_instruction_value_operand(127 instr_value,128 self.env(),129 );130 for place in &operands {131 self.visit_place(id, place, state);132 }133 }134 }135 }136137 fn visit_instruction(&self, instruction: &ReactiveInstruction, state: &mut Self::State) {138 self.traverse_instruction(instruction, state);139 }140141 fn traverse_instruction(&self, instruction: &ReactiveInstruction, state: &mut Self::State) {142 self.visit_id(instruction.id, state);143 // Visit instruction-level lvalue144 if let Some(lvalue) = &instruction.lvalue {145 self.visit_lvalue(instruction.id, lvalue, state);146 }147 // Visit value-level lvalues (TS: eachInstructionValueLValue)148 if let ReactiveValue::Instruction(iv) = &instruction.value {149 for place in react_compiler_hir::visitors::each_instruction_value_lvalue(iv) {150 self.visit_lvalue(instruction.id, &place, state);151 }152 }153 self.visit_value(instruction.id, &instruction.value, state);154 }155156 fn visit_terminal(&self, stmt: &ReactiveTerminalStatement, state: &mut Self::State) {157 self.traverse_terminal(stmt, state);158 }159160 fn traverse_terminal(&self, stmt: &ReactiveTerminalStatement, state: &mut Self::State) {161 let terminal = &stmt.terminal;162 let id = terminal_id(terminal);163 self.visit_id(id, state);164 match terminal {165 ReactiveTerminal::Break { .. } | ReactiveTerminal::Continue { .. } => {}166 ReactiveTerminal::Return { value, id, .. } => {167 self.visit_place(*id, value, state);168 }169 ReactiveTerminal::Throw { value, id, .. } => {170 self.visit_place(*id, value, state);171 }172 ReactiveTerminal::For {173 init,174 test,175 update,176 loop_block,177 id,178 ..179 } => {180 self.visit_value(*id, init, state);181 self.visit_value(*id, test, state);182 self.visit_block(loop_block, state);183 if let Some(update) = update {184 self.visit_value(*id, update, state);185 }186 }187 ReactiveTerminal::ForOf {188 init,189 test,190 loop_block,191 id,192 ..193 } => {194 self.visit_value(*id, init, state);195 self.visit_value(*id, test, state);196 self.visit_block(loop_block, state);197 }198 ReactiveTerminal::ForIn {199 init,200 loop_block,201 id,202 ..203 } => {204 self.visit_value(*id, init, state);205 self.visit_block(loop_block, state);206 }207 ReactiveTerminal::DoWhile {208 loop_block,209 test,210 id,211 ..212 } => {213 self.visit_block(loop_block, state);214 self.visit_value(*id, test, state);215 }216 ReactiveTerminal::While {217 test,218 loop_block,219 id,220 ..221 } => {222 self.visit_value(*id, test, state);223 self.visit_block(loop_block, state);224 }225 ReactiveTerminal::If {226 test,227 consequent,228 alternate,229 id,230 ..231 } => {232 self.visit_place(*id, test, state);233 self.visit_block(consequent, state);234 if let Some(alt) = alternate {235 self.visit_block(alt, state);236 }237 }238 ReactiveTerminal::Switch {239 test, cases, id, ..240 } => {241 self.visit_place(*id, test, state);242 for case in cases {243 if let Some(t) = &case.test {244 self.visit_place(*id, t, state);245 }246 if let Some(block) = &case.block {247 self.visit_block(block, state);248 }249 }250 }251 ReactiveTerminal::Label { block, .. } => {252 self.visit_block(block, state);253 }254 ReactiveTerminal::Try {255 block,256 handler_binding,257 handler,258 id,259 ..260 } => {261 self.visit_block(block, state);262 if let Some(binding) = handler_binding {263 self.visit_place(*id, binding, state);264 }265 self.visit_block(handler, state);266 }267 }268 }269270 fn visit_scope(&self, scope: &ReactiveScopeBlock, state: &mut Self::State) {271 self.traverse_scope(scope, state);272 }273274 fn traverse_scope(&self, scope: &ReactiveScopeBlock, state: &mut Self::State) {275 self.visit_block(&scope.instructions, state);276 }277278 fn visit_pruned_scope(&self, scope: &PrunedReactiveScopeBlock, state: &mut Self::State) {279 self.traverse_pruned_scope(scope, state);280 }281282 fn traverse_pruned_scope(&self, scope: &PrunedReactiveScopeBlock, state: &mut Self::State) {283 self.visit_block(&scope.instructions, state);284 }285286 fn visit_block(&self, block: &ReactiveBlock, state: &mut Self::State) {287 self.traverse_block(block, state);288 }289290 fn traverse_block(&self, block: &ReactiveBlock, state: &mut Self::State) {291 for stmt in block {292 match stmt {293 ReactiveStatement::Instruction(instr) => {294 self.visit_instruction(instr, state);295 }296 ReactiveStatement::Scope(scope) => {297 self.visit_scope(scope, state);298 }299 ReactiveStatement::PrunedScope(scope) => {300 self.visit_pruned_scope(scope, state);301 }302 ReactiveStatement::Terminal(terminal) => {303 self.visit_terminal(terminal, state);304 }305 }306 }307 }308}309310/// Entry point for visiting a reactive function.311/// TS: `visitReactiveFunction`312pub fn visit_reactive_function<V: ReactiveFunctionVisitor>(313 func: &ReactiveFunction,314 visitor: &V,315 state: &mut V::State,316) {317 visitor.visit_block(&func.body, state);318}319320// =============================================================================321// Transformed / TransformedValue enums322// =============================================================================323324/// Result of transforming a ReactiveStatement.325/// TS: `Transformed<T>`326pub enum Transformed<T> {327 Keep,328 Remove,329 Replace(T),330 ReplaceMany(Vec<T>),331}332333/// Result of transforming a ReactiveValue.334/// TS: `TransformedValue`335#[allow(dead_code)]336pub enum TransformedValue {337 Keep,338 Replace(ReactiveValue),339}340341// =============================================================================342// ReactiveFunctionTransform trait343// =============================================================================344345/// Transform trait for modifying a ReactiveFunction tree in-place.346///347/// Extends the visitor pattern with `transform_*` methods that can modify348/// or remove statements. The `traverse_block` implementation handles applying349/// transform results to the block.350///351/// TS: `class ReactiveFunctionTransform<TState>`352pub trait ReactiveFunctionTransform {353 type State;354355 /// Provide Environment access. The default traversal uses this to include356 /// FunctionExpression/ObjectMethod context places as operands (matching the357 /// TS `eachInstructionValueOperand` behavior).358 fn env(&self) -> &Environment;359360 fn visit_id(361 &mut self,362 _id: EvaluationOrder,363 _state: &mut Self::State,364 ) -> Result<(), CompilerError> {365 Ok(())366 }367368 fn visit_place(369 &mut self,370 _id: EvaluationOrder,371 _place: &Place,372 _state: &mut Self::State,373 ) -> Result<(), CompilerError> {374 Ok(())375 }376377 fn visit_lvalue(378 &mut self,379 _id: EvaluationOrder,380 _lvalue: &Place,381 _state: &mut Self::State,382 ) -> Result<(), CompilerError> {383 Ok(())384 }385386 fn visit_value(387 &mut self,388 id: EvaluationOrder,389 value: &mut ReactiveValue,390 state: &mut Self::State,391 ) -> Result<(), CompilerError> {392 self.traverse_value(id, value, state)393 }394395 fn traverse_value(396 &mut self,397 id: EvaluationOrder,398 value: &mut ReactiveValue,399 state: &mut Self::State,400 ) -> Result<(), CompilerError> {401 match value {402 ReactiveValue::OptionalExpression { value: inner, .. } => {403 let next = self.transform_value(id, inner, state)?;404 if let TransformedValue::Replace(new_value) = next {405 **inner = new_value;406 }407 }408 ReactiveValue::LogicalExpression { left, right, .. } => {409 let next_left = self.transform_value(id, left, state)?;410 if let TransformedValue::Replace(new_value) = next_left {411 **left = new_value;412 }413 let next_right = self.transform_value(id, right, state)?;414 if let TransformedValue::Replace(new_value) = next_right {415 **right = new_value;416 }417 }418 ReactiveValue::ConditionalExpression {419 test,420 consequent,421 alternate,422 ..423 } => {424 let next_test = self.transform_value(id, test, state)?;425 if let TransformedValue::Replace(new_value) = next_test {426 **test = new_value;427 }428 let next_cons = self.transform_value(id, consequent, state)?;429 if let TransformedValue::Replace(new_value) = next_cons {430 **consequent = new_value;431 }432 let next_alt = self.transform_value(id, alternate, state)?;433 if let TransformedValue::Replace(new_value) = next_alt {434 **alternate = new_value;435 }436 }437 ReactiveValue::SequenceExpression {438 instructions,439 id: seq_id,440 value: inner,441 ..442 } => {443 let seq_id = *seq_id;444 for instr in instructions.iter_mut() {445 self.visit_instruction(instr, state)?;446 }447 let next = self.transform_value(seq_id, inner, state)?;448 if let TransformedValue::Replace(new_value) = next {449 **inner = new_value;450 }451 }452 ReactiveValue::Instruction(instr_value) => {453 // Collect operands before visiting to avoid borrow conflict454 // (self.env() borrows self immutably, self.visit_place() needs &mut self).455 let operands = react_compiler_hir::visitors::each_instruction_value_operand(456 instr_value,457 self.env(),458 );459 for place in &operands {460 self.visit_place(id, place, state)?;461 }462 }463 }464 Ok(())465 }466467 fn visit_instruction(468 &mut self,469 instruction: &mut ReactiveInstruction,470 state: &mut Self::State,471 ) -> Result<(), CompilerError> {472 self.traverse_instruction(instruction, state)473 }474475 fn transform_value(476 &mut self,477 id: EvaluationOrder,478 value: &mut ReactiveValue,479 state: &mut Self::State,480 ) -> Result<TransformedValue, CompilerError> {481 self.visit_value(id, value, state)?;482 Ok(TransformedValue::Keep)483 }484485 fn traverse_instruction(486 &mut self,487 instruction: &mut ReactiveInstruction,488 state: &mut Self::State,489 ) -> Result<(), CompilerError> {490 self.visit_id(instruction.id, state)?;491 // Visit instruction-level lvalue492 if let Some(lvalue) = &instruction.lvalue {493 self.visit_lvalue(instruction.id, lvalue, state)?;494 }495 // Visit value-level lvalues (TS: eachInstructionValueLValue)496 if let ReactiveValue::Instruction(iv) = &instruction.value {497 for place in react_compiler_hir::visitors::each_instruction_value_lvalue(iv) {498 self.visit_lvalue(instruction.id, &place, state)?;499 }500 }501 let next_value = self.transform_value(instruction.id, &mut instruction.value, state)?;502 if let TransformedValue::Replace(new_value) = next_value {503 instruction.value = new_value;504 }505 Ok(())506 }507508 fn visit_terminal(509 &mut self,510 stmt: &mut ReactiveTerminalStatement,511 state: &mut Self::State,512 ) -> Result<(), CompilerError> {513 self.traverse_terminal(stmt, state)514 }515516 fn traverse_terminal(517 &mut self,518 stmt: &mut ReactiveTerminalStatement,519 state: &mut Self::State,520 ) -> Result<(), CompilerError> {521 let terminal = &mut stmt.terminal;522 let id = terminal_id(terminal);523 self.visit_id(id, state)?;524 match terminal {525 ReactiveTerminal::Break { .. } | ReactiveTerminal::Continue { .. } => {}526 ReactiveTerminal::Return { value, id, .. } => {527 self.visit_place(*id, value, state)?;528 }529 ReactiveTerminal::Throw { value, id, .. } => {530 self.visit_place(*id, value, state)?;531 }532 ReactiveTerminal::For {533 init,534 test,535 update,536 loop_block,537 id,538 ..539 } => {540 let id = *id;541 let next_init = self.transform_value(id, init, state)?;542 if let TransformedValue::Replace(new_value) = next_init {543 *init = new_value;544 }545 let next_test = self.transform_value(id, test, state)?;546 if let TransformedValue::Replace(new_value) = next_test {547 *test = new_value;548 }549 if let Some(update) = update {550 let next_update = self.transform_value(id, update, state)?;551 if let TransformedValue::Replace(new_value) = next_update {552 *update = new_value;553 }554 }555 self.visit_block(loop_block, state)?;556 }557 ReactiveTerminal::ForOf {558 init,559 test,560 loop_block,561 id,562 ..563 } => {564 let id = *id;565 let next_init = self.transform_value(id, init, state)?;566 if let TransformedValue::Replace(new_value) = next_init {567 *init = new_value;568 }569 let next_test = self.transform_value(id, test, state)?;570 if let TransformedValue::Replace(new_value) = next_test {571 *test = new_value;572 }573 self.visit_block(loop_block, state)?;574 }575 ReactiveTerminal::ForIn {576 init,577 loop_block,578 id,579 ..580 } => {581 let id = *id;582 let next_init = self.transform_value(id, init, state)?;583 if let TransformedValue::Replace(new_value) = next_init {584 *init = new_value;585 }586 self.visit_block(loop_block, state)?;587 }588 ReactiveTerminal::DoWhile {589 loop_block,590 test,591 id,592 ..593 } => {594 let id = *id;595 self.visit_block(loop_block, state)?;596 let next_test = self.transform_value(id, test, state)?;597 if let TransformedValue::Replace(new_value) = next_test {598 *test = new_value;599 }600 }601 ReactiveTerminal::While {602 test,603 loop_block,604 id,605 ..606 } => {607 let id = *id;608 let next_test = self.transform_value(id, test, state)?;609 if let TransformedValue::Replace(new_value) = next_test {610 *test = new_value;611 }612 self.visit_block(loop_block, state)?;613 }614 ReactiveTerminal::If {615 test,616 consequent,617 alternate,618 id,619 ..620 } => {621 self.visit_place(*id, test, state)?;622 self.visit_block(consequent, state)?;623 if let Some(alt) = alternate {624 self.visit_block(alt, state)?;625 }626 }627 ReactiveTerminal::Switch {628 test, cases, id, ..629 } => {630 let id = *id;631 self.visit_place(id, test, state)?;632 for case in cases.iter_mut() {633 if let Some(t) = &case.test {634 self.visit_place(id, t, state)?;635 }636 if let Some(block) = &mut case.block {637 self.visit_block(block, state)?;638 }639 }640 }641 ReactiveTerminal::Label { block, .. } => {642 self.visit_block(block, state)?;643 }644 ReactiveTerminal::Try {645 block,646 handler_binding,647 handler,648 id,649 ..650 } => {651 let id = *id;652 self.visit_block(block, state)?;653 if let Some(binding) = handler_binding {654 self.visit_place(id, binding, state)?;655 }656 self.visit_block(handler, state)?;657 }658 }659 Ok(())660 }661662 fn visit_scope(663 &mut self,664 scope: &mut ReactiveScopeBlock,665 state: &mut Self::State,666 ) -> Result<(), CompilerError> {667 self.traverse_scope(scope, state)668 }669670 fn traverse_scope(671 &mut self,672 scope: &mut ReactiveScopeBlock,673 state: &mut Self::State,674 ) -> Result<(), CompilerError> {675 self.visit_block(&mut scope.instructions, state)676 }677678 fn visit_pruned_scope(679 &mut self,680 scope: &mut PrunedReactiveScopeBlock,681 state: &mut Self::State,682 ) -> Result<(), CompilerError> {683 self.traverse_pruned_scope(scope, state)684 }685686 fn traverse_pruned_scope(687 &mut self,688 scope: &mut PrunedReactiveScopeBlock,689 state: &mut Self::State,690 ) -> Result<(), CompilerError> {691 self.visit_block(&mut scope.instructions, state)692 }693694 fn visit_block(695 &mut self,696 block: &mut ReactiveBlock,697 state: &mut Self::State,698 ) -> Result<(), CompilerError> {699 self.traverse_block(block, state)700 }701702 fn transform_instruction(703 &mut self,704 instruction: &mut ReactiveInstruction,705 state: &mut Self::State,706 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {707 self.visit_instruction(instruction, state)?;708 Ok(Transformed::Keep)709 }710711 fn transform_terminal(712 &mut self,713 stmt: &mut ReactiveTerminalStatement,714 state: &mut Self::State,715 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {716 self.visit_terminal(stmt, state)?;717 Ok(Transformed::Keep)718 }719720 fn transform_scope(721 &mut self,722 scope: &mut ReactiveScopeBlock,723 state: &mut Self::State,724 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {725 self.visit_scope(scope, state)?;726 Ok(Transformed::Keep)727 }728729 fn transform_pruned_scope(730 &mut self,731 scope: &mut PrunedReactiveScopeBlock,732 state: &mut Self::State,733 ) -> Result<Transformed<ReactiveStatement>, CompilerError> {734 self.visit_pruned_scope(scope, state)?;735 Ok(Transformed::Keep)736 }737738 fn traverse_block(739 &mut self,740 block: &mut ReactiveBlock,741 state: &mut Self::State,742 ) -> Result<(), CompilerError> {743 let mut next_block: Option<Vec<ReactiveStatement>> = None;744 let len = block.len();745 for i in 0..len {746 // Take the statement out temporarily747 let mut stmt = std::mem::replace(748 &mut block[i],749 // Placeholder — will be overwritten or discarded750 ReactiveStatement::Instruction(ReactiveInstruction {751 id: EvaluationOrder(0),752 lvalue: None,753 value: ReactiveValue::Instruction(754 react_compiler_hir::InstructionValue::Debugger { loc: None },755 ),756 effects: None,757 loc: None,758 }),759 );760 let transformed = match &mut stmt {761 ReactiveStatement::Instruction(instr) => {762 self.transform_instruction(instr, state)?763 }764 ReactiveStatement::Scope(scope) => self.transform_scope(scope, state)?,765 ReactiveStatement::PrunedScope(scope) => {766 self.transform_pruned_scope(scope, state)?767 }768 ReactiveStatement::Terminal(terminal) => {769 self.transform_terminal(terminal, state)?770 }771 };772 match transformed {773 Transformed::Keep => {774 if let Some(ref mut nb) = next_block {775 nb.push(stmt);776 } else {777 // Put it back778 block[i] = stmt;779 }780 }781 Transformed::Remove => {782 if next_block.is_none() {783 next_block = Some(block[..i].to_vec());784 }785 }786 Transformed::Replace(replacement) => {787 if next_block.is_none() {788 next_block = Some(block[..i].to_vec());789 }790 next_block.as_mut().unwrap().push(replacement);791 }792 Transformed::ReplaceMany(replacements) => {793 if next_block.is_none() {794 next_block = Some(block[..i].to_vec());795 }796 next_block.as_mut().unwrap().extend(replacements);797 }798 }799 }800 if let Some(nb) = next_block {801 *block = nb;802 }803 Ok(())804 }805}806807/// Entry point for transforming a reactive function.808/// TS: `visitReactiveFunction` (used with transforms too)809pub fn transform_reactive_function<T: ReactiveFunctionTransform>(810 func: &mut ReactiveFunction,811 transform: &mut T,812 state: &mut T::State,813) -> Result<(), CompilerError> {814 transform.visit_block(&mut func.body, state)815}816817// =============================================================================818// Helper: extract terminal ID819// =============================================================================820821fn terminal_id(terminal: &ReactiveTerminal) -> EvaluationOrder {822 match terminal {823 ReactiveTerminal::Break { id, .. }824 | ReactiveTerminal::Continue { id, .. }825 | ReactiveTerminal::Return { id, .. }826 | ReactiveTerminal::Throw { id, .. }827 | ReactiveTerminal::Switch { id, .. }828 | ReactiveTerminal::DoWhile { id, .. }829 | ReactiveTerminal::While { id, .. }830 | ReactiveTerminal::For { id, .. }831 | ReactiveTerminal::ForOf { id, .. }832 | ReactiveTerminal::ForIn { id, .. }833 | ReactiveTerminal::If { id, .. }834 | ReactiveTerminal::Label { id, .. }835 | ReactiveTerminal::Try { id, .. } => *id,836 }837}
Findings
✓ No findings reported for this file.