compiler/crates/react_compiler_reactive_scopes/src/visitors.rs RUST 838 lines View on github.com → Search inside
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.

Get this view in your editor

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