compiler/crates/react_compiler_inference/src/infer_reactive_places.rs RUST 786 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//! Infers which `Place`s are reactive.7//!8//! Ported from TypeScript `src/Inference/InferReactivePlaces.ts`.9//!10//! A place is reactive if it derives from any source of reactivity:11//! 1. Props (component parameters may change between renders)12//! 2. Hooks (can access state or context)13//! 3. `use` operator (can access context)14//! 4. Mutation with reactive operands15//! 5. Conditional assignment based on reactive control flow1617use rustc_hash::{FxHashMap, FxHashSet};1819use react_compiler_diagnostics::{CompilerDiagnostic, ErrorCategory};20use react_compiler_hir::dominator::post_dominator_frontier;21use react_compiler_hir::environment::Environment;22use react_compiler_hir::object_shape::HookKind;23use react_compiler_hir::visitors;24use react_compiler_hir::{25    BlockId, Effect, FunctionId, HirFunction, IdentifierId, InstructionValue, ParamPattern,26    Terminal, Type,27};2829use react_compiler_utils::DisjointSet;3031use crate::infer_reactive_scope_variables::find_disjoint_mutable_values;3233// =============================================================================34// Public API35// =============================================================================3637/// Infer which places in a function are reactive.38///39/// Corresponds to TS `inferReactivePlaces(fn: HIRFunction): void`.40pub fn infer_reactive_places(41    func: &mut HirFunction,42    env: &mut Environment,43) -> Result<(), CompilerDiagnostic> {44    let mut aliased_identifiers = find_disjoint_mutable_values(func, env);45    let mut reactive_map = ReactivityMap::new(&mut aliased_identifiers);46    let mut stable_sidemap = StableSidemap::new();4748    // Mark all function parameters as reactive49    for param in &func.params {50        let place = match param {51            ParamPattern::Place(p) => p,52            ParamPattern::Spread(s) => &s.place,53        };54        reactive_map.mark_reactive(place.identifier);55    }5657    // Compute control dominators58    let post_dominators = react_compiler_hir::dominator::compute_post_dominator_tree(59        func,60        env.next_block_id().0,61        false,62    )?;6364    // Collect block IDs for iteration65    let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();6667    // Track phi operand reactive flags during fixpoint.68    // In TS, isReactive() sets place.reactive as a side effect. But when a phi69    // is already reactive, the TS `continue`s and skips operand processing.70    // We track which phi operand Places should be marked reactive.71    // Key: (block_id, phi_idx, operand_idx), Value: should be reactive72    let mut phi_operand_reactive: FxHashMap<(BlockId, usize, usize), bool> = FxHashMap::default();7374    // Fixpoint iteration — compute reactive set75    loop {76        for block_id in &block_ids {77            let block = func.body.blocks.get(block_id).unwrap();78            let has_reactive_control =79                is_reactive_controlled_block(block.id, func, &post_dominators, &mut reactive_map);8081            // Process phi nodes82            let block = func.body.blocks.get(block_id).unwrap();83            for (phi_idx, phi) in block.phis.iter().enumerate() {84                if reactive_map.is_reactive(phi.place.identifier) {85                    // TS does `continue` here — skips operand isReactive calls.86                    // phi operand reactive flags stay as they were from last visit.87                    continue;88                }89                let mut is_phi_reactive = false;90                for (op_idx, (_pred, operand)) in phi.operands.iter().enumerate() {91                    let op_reactive = reactive_map.is_reactive(operand.identifier);92                    // Record the reactive state for this operand at this point93                    phi_operand_reactive.insert((*block_id, phi_idx, op_idx), op_reactive);94                    if op_reactive {95                        is_phi_reactive = true;96                        break; // TS breaks here — remaining operands NOT visited97                    }98                }99                if is_phi_reactive {100                    reactive_map.mark_reactive(phi.place.identifier);101                } else {102                    for (pred, _operand) in &phi.operands {103                        if is_reactive_controlled_block(104                            *pred,105                            func,106                            &post_dominators,107                            &mut reactive_map,108                        ) {109                            reactive_map.mark_reactive(phi.place.identifier);110                            break;111                        }112                    }113                }114            }115116            // Process instructions117            let block = func.body.blocks.get(block_id).unwrap();118            for instr_id in &block.instructions {119                let instr = &func.instructions[instr_id.0 as usize];120121                // Handle stable identifier sources122                stable_sidemap.handle_instruction(instr, env);123124                let value = &instr.value;125126                // Check if any operand is reactive127                let mut has_reactive_input = false;128                let operands: Vec<IdentifierId> =129                    visitors::each_instruction_value_operand(value, env)130                        .into_iter()131                        .map(|p| p.identifier)132                        .collect();133                for &op_id in &operands {134                    let reactive = reactive_map.is_reactive(op_id);135                    has_reactive_input = has_reactive_input || reactive;136                }137138                // Hooks and `use` operator are sources of reactivity139                match value {140                    InstructionValue::CallExpression { callee, .. } => {141                        let callee_ty = &env.types142                            [env.identifiers[callee.identifier.0 as usize].type_.0 as usize];143                        if get_hook_kind_for_type(env, callee_ty)?.is_some()144                            || is_use_operator_type(callee_ty)145                        {146                            has_reactive_input = true;147                        }148                    }149                    InstructionValue::MethodCall { property, .. } => {150                        let property_ty = &env.types151                            [env.identifiers[property.identifier.0 as usize].type_.0 as usize];152                        if get_hook_kind_for_type(env, property_ty)?.is_some()153                            || is_use_operator_type(property_ty)154                        {155                            has_reactive_input = true;156                        }157                    }158                    _ => {}159                }160161                if has_reactive_input {162                    // Mark lvalues reactive (unless stable)163                    let lvalue_ids: Vec<IdentifierId> = visitors::each_instruction_lvalue(instr)164                        .into_iter()165                        .map(|p| p.identifier)166                        .collect();167                    for lvalue_id in lvalue_ids {168                        if stable_sidemap.is_stable(lvalue_id) {169                            continue;170                        }171                        reactive_map.mark_reactive(lvalue_id);172                    }173                }174175                if has_reactive_input || has_reactive_control {176                    // Mark mutable operands reactive177                    let operand_places = visitors::each_instruction_value_operand(value, env);178                    for op_place in &operand_places {179                        match op_place.effect {180                            Effect::Capture181                            | Effect::Store182                            | Effect::ConditionallyMutate183                            | Effect::ConditionallyMutateIterator184                            | Effect::Mutate => {185                                let op_range =186                                    &env.identifiers[op_place.identifier.0 as usize].mutable_range;187                                if op_range.contains(instr.id) {188                                    reactive_map.mark_reactive(op_place.identifier);189                                }190                            }191                            Effect::Freeze | Effect::Read => {192                                // no-op193                            }194                            Effect::Unknown => {195                                return Err(CompilerDiagnostic::new(196                                    ErrorCategory::Invariant,197                                    &format!("Unexpected unknown effect at {:?}", op_place.loc),198                                    None,199                                ));200                            }201                        }202                    }203                }204            }205206            // Process terminal operands (just to mark them reactive for output)207            for op in visitors::each_terminal_operand(&block.terminal) {208                reactive_map.is_reactive(op.identifier);209            }210        }211212        if !reactive_map.snapshot() {213            break;214        }215    }216217    // Propagate reactivity to inner functions (read-only phase, just queries reactive_map)218    propagate_reactivity_to_inner_functions_outer(func, env, &mut reactive_map);219220    // Now apply reactive flags by replaying the traversal pattern.221    apply_reactive_flags_replay(222        func,223        env,224        &mut reactive_map,225        &mut stable_sidemap,226        &phi_operand_reactive,227    );228229    Ok(())230}231232// =============================================================================233// ReactivityMap234// =============================================================================235236struct ReactivityMap<'a> {237    has_changes: bool,238    reactive: FxHashSet<IdentifierId>,239    aliased_identifiers: &'a mut DisjointSet<IdentifierId>,240}241242impl<'a> ReactivityMap<'a> {243    fn new(aliased_identifiers: &'a mut DisjointSet<IdentifierId>) -> Self {244        ReactivityMap {245            has_changes: false,246            reactive: FxHashSet::default(),247            aliased_identifiers,248        }249    }250251    fn is_reactive(&mut self, id: IdentifierId) -> bool {252        let canonical = self.aliased_identifiers.find_opt(id).unwrap_or(id);253        self.reactive.contains(&canonical)254    }255256    fn mark_reactive(&mut self, id: IdentifierId) {257        let canonical = self.aliased_identifiers.find_opt(id).unwrap_or(id);258        if self.reactive.insert(canonical) {259            self.has_changes = true;260        }261    }262263    /// Reset change tracking, returns true if there were changes.264    fn snapshot(&mut self) -> bool {265        let had_changes = self.has_changes;266        self.has_changes = false;267        had_changes268    }269}270271// =============================================================================272// StableSidemap273// =============================================================================274275struct StableSidemap {276    map: FxHashMap<IdentifierId, bool>,277}278279impl StableSidemap {280    fn new() -> Self {281        StableSidemap {282            map: FxHashMap::default(),283        }284    }285286    fn handle_instruction(&mut self, instr: &react_compiler_hir::Instruction, env: &Environment) {287        let lvalue_id = instr.lvalue.identifier;288        let value = &instr.value;289290        match value {291            InstructionValue::CallExpression { callee, .. } => {292                let callee_ty =293                    &env.types[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];294                if evaluates_to_stable_type_or_container(env, callee_ty) {295                    let lvalue_ty =296                        &env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];297                    if is_stable_type(lvalue_ty) {298                        self.map.insert(lvalue_id, true);299                    } else {300                        self.map.insert(lvalue_id, false);301                    }302                }303            }304            InstructionValue::MethodCall { property, .. } => {305                let property_ty =306                    &env.types[env.identifiers[property.identifier.0 as usize].type_.0 as usize];307                if evaluates_to_stable_type_or_container(env, property_ty) {308                    let lvalue_ty =309                        &env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];310                    if is_stable_type(lvalue_ty) {311                        self.map.insert(lvalue_id, true);312                    } else {313                        self.map.insert(lvalue_id, false);314                    }315                }316            }317            InstructionValue::PropertyLoad { object, .. } => {318                let source_id = object.identifier;319                if self.map.contains_key(&source_id) {320                    let lvalue_ty =321                        &env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];322                    if is_stable_type_container(lvalue_ty) {323                        self.map.insert(lvalue_id, false);324                    } else if is_stable_type(lvalue_ty) {325                        self.map.insert(lvalue_id, true);326                    }327                }328            }329            InstructionValue::Destructure { value: val, .. } => {330                let source_id = val.identifier;331                if self.map.contains_key(&source_id) {332                    let lvalue_ids: Vec<IdentifierId> = visitors::each_instruction_lvalue(instr)333                        .into_iter()334                        .map(|p| p.identifier)335                        .collect();336                    for lid in lvalue_ids {337                        let lid_ty = &env.types[env.identifiers[lid.0 as usize].type_.0 as usize];338                        if is_stable_type_container(lid_ty) {339                            self.map.insert(lid, false);340                        } else if is_stable_type(lid_ty) {341                            self.map.insert(lid, true);342                        }343                    }344                }345            }346            InstructionValue::StoreLocal {347                lvalue, value: val, ..348            } => {349                if let Some(&entry) = self.map.get(&val.identifier) {350                    self.map.insert(lvalue_id, entry);351                    self.map.insert(lvalue.place.identifier, entry);352                }353            }354            InstructionValue::LoadLocal { place, .. } => {355                if let Some(&entry) = self.map.get(&place.identifier) {356                    self.map.insert(lvalue_id, entry);357                }358            }359            _ => {}360        }361    }362363    fn is_stable(&self, id: IdentifierId) -> bool {364        self.map.get(&id).copied().unwrap_or(false)365    }366}367368// =============================================================================369// Control dominators (ported from ControlDominators.ts)370// =============================================================================371372fn is_reactive_controlled_block(373    block_id: BlockId,374    func: &HirFunction,375    post_dominators: &react_compiler_hir::dominator::PostDominator,376    reactive_map: &mut ReactivityMap,377) -> bool {378    let frontier = post_dominator_frontier(func, post_dominators, block_id);379    for frontier_block_id in &frontier {380        let control_block = func.body.blocks.get(frontier_block_id).unwrap();381        match &control_block.terminal {382            Terminal::If { test, .. } | Terminal::Branch { test, .. } => {383                if reactive_map.is_reactive(test.identifier) {384                    return true;385                }386            }387            Terminal::Switch { test, cases, .. } => {388                if reactive_map.is_reactive(test.identifier) {389                    return true;390                }391                for case in cases {392                    if let Some(ref case_test) = case.test {393                        if reactive_map.is_reactive(case_test.identifier) {394                            return true;395                        }396                    }397                }398            }399            _ => {}400        }401    }402    false403}404405// =============================================================================406// Type helpers (ported from HIR.ts)407// =============================================================================408409use react_compiler_hir::is_use_operator_type;410411fn get_hook_kind_for_type<'a>(412    env: &'a Environment,413    ty: &Type,414) -> Result<Option<&'a HookKind>, CompilerDiagnostic> {415    env.get_hook_kind_for_type(ty)416}417418fn is_stable_type(ty: &Type) -> bool {419    match ty {420        Type::Function {421            shape_id: Some(id), ..422        } => {423            matches!(424                id.as_str(),425                "BuiltInSetState"426                    | "BuiltInSetActionState"427                    | "BuiltInDispatch"428                    | "BuiltInStartTransition"429                    | "BuiltInSetOptimistic"430            )431        }432        Type::Object { shape_id: Some(id) } => {433            matches!(id.as_str(), "BuiltInUseRefId")434        }435        _ => false,436    }437}438439fn is_stable_type_container(ty: &Type) -> bool {440    match ty {441        Type::Object { shape_id: Some(id) } => {442            matches!(443                id.as_str(),444                "BuiltInUseState"445                    | "BuiltInUseActionState"446                    | "BuiltInUseReducer"447                    | "BuiltInUseOptimistic"448                    | "BuiltInUseTransition"449            )450        }451        _ => false,452    }453}454455fn evaluates_to_stable_type_or_container(env: &Environment, callee_ty: &Type) -> bool {456    if let Some(hook_kind) = get_hook_kind_for_type(env, callee_ty).ok().flatten() {457        matches!(458            hook_kind,459            HookKind::UseState460                | HookKind::UseReducer461                | HookKind::UseActionState462                | HookKind::UseRef463                | HookKind::UseTransition464                | HookKind::UseOptimistic465        )466    } else {467        false468    }469}470471// =============================================================================472// Propagate reactivity to inner functions473// =============================================================================474475fn propagate_reactivity_to_inner_functions_outer(476    func: &HirFunction,477    env: &Environment,478    reactive_map: &mut ReactivityMap,479) {480    for (_block_id, block) in &func.body.blocks {481        for instr_id in &block.instructions {482            let instr = &func.instructions[instr_id.0 as usize];483            match &instr.value {484                InstructionValue::FunctionExpression { lowered_func, .. }485                | InstructionValue::ObjectMethod { lowered_func, .. } => {486                    propagate_reactivity_to_inner_functions_inner(487                        lowered_func.func,488                        env,489                        reactive_map,490                    );491                }492                _ => {}493            }494        }495    }496}497498fn propagate_reactivity_to_inner_functions_inner(499    func_id: FunctionId,500    env: &Environment,501    reactive_map: &mut ReactivityMap,502) {503    let inner_func = &env.functions[func_id.0 as usize];504505    for (_block_id, block) in &inner_func.body.blocks {506        for instr_id in &block.instructions {507            let instr = &inner_func.instructions[instr_id.0 as usize];508509            for op in visitors::each_instruction_value_operand(&instr.value, env) {510                reactive_map.is_reactive(op.identifier);511            }512513            match &instr.value {514                InstructionValue::FunctionExpression { lowered_func, .. }515                | InstructionValue::ObjectMethod { lowered_func, .. } => {516                    propagate_reactivity_to_inner_functions_inner(517                        lowered_func.func,518                        env,519                        reactive_map,520                    );521                }522                _ => {}523            }524        }525526        for op in visitors::each_terminal_operand(&block.terminal) {527            reactive_map.is_reactive(op.identifier);528        }529    }530}531532// =============================================================================533// Apply reactive flags to the HIR (replay pass)534// =============================================================================535536fn apply_reactive_flags_replay(537    func: &mut HirFunction,538    env: &mut Environment,539    reactive_map: &mut ReactivityMap,540    stable_sidemap: &mut StableSidemap,541    phi_operand_reactive: &FxHashMap<(BlockId, usize, usize), bool>,542) {543    let reactive_ids = build_reactive_id_set(reactive_map);544545    // 1. Mark params546    for param in &mut func.params {547        let place = match param {548            ParamPattern::Place(p) => p,549            ParamPattern::Spread(s) => &mut s.place,550        };551        place.reactive = true;552    }553554    // 2. Walk blocks555    let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();556557    for block_id in &block_ids {558        let block = func.body.blocks.get(block_id).unwrap();559560        // 2a. Phi nodes561        let phi_count = block.phis.len();562        for phi_idx in 0..phi_count {563            let block = func.body.blocks.get_mut(block_id).unwrap();564            let phi = &mut block.phis[phi_idx];565566            if reactive_ids.contains(&phi.place.identifier) {567                phi.place.reactive = true;568            }569570            for (op_idx, (_pred, operand)) in phi.operands.iter_mut().enumerate() {571                if let Some(&is_reactive) = phi_operand_reactive.get(&(*block_id, phi_idx, op_idx))572                {573                    if is_reactive {574                        operand.reactive = true;575                    }576                }577            }578        }579580        // 2b. Instructions581        let block = func.body.blocks.get(block_id).unwrap();582        let instr_ids: Vec<react_compiler_hir::InstructionId> = block.instructions.clone();583584        for instr_id in &instr_ids {585            let instr = &func.instructions[instr_id.0 as usize];586587            // Compute hasReactiveInput by checking value operands588            let value_operand_ids: Vec<IdentifierId> =589                visitors::each_instruction_value_operand(&instr.value, env)590                    .into_iter()591                    .map(|p| p.identifier)592                    .collect();593            let mut has_reactive_input = false;594            for &op_id in &value_operand_ids {595                if reactive_ids.contains(&op_id) {596                    has_reactive_input = true;597                }598            }599600            // Check hooks/use601            match &instr.value {602                InstructionValue::CallExpression { callee, .. } => {603                    let callee_ty =604                        &env.types[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];605                    if get_hook_kind_for_type(env, callee_ty)606                        .ok()607                        .flatten()608                        .is_some()609                        || is_use_operator_type(callee_ty)610                    {611                        has_reactive_input = true;612                    }613                }614                InstructionValue::MethodCall { property, .. } => {615                    let property_ty = &env.types616                        [env.identifiers[property.identifier.0 as usize].type_.0 as usize];617                    if get_hook_kind_for_type(env, property_ty)618                        .ok()619                        .flatten()620                        .is_some()621                        || is_use_operator_type(property_ty)622                    {623                        has_reactive_input = true;624                    }625                }626                _ => {}627            }628629            // Value operands: set reactive flag using canonical visitor630            let instr = &mut func.instructions[instr_id.0 as usize];631            visitors::for_each_instruction_value_operand_mut(&mut instr.value, &mut |place| {632                if reactive_ids.contains(&place.identifier) {633                    place.reactive = true;634                }635            });636            // FunctionExpression/ObjectMethod context variables require env access637            if let InstructionValue::FunctionExpression { lowered_func, .. }638            | InstructionValue::ObjectMethod { lowered_func, .. } = &mut instr.value639            {640                let inner_func = &mut env.functions[lowered_func.func.0 as usize];641                for ctx in &mut inner_func.context {642                    if reactive_ids.contains(&ctx.identifier) {643                        ctx.reactive = true;644                    }645                }646            }647648            // Lvalues: markReactive is called only when hasReactiveInput649            if has_reactive_input {650                let lvalue_id = instr.lvalue.identifier;651                if !stable_sidemap.is_stable(lvalue_id) && reactive_ids.contains(&lvalue_id) {652                    instr.lvalue.reactive = true;653                }654                // Handle value lvalues — includes DeclareContext/StoreContext which655                // for_each_instruction_lvalue_mut skips, so we use a direct match.656                match &mut instr.value {657                    InstructionValue::DeclareLocal { lvalue, .. }658                    | InstructionValue::DeclareContext { lvalue, .. }659                    | InstructionValue::StoreLocal { lvalue, .. }660                    | InstructionValue::StoreContext { lvalue, .. } => {661                        let id = lvalue.place.identifier;662                        if !stable_sidemap.is_stable(id) && reactive_ids.contains(&id) {663                            lvalue.place.reactive = true;664                        }665                    }666                    InstructionValue::Destructure { lvalue, .. } => {667                        visitors::for_each_pattern_operand_mut(&mut lvalue.pattern, &mut |place| {668                            if !stable_sidemap.is_stable(place.identifier)669                                && reactive_ids.contains(&place.identifier)670                            {671                                place.reactive = true;672                            }673                        });674                    }675                    InstructionValue::PrefixUpdate { lvalue, .. }676                    | InstructionValue::PostfixUpdate { lvalue, .. } => {677                        let id = lvalue.identifier;678                        if !stable_sidemap.is_stable(id) && reactive_ids.contains(&id) {679                            lvalue.reactive = true;680                        }681                    }682                    _ => {}683                }684            }685        }686687        // 2c. Terminal operands688        let block = func.body.blocks.get_mut(block_id).unwrap();689        visitors::for_each_terminal_operand_mut(&mut block.terminal, &mut |place| {690            if reactive_ids.contains(&place.identifier) {691                place.reactive = true;692            }693        });694    }695696    // 3. Apply to inner functions697    apply_reactive_flags_to_inner_functions(func, env, &reactive_ids);698}699700fn build_reactive_id_set(reactive_map: &mut ReactivityMap) -> FxHashSet<IdentifierId> {701    let mut result = FxHashSet::default();702    for &id in &reactive_map.reactive {703        result.insert(id);704    }705    let reactive = &reactive_map.reactive;706    reactive_map.aliased_identifiers.for_each(|id, canonical| {707        if reactive.contains(&canonical) {708            result.insert(id);709        }710    });711    result712}713714fn apply_reactive_flags_to_inner_functions(715    func: &HirFunction,716    env: &mut Environment,717    reactive_ids: &FxHashSet<IdentifierId>,718) {719    for (_block_id, block) in &func.body.blocks {720        for instr_id in &block.instructions {721            let instr = &func.instructions[instr_id.0 as usize];722            match &instr.value {723                InstructionValue::FunctionExpression { lowered_func, .. }724                | InstructionValue::ObjectMethod { lowered_func, .. } => {725                    apply_reactive_flags_to_inner_func(lowered_func.func, env, reactive_ids);726                }727                _ => {}728            }729        }730    }731}732733fn apply_reactive_flags_to_inner_func(734    func_id: FunctionId,735    env: &mut Environment,736    reactive_ids: &FxHashSet<IdentifierId>,737) {738    // Collect nested function IDs first to avoid borrow issues739    let nested_func_ids: Vec<FunctionId> = {740        let func = &env.functions[func_id.0 as usize];741        let mut ids = Vec::new();742        for (_block_id, block) in &func.body.blocks {743            for instr_id in &block.instructions {744                let instr = &func.instructions[instr_id.0 as usize];745                match &instr.value {746                    InstructionValue::FunctionExpression { lowered_func, .. }747                    | InstructionValue::ObjectMethod { lowered_func, .. } => {748                        ids.push(lowered_func.func);749                    }750                    _ => {}751                }752            }753        }754        ids755    };756757    // Apply reactive flags using canonical visitors758    let inner_func = &mut env.functions[func_id.0 as usize];759    for (_block_id, block) in &mut inner_func.body.blocks {760        for instr_id in &block.instructions {761            let instr = &mut inner_func.instructions[instr_id.0 as usize];762            visitors::for_each_instruction_value_operand_mut(&mut instr.value, &mut |place| {763                if reactive_ids.contains(&place.identifier) {764                    place.reactive = true;765                }766            });767        }768        visitors::for_each_terminal_operand_mut(&mut block.terminal, &mut |place| {769            if reactive_ids.contains(&place.identifier) {770                place.reactive = true;771            }772        });773    }774775    // Recurse into nested functions, and set reactive on their context variables776    for nested_id in nested_func_ids {777        let nested_func = &mut env.functions[nested_id.0 as usize];778        for ctx in &mut nested_func.context {779            if reactive_ids.contains(&ctx.identifier) {780                ctx.reactive = true;781            }782        }783        apply_reactive_flags_to_inner_func(nested_id, env, reactive_ids);784    }785}

Code quality findings 39

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get(block_id).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get(block_id).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get(block_id).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
[env.identifiers[property.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.identifiers[op_place.identifier.0 as usize].mutable_range;
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[property.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[lvalue_id.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let lid_ty = &env.types[env.identifiers[lid.0 as usize].type_.0 as usize];
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let control_block = func.body.blocks.get(frontier_block_id).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let inner_func = &env.functions[func_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &inner_func.instructions[instr_id.0 as usize];
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get(block_id).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get_mut(block_id).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let phi = &mut block.phis[phi_idx];
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get(block_id).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&env.types[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
[env.identifiers[property.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &mut func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let inner_func = &mut env.functions[lowered_func.func.0 as usize];
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let block = func.body.blocks.get_mut(block_id).unwrap();
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let func = &env.functions[func_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let inner_func = &mut env.functions[func_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &mut inner_func.instructions[instr_id.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let nested_func = &mut env.functions[nested_id.0 as usize];
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &instr.value {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &instr.value {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let instr_ids: Vec<react_compiler_hir::InstructionId> = block.instructions.clone();
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &instr.value {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &instr.value {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
ids.push(lowered_func.func);

Get this view in your editor

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