compiler/crates/react_compiler_inference/src/propagate_scope_dependencies_hir.rs RUST 2,348 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,348.
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//! Propagates scope dependencies through the HIR, computing which values each7//! reactive scope depends on.8//!9//! Ported from TypeScript:10//! - `src/HIR/PropagateScopeDependenciesHIR.ts`11//! - `src/HIR/CollectOptionalChainDependencies.ts`12//! - `src/HIR/CollectHoistablePropertyLoads.ts`13//! - `src/HIR/DeriveMinimalDependenciesHIR.ts`1415use indexmap::IndexMap;16use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};17use std::collections::BTreeSet;1819use react_compiler_hir::environment::Environment;20use react_compiler_hir::visitors::{ScopeBlockInfo, ScopeBlockTraversal};21use react_compiler_hir::{22    BasicBlock, BlockId, DeclarationId, DependencyPathEntry, EvaluationOrder, FunctionId,23    GotoVariant, HirFunction, IdentifierId, Instruction, InstructionId, InstructionKind,24    InstructionValue, MutableRange, ParamPattern, Place, PlaceOrSpread, PropertyLiteral,25    ReactFunctionType, ReactiveScopeDependency, ScopeId, Terminal, Type, visitors,26};2728// =============================================================================29// Public entry point30// =============================================================================3132/// Main entry point: propagate scope dependencies through the HIR.33/// Corresponds to TS `propagateScopeDependenciesHIR(fn)`.34pub fn propagate_scope_dependencies_hir(func: &mut HirFunction, env: &mut Environment) {35    let used_outside_declaring_scope = find_temporaries_used_outside_declaring_scope(func, env);36    let temporaries = collect_temporaries_sidemap(func, env, &used_outside_declaring_scope);3738    let OptionalChainSidemap {39        temporaries_read_in_optional,40        processed_instrs_in_optional,41        hoistable_objects,42    } = collect_optional_chain_sidemap(func, env);4344    let hoistable_property_loads = {45        let (working, registry) =46            collect_hoistable_and_propagate(func, env, &temporaries, &hoistable_objects);47        // Convert to scope-keyed map with full dependency paths48        let mut keyed: FxHashMap<ScopeId, Vec<ReactiveScopeDependency>> = FxHashMap::default();49        for (_block_id, block) in &func.body.blocks {50            if let Terminal::Scope {51                scope,52                block: inner_block,53                ..54            } = &block.terminal55            {56                if let Some(node_indices) = working.get(inner_block) {57                    let deps: Vec<ReactiveScopeDependency> = node_indices58                        .iter()59                        .map(|&idx| registry.nodes[idx].full_path.clone())60                        .collect();61                    keyed.insert(*scope, deps);62                }63            }64        }65        keyed66    };6768    // Merge temporaries + temporariesReadInOptional69    let mut merged_temporaries = temporaries;70    for (k, v) in temporaries_read_in_optional {71        merged_temporaries.insert(k, v);72    }7374    let scope_deps = collect_dependencies(75        func,76        env,77        &used_outside_declaring_scope,78        &merged_temporaries,79        &processed_instrs_in_optional,80    );8182    // Derive the minimal set of hoistable dependencies for each scope.83    for (scope_id, deps) in &scope_deps {84        if deps.is_empty() {85            continue;86        }8788        let hoistables = hoistable_property_loads.get(scope_id);89        let hoistables =90            hoistables.expect("[PropagateScopeDependencies] Scope not found in tracked blocks");9192        // Step 2: Calculate hoistable dependencies using the tree.93        let mut tree = ReactiveScopeDependencyTreeHIR::new(hoistables.iter(), env);94        for dep in deps {95            tree.add_dependency(dep.clone(), env);96        }9798        // Step 3: Reduce dependencies to a minimal set.99        let candidates = tree.derive_minimal_dependencies(env);100        let scope = &mut env.scopes[scope_id.0 as usize];101        for candidate_dep in candidates {102            let already_exists = scope.dependencies.iter().any(|existing_dep| {103                let existing_decl_id =104                    env.identifiers[existing_dep.identifier.0 as usize].declaration_id;105                let candidate_decl_id =106                    env.identifiers[candidate_dep.identifier.0 as usize].declaration_id;107                existing_decl_id == candidate_decl_id108                    && are_equal_paths(&existing_dep.path, &candidate_dep.path)109            });110            if !already_exists {111                scope.dependencies.push(candidate_dep);112            }113        }114    }115}116117fn are_equal_paths(a: &[DependencyPathEntry], b: &[DependencyPathEntry]) -> bool {118    a.len() == b.len()119        && a.iter()120            .zip(b.iter())121            .all(|(ai, bi)| ai.property == bi.property && ai.optional == bi.optional)122}123124// =============================================================================125// findTemporariesUsedOutsideDeclaringScope126// =============================================================================127128/// Corresponds to TS `findTemporariesUsedOutsideDeclaringScope`.129fn find_temporaries_used_outside_declaring_scope(130    func: &HirFunction,131    env: &Environment,132) -> FxHashSet<DeclarationId> {133    let mut declarations: FxHashMap<DeclarationId, ScopeId> = FxHashMap::default();134    let mut pruned_scopes: FxHashSet<ScopeId> = FxHashSet::default();135    let mut traversal = ScopeBlockTraversal::new();136    let mut used_outside_declaring_scope: FxHashSet<DeclarationId> = FxHashSet::default();137138    let handle_place = |place_id: IdentifierId,139                        declarations: &FxHashMap<DeclarationId, ScopeId>,140                        traversal: &ScopeBlockTraversal,141                        pruned_scopes: &FxHashSet<ScopeId>,142                        used_outside: &mut FxHashSet<DeclarationId>,143                        env: &Environment| {144        let decl_id = env.identifiers[place_id.0 as usize].declaration_id;145        if let Some(&declaring_scope) = declarations.get(&decl_id) {146            if !traversal.is_scope_active(declaring_scope)147                && !pruned_scopes.contains(&declaring_scope)148            {149                used_outside.insert(decl_id);150            }151        }152    };153154    for (block_id, block) in &func.body.blocks {155        // recordScopes156        traversal.record_scopes(block);157158        let scope_start_info = traversal.block_infos.get(block_id);159        if let Some(ScopeBlockInfo::Begin {160            scope,161            pruned: true,162            ..163        }) = scope_start_info164        {165            pruned_scopes.insert(*scope);166        }167168        for &instr_id in &block.instructions {169            let instr = &func.instructions[instr_id.0 as usize];170            // Handle operands171            for op_id in visitors::each_instruction_operand(instr, env)172                .into_iter()173                .map(|p| p.identifier)174                .collect::<Vec<_>>()175            {176                handle_place(177                    op_id,178                    &declarations,179                    &traversal,180                    &pruned_scopes,181                    &mut used_outside_declaring_scope,182                    env,183                );184            }185            // Handle instruction (track declarations)186            let current_scope = traversal.current_scope();187            if let Some(scope) = current_scope {188                if !pruned_scopes.contains(&scope) {189                    match &instr.value {190                        InstructionValue::LoadLocal { .. }191                        | InstructionValue::LoadContext { .. }192                        | InstructionValue::PropertyLoad { .. } => {193                            let decl_id =194                                env.identifiers[instr.lvalue.identifier.0 as usize].declaration_id;195                            declarations.insert(decl_id, scope);196                        }197                        _ => {}198                    }199                }200            }201        }202203        // Terminal operands204        for op_id in visitors::each_terminal_operand(&block.terminal)205            .into_iter()206            .map(|p| p.identifier)207            .collect::<Vec<_>>()208        {209            handle_place(210                op_id,211                &declarations,212                &traversal,213                &pruned_scopes,214                &mut used_outside_declaring_scope,215                env,216            );217        }218    }219220    used_outside_declaring_scope221}222223// =============================================================================224// collectTemporariesSidemap225// =============================================================================226227/// Corresponds to TS `collectTemporariesSidemap`.228fn collect_temporaries_sidemap(229    func: &HirFunction,230    env: &Environment,231    used_outside_declaring_scope: &FxHashSet<DeclarationId>,232) -> FxHashMap<IdentifierId, ReactiveScopeDependency> {233    let mut temporaries = FxHashMap::default();234    collect_temporaries_sidemap_impl(235        func,236        env,237        used_outside_declaring_scope,238        &mut temporaries,239        None,240    );241    temporaries242}243244/// Corresponds to TS `isLoadContextMutable`.245fn is_load_context_mutable(246    value: &InstructionValue,247    id: EvaluationOrder,248    env: &Environment,249) -> bool {250    if let InstructionValue::LoadContext { place, .. } = value {251        if let Some(scope_id) = env.identifiers[place.identifier.0 as usize].scope {252            let scope_range = &env.scopes[scope_id.0 as usize].range;253            return id >= scope_range.end;254        }255    }256    false257}258259/// Corresponds to TS `convertHoistedLValueKind` — returns None for non-hoisted kinds.260fn convert_hoisted_lvalue_kind(kind: InstructionKind) -> Option<InstructionKind> {261    match kind {262        InstructionKind::HoistedLet => Some(InstructionKind::Let),263        InstructionKind::HoistedConst => Some(InstructionKind::Const),264        InstructionKind::HoistedFunction => Some(InstructionKind::Function),265        _ => None,266    }267}268269/// Recursive implementation. Corresponds to TS `collectTemporariesSidemapImpl`.270fn collect_temporaries_sidemap_impl(271    func: &HirFunction,272    env: &Environment,273    used_outside_declaring_scope: &FxHashSet<DeclarationId>,274    temporaries: &mut FxHashMap<IdentifierId, ReactiveScopeDependency>,275    inner_fn_context: Option<EvaluationOrder>,276) {277    for (_block_id, block) in &func.body.blocks {278        for &instr_id in &block.instructions {279            let instr = &func.instructions[instr_id.0 as usize];280            let instr_eval_order = if let Some(outer_id) = inner_fn_context {281                outer_id282            } else {283                instr.id284            };285            let lvalue_decl_id = env.identifiers[instr.lvalue.identifier.0 as usize].declaration_id;286            let used_outside = used_outside_declaring_scope.contains(&lvalue_decl_id);287288            match &instr.value {289                InstructionValue::PropertyLoad {290                    object,291                    property,292                    loc,293                    ..294                } if !used_outside => {295                    if inner_fn_context.is_none() || temporaries.contains_key(&object.identifier) {296                        let prop = get_property(object, property, false, *loc, temporaries, env);297                        temporaries.insert(instr.lvalue.identifier, prop);298                    }299                }300                InstructionValue::LoadLocal { place, loc, .. }301                    if env.identifiers[instr.lvalue.identifier.0 as usize]302                        .name303                        .is_none()304                        && env.identifiers[place.identifier.0 as usize].name.is_some()305                        && !used_outside =>306                {307                    if inner_fn_context.is_none()308                        || func309                            .context310                            .iter()311                            .any(|ctx| ctx.identifier == place.identifier)312                    {313                        temporaries.insert(314                            instr.lvalue.identifier,315                            ReactiveScopeDependency {316                                identifier: place.identifier,317                                reactive: place.reactive,318                                path: vec![],319                                loc: *loc,320                            },321                        );322                    }323                }324                value @ InstructionValue::LoadContext { place, loc, .. }325                    if is_load_context_mutable(value, instr_eval_order, env)326                        && env.identifiers[instr.lvalue.identifier.0 as usize]327                            .name328                            .is_none()329                        && env.identifiers[place.identifier.0 as usize].name.is_some()330                        && !used_outside =>331                {332                    if inner_fn_context.is_none()333                        || func334                            .context335                            .iter()336                            .any(|ctx| ctx.identifier == place.identifier)337                    {338                        temporaries.insert(339                            instr.lvalue.identifier,340                            ReactiveScopeDependency {341                                identifier: place.identifier,342                                reactive: place.reactive,343                                path: vec![],344                                loc: *loc,345                            },346                        );347                    }348                }349                InstructionValue::FunctionExpression { lowered_func, .. }350                | InstructionValue::ObjectMethod { lowered_func, .. } => {351                    let inner_func = &env.functions[lowered_func.func.0 as usize];352                    let ctx = inner_fn_context.unwrap_or(instr.id);353                    collect_temporaries_sidemap_impl(354                        inner_func,355                        env,356                        used_outside_declaring_scope,357                        temporaries,358                        Some(ctx),359                    );360                }361                _ => {}362            }363        }364    }365}366367/// Corresponds to TS `getProperty`.368fn get_property(369    object: &Place,370    property_name: &PropertyLiteral,371    optional: bool,372    loc: Option<react_compiler_hir::SourceLocation>,373    temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,374    _env: &Environment,375) -> ReactiveScopeDependency {376    let resolved = temporaries.get(&object.identifier);377    if let Some(resolved) = resolved {378        let mut path = resolved.path.clone();379        path.push(DependencyPathEntry {380            property: property_name.clone(),381            optional,382            loc,383        });384        ReactiveScopeDependency {385            identifier: resolved.identifier,386            reactive: resolved.reactive,387            path,388            loc,389        }390    } else {391        ReactiveScopeDependency {392            identifier: object.identifier,393            reactive: object.reactive,394            path: vec![DependencyPathEntry {395                property: property_name.clone(),396                optional,397                loc,398            }],399            loc,400        }401    }402}403404// =============================================================================405// CollectOptionalChainDependencies406// =============================================================================407408struct OptionalChainSidemap {409    temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,410    processed_instrs_in_optional: FxHashSet<ProcessedInstr>,411    hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,412}413414/// We track processed instructions/terminals by their lvalue IdentifierId + block id.415/// In TS this uses reference identity (Set<Instruction | Terminal>).416/// We use IdentifierId for instructions (globally unique across functions) and417/// BlockId for terminals. Note: EvaluationOrder (instruction id) is NOT unique418/// across functions, so we cannot use it here.419#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]420enum ProcessedInstr {421    Instruction(IdentifierId),422    Terminal(BlockId),423}424425fn collect_optional_chain_sidemap(func: &HirFunction, env: &Environment) -> OptionalChainSidemap {426    let mut ctx = OptionalTraversalContext {427        seen_optionals: FxHashSet::default(),428        processed_instrs_in_optional: FxHashSet::default(),429        temporaries_read_in_optional: FxHashMap::default(),430        hoistable_objects: FxHashMap::default(),431    };432433    traverse_function_optional(func, env, &mut ctx);434435    OptionalChainSidemap {436        temporaries_read_in_optional: ctx.temporaries_read_in_optional,437        processed_instrs_in_optional: ctx.processed_instrs_in_optional,438        hoistable_objects: ctx.hoistable_objects,439    }440}441442struct OptionalTraversalContext {443    seen_optionals: FxHashSet<BlockId>,444    processed_instrs_in_optional: FxHashSet<ProcessedInstr>,445    temporaries_read_in_optional: FxHashMap<IdentifierId, ReactiveScopeDependency>,446    hoistable_objects: FxHashMap<BlockId, ReactiveScopeDependency>,447}448449fn traverse_function_optional(450    func: &HirFunction,451    env: &Environment,452    ctx: &mut OptionalTraversalContext,453) {454    for (_block_id, block) in &func.body.blocks {455        for &instr_id in &block.instructions {456            let instr = &func.instructions[instr_id.0 as usize];457            match &instr.value {458                InstructionValue::FunctionExpression { lowered_func, .. }459                | InstructionValue::ObjectMethod { lowered_func, .. } => {460                    let inner_func = &env.functions[lowered_func.func.0 as usize];461                    traverse_function_optional(inner_func, env, ctx);462                }463                _ => {}464            }465        }466        if let Terminal::Optional { .. } = &block.terminal {467            if !ctx.seen_optionals.contains(&block.id) {468                traverse_optional_block(block, func, env, ctx, None);469            }470        }471    }472}473474struct MatchConsequentResult {475    consequent_id: IdentifierId,476    property: PropertyLiteral,477    property_id: IdentifierId,478    store_local_lvalue_id: IdentifierId,479    consequent_goto: BlockId,480    property_load_loc: Option<react_compiler_hir::SourceLocation>,481}482483fn match_optional_test_block(484    test: &Terminal,485    func: &HirFunction,486    _env: &Environment,487) -> Option<MatchConsequentResult> {488    let (test_place, consequent_block_id, alternate_block_id) = match test {489        Terminal::Branch {490            test,491            consequent,492            alternate,493            ..494        } => (test, *consequent, *alternate),495        _ => return None,496    };497498    let consequent_block = func.body.blocks.get(&consequent_block_id)?;499    if consequent_block.instructions.len() != 2 {500        return None;501    }502503    let instr0 = &func.instructions[consequent_block.instructions[0].0 as usize];504    let instr1 = &func.instructions[consequent_block.instructions[1].0 as usize];505506    let (property_load_object, property, property_load_loc) = match &instr0.value {507        InstructionValue::PropertyLoad {508            object,509            property,510            loc,511        } => (object, property, loc),512        _ => return None,513    };514515    let store_local_value = match &instr1.value {516        InstructionValue::StoreLocal { value, lvalue, .. } => {517            // Verify the store local's value matches the property load's lvalue518            if value.identifier != instr0.lvalue.identifier {519                return None;520            }521            &lvalue.place522        }523        _ => return None,524    };525526    // Verify property load's object matches the test527    if property_load_object.identifier != test_place.identifier {528        return None;529    }530531    // Check consequent block terminal is goto break532    match &consequent_block.terminal {533        Terminal::Goto {534            variant: GotoVariant::Break,535            block: goto_block,536            ..537        } => {538            // Verify alternate block structure539            let alternate_block = func.body.blocks.get(&alternate_block_id)?;540            if alternate_block.instructions.len() != 2 {541                return None;542            }543            let alt_instr0 = &func.instructions[alternate_block.instructions[0].0 as usize];544            let alt_instr1 = &func.instructions[alternate_block.instructions[1].0 as usize];545            match (&alt_instr0.value, &alt_instr1.value) {546                (InstructionValue::Primitive { .. }, InstructionValue::StoreLocal { .. }) => {}547                _ => return None,548            }549550            Some(MatchConsequentResult {551                consequent_id: store_local_value.identifier,552                property: property.clone(),553                property_id: instr0.lvalue.identifier,554                store_local_lvalue_id: instr1.lvalue.identifier,555                consequent_goto: *goto_block,556                property_load_loc: *property_load_loc,557            })558        }559        _ => None,560    }561}562563fn traverse_optional_block(564    optional_block: &BasicBlock,565    func: &HirFunction,566    env: &Environment,567    ctx: &mut OptionalTraversalContext,568    outer_alternate: Option<BlockId>,569) -> Option<IdentifierId> {570    ctx.seen_optionals.insert(optional_block.id);571572    let (test_block_id, is_optional, fallthrough_block_id) = match &optional_block.terminal {573        Terminal::Optional {574            test,575            optional,576            fallthrough,577            ..578        } => (*test, *optional, *fallthrough),579        _ => return None,580    };581582    let maybe_test_block = func.body.blocks.get(&test_block_id)?;583584    let (test_terminal, base_object) = match &maybe_test_block.terminal {585        Terminal::Branch { .. } => {586            // Base case: optional must be true587            if !is_optional {588                return None;589            }590            // Match base expression that is straightforward PropertyLoad chain591            if maybe_test_block.instructions.is_empty() {592                return None;593            }594            let first_instr = &func.instructions[maybe_test_block.instructions[0].0 as usize];595            if !matches!(&first_instr.value, InstructionValue::LoadLocal { .. }) {596                return None;597            }598599            let mut path: Vec<DependencyPathEntry> = Vec::new();600            for i in 1..maybe_test_block.instructions.len() {601                let curr_instr = &func.instructions[maybe_test_block.instructions[i].0 as usize];602                let prev_instr =603                    &func.instructions[maybe_test_block.instructions[i - 1].0 as usize];604                match &curr_instr.value {605                    InstructionValue::PropertyLoad {606                        object,607                        property,608                        loc,609                        ..610                    } if object.identifier == prev_instr.lvalue.identifier => {611                        path.push(DependencyPathEntry {612                            property: property.clone(),613                            optional: false,614                            loc: *loc,615                        });616                    }617                    _ => return None,618                }619            }620621            // Verify test expression matches last instruction's lvalue622            let last_instr_id = *maybe_test_block.instructions.last().unwrap();623            let last_instr = &func.instructions[last_instr_id.0 as usize];624            let test_ident = match &maybe_test_block.terminal {625                Terminal::Branch { test, .. } => test.identifier,626                _ => return None,627            };628            if test_ident != last_instr.lvalue.identifier {629                return None;630            }631632            let first_place = match &first_instr.value {633                InstructionValue::LoadLocal { place, .. } => place,634                _ => return None,635            };636637            let base = ReactiveScopeDependency {638                identifier: first_place.identifier,639                reactive: first_place.reactive,640                path,641                loc: first_place.loc,642            };643            (&maybe_test_block.terminal, base)644        }645        Terminal::Optional {646            fallthrough: inner_fallthrough,647            optional: _inner_optional,648            ..649        } => {650            let test_block = func.body.blocks.get(inner_fallthrough)?;651            if !matches!(&test_block.terminal, Terminal::Branch { .. }) {652                return None;653            }654655            // Recurse into inner optional656            let inner_alternate = match &test_block.terminal {657                Terminal::Branch { alternate, .. } => Some(*alternate),658                _ => None,659            };660            let inner_optional_result =661                traverse_optional_block(maybe_test_block, func, env, ctx, inner_alternate);662            let inner_optional_id = inner_optional_result?;663664            // Check that inner optional is part of the same chain665            let test_ident = match &test_block.terminal {666                Terminal::Branch { test, .. } => test.identifier,667                _ => return None,668            };669            if test_ident != inner_optional_id {670                return None;671            }672673            if !is_optional {674                // Non-optional load: record that PropertyLoads from inner optional are hoistable675                if let Some(inner_dep) = ctx.temporaries_read_in_optional.get(&inner_optional_id) {676                    ctx.hoistable_objects677                        .insert(optional_block.id, inner_dep.clone());678                }679            }680681            let base = ctx682                .temporaries_read_in_optional683                .get(&inner_optional_id)?684                .clone();685            (&test_block.terminal, base)686        }687        _ => return None,688    };689690    // Verify alternate matches outer_alternate if present691    if let Some(outer_alt) = outer_alternate {692        let test_alternate = match test_terminal {693            Terminal::Branch { alternate, .. } => *alternate,694            _ => return None,695        };696        if test_alternate == outer_alt {697            // Verify optional block has no instructions698            if !optional_block.instructions.is_empty() {699                return None;700            }701        }702    }703704    let match_result = match_optional_test_block(test_terminal, func, env)?;705706    // Verify consequent goto matches optional fallthrough707    if match_result.consequent_goto != fallthrough_block_id {708        return None;709    }710711    let load = ReactiveScopeDependency {712        identifier: base_object.identifier,713        reactive: base_object.reactive,714        path: {715            let mut p = base_object.path.clone();716            p.push(DependencyPathEntry {717                property: match_result.property.clone(),718                optional: is_optional,719                loc: match_result.property_load_loc,720            });721            p722        },723        loc: match_result.property_load_loc,724    };725726    ctx.processed_instrs_in_optional727        .insert(ProcessedInstr::Instruction(728            match_result.store_local_lvalue_id,729        ));730    ctx.processed_instrs_in_optional731        .insert(ProcessedInstr::Terminal(match &test_terminal {732            Terminal::Branch { .. } => {733                // Find the block ID for this terminal734                // The terminal belongs to either maybe_test_block or the fallthrough block of inner optional735                // We need to identify which block this terminal belongs to.736                // For the base case, it's test_block_id.737                // For nested optional, it's the fallthrough block.738                // We'll use the block_id approach based on what we know.739                // Actually, we tracked the terminal by its block, so we need to find which block740                // contains this terminal. Let's use a pragmatic approach:741                // The test terminal we matched was from maybe_test_block or from the inner fallthrough block.742                // We'll search for it.743744                // For the base case (Branch terminal at maybe_test_block), block_id = test_block_id745                // For the nested case, the test terminal is at the fallthrough block of inner optional746                // In either case, we stored the terminal as test_terminal which comes from a known block.747                // We need to find the block that owns this terminal.748749                // Let's take a simpler approach: find the block whose terminal matches750                // This is the block we got test_terminal from.751                // In the first branch of the match, test_terminal = &maybe_test_block.terminal752                //   and maybe_test_block.id = test_block_id753                // In the second branch, test_terminal = &test_block.terminal754                //   and test_block = func.body.blocks.get(inner_fallthrough)755                // We can't easily tell which case we're in here since we're past the match.756757                // Actually, since test_terminal is a reference to a terminal in a block,758                // we can just look up which block it belongs to by finding blocks whose terminal759                // pointer matches. But that's expensive. Instead, let's use the block approach760                // and find the block from the terminal's properties.761762                // For simplicity, use a sentinel approach: just check all blocks.763                // This is O(n) but only happens for optional chains.764                let mut found_block = BlockId(0);765                for (bid, blk) in &func.body.blocks {766                    if std::ptr::eq(&blk.terminal, test_terminal) {767                        found_block = *bid;768                        break;769                    }770                }771                found_block772            }773            _ => BlockId(0),774        }));775    ctx.temporaries_read_in_optional776        .insert(match_result.consequent_id, load.clone());777    ctx.temporaries_read_in_optional778        .insert(match_result.property_id, load);779780    Some(match_result.consequent_id)781}782783// =============================================================================784// CollectHoistablePropertyLoads785// =============================================================================786787#[derive(Debug, Clone)]788struct PropertyPathNode {789    properties: FxHashMap<PropertyLiteral, usize>, // index into registry790    optional_properties: FxHashMap<PropertyLiteral, usize>, // index into registry791    #[allow(dead_code)]792    parent: Option<usize>,793    full_path: ReactiveScopeDependency,794    has_optional: bool,795    #[allow(dead_code)]796    root: Option<IdentifierId>,797}798799struct PropertyPathRegistry {800    nodes: Vec<PropertyPathNode>,801    roots: FxHashMap<IdentifierId, usize>,802}803804impl PropertyPathRegistry {805    fn new() -> Self {806        Self {807            nodes: Vec::new(),808            roots: FxHashMap::default(),809        }810    }811812    fn get_or_create_identifier(813        &mut self,814        identifier_id: IdentifierId,815        reactive: bool,816        loc: Option<react_compiler_hir::SourceLocation>,817    ) -> usize {818        if let Some(&idx) = self.roots.get(&identifier_id) {819            return idx;820        }821        let idx = self.nodes.len();822        self.nodes.push(PropertyPathNode {823            properties: FxHashMap::default(),824            optional_properties: FxHashMap::default(),825            parent: None,826            full_path: ReactiveScopeDependency {827                identifier: identifier_id,828                reactive,829                path: vec![],830                loc,831            },832            has_optional: false,833            root: Some(identifier_id),834        });835        self.roots.insert(identifier_id, idx);836        idx837    }838839    fn get_or_create_property_entry(840        &mut self,841        parent_idx: usize,842        entry: &DependencyPathEntry,843    ) -> usize {844        let map_key = entry.property.clone();845        let existing = if entry.optional {846            self.nodes[parent_idx]847                .optional_properties848                .get(&map_key)849                .copied()850        } else {851            self.nodes[parent_idx].properties.get(&map_key).copied()852        };853        if let Some(idx) = existing {854            return idx;855        }856        let parent_full_path = self.nodes[parent_idx].full_path.clone();857        let parent_has_optional = self.nodes[parent_idx].has_optional;858        let idx = self.nodes.len();859        let mut new_path = parent_full_path.path.clone();860        new_path.push(entry.clone());861        self.nodes.push(PropertyPathNode {862            properties: FxHashMap::default(),863            optional_properties: FxHashMap::default(),864            parent: Some(parent_idx),865            full_path: ReactiveScopeDependency {866                identifier: parent_full_path.identifier,867                reactive: parent_full_path.reactive,868                path: new_path,869                loc: entry.loc,870            },871            has_optional: parent_has_optional || entry.optional,872            root: None,873        });874        if entry.optional {875            self.nodes[parent_idx]876                .optional_properties877                .insert(map_key, idx);878        } else {879            self.nodes[parent_idx].properties.insert(map_key, idx);880        }881        idx882    }883884    fn get_or_create_property(&mut self, dep: &ReactiveScopeDependency) -> usize {885        let mut curr = self.get_or_create_identifier(dep.identifier, dep.reactive, dep.loc);886        for entry in &dep.path {887            curr = self.get_or_create_property_entry(curr, entry);888        }889        curr890    }891}892893/// Reduces optional chains in a set of property path nodes.894///895/// Any two optional chains with different operations (`.` vs `?.`) but the same set896/// of property string paths de-duplicates. If unconditional reads from `<base>` are897/// hoistable (i.e., `<base>` is in the set), we replace `<base>?.PROPERTY` with898/// `<base>.PROPERTY`.899///900/// Port of `reduceMaybeOptionalChains` from CollectHoistablePropertyLoads.ts.901fn reduce_maybe_optional_chains(nodes: &mut BTreeSet<usize>, registry: &mut PropertyPathRegistry) {902    // Collect indices of nodes that have optional in their path903    let mut optional_chain_nodes: BTreeSet<usize> = nodes904        .iter()905        .copied()906        .filter(|&idx| registry.nodes[idx].has_optional)907        .collect();908909    if optional_chain_nodes.is_empty() {910        return;911    }912913    loop {914        let mut changed = false;915916        // Collect the indices to process (snapshot to avoid borrow issues)917        let to_process: Vec<usize> = optional_chain_nodes.iter().copied().collect();918919        for original_idx in to_process {920            let full_path = registry.nodes[original_idx].full_path.clone();921922            let mut curr_node = registry.get_or_create_identifier(923                full_path.identifier,924                full_path.reactive,925                full_path.loc,926            );927928            for entry in &full_path.path {929                // If the base is known to be non-null (in the set), replace optional with non-optional930                let next_entry = if entry.optional && nodes.contains(&curr_node) {931                    DependencyPathEntry {932                        property: entry.property.clone(),933                        optional: false,934                        loc: entry.loc,935                    }936                } else {937                    entry.clone()938                };939                curr_node = registry.get_or_create_property_entry(curr_node, &next_entry);940            }941942            if curr_node != original_idx {943                changed = true;944                optional_chain_nodes.remove(&original_idx);945                optional_chain_nodes.insert(curr_node);946                nodes.remove(&original_idx);947                nodes.insert(curr_node);948            }949        }950951        if !changed {952            break;953        }954    }955}956957#[derive(Debug, Clone)]958struct BlockInfo {959    assumed_non_null_objects: BTreeSet<usize>, // indices into PropertyPathRegistry960}961962#[allow(dead_code)]963fn collect_hoistable_property_loads(964    func: &HirFunction,965    env: &Environment,966    temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,967    hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,968) -> FxHashMap<BlockId, BlockInfo> {969    let mut registry = PropertyPathRegistry::new();970    let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type971        == ReactFunctionType::Component972        || func.fn_type == ReactFunctionType::Hook973    {974        func.params975            .iter()976            .filter_map(|p| match p {977                ParamPattern::Place(place) => Some(place.identifier),978                _ => None,979            })980            .collect()981    } else {982        FxHashSet::default()983    };984985    let assumed_invoked_fns = get_assumed_invoked_functions(func, env);986    let ctx = CollectHoistableContext {987        temporaries,988        known_immutable_identifiers: &known_immutable_identifiers,989        hoistable_from_optionals,990        nested_fn_immutable_context: None,991        assumed_invoked_fns: &assumed_invoked_fns,992    };993994    collect_hoistable_property_loads_impl(func, env, &ctx, &mut registry)995}996997struct CollectHoistableContext<'a> {998    temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,999    known_immutable_identifiers: &'a FxHashSet<IdentifierId>,1000    hoistable_from_optionals: &'a FxHashMap<BlockId, ReactiveScopeDependency>,1001    nested_fn_immutable_context: Option<&'a FxHashSet<IdentifierId>>,1002    assumed_invoked_fns: &'a FxHashSet<FunctionId>,1003}10041005fn is_immutable_at_instr(1006    identifier_id: IdentifierId,1007    instr_id: EvaluationOrder,1008    env: &Environment,1009    ctx: &CollectHoistableContext,1010) -> bool {1011    if let Some(nested_ctx) = ctx.nested_fn_immutable_context {1012        return nested_ctx.contains(&identifier_id);1013    }1014    let ident = &env.identifiers[identifier_id.0 as usize];1015    let mutable_at_instr = ident.mutable_range.end1016        > EvaluationOrder(ident.mutable_range.start.0 + 1)1017        && ident.scope.is_some()1018        && {1019            let scope = &env.scopes[ident.scope.unwrap().0 as usize];1020            in_range(instr_id, &scope.range)1021        };1022    !mutable_at_instr || ctx.known_immutable_identifiers.contains(&identifier_id)1023}10241025fn in_range(id: EvaluationOrder, range: &MutableRange) -> bool {1026    id >= range.start && id < range.end1027}10281029fn get_maybe_non_null_in_instruction(1030    value: &InstructionValue,1031    temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,1032) -> Option<ReactiveScopeDependency> {1033    match value {1034        InstructionValue::PropertyLoad { object, .. } => Some(1035            temporaries1036                .get(&object.identifier)1037                .cloned()1038                .unwrap_or_else(|| ReactiveScopeDependency {1039                    identifier: object.identifier,1040                    reactive: object.reactive,1041                    path: vec![],1042                    loc: object.loc,1043                }),1044        ),1045        InstructionValue::Destructure { value: val, .. } => {1046            temporaries.get(&val.identifier).cloned()1047        }1048        InstructionValue::ComputedLoad { object, .. } => {1049            temporaries.get(&object.identifier).cloned()1050        }1051        _ => None,1052    }1053}10541055#[allow(dead_code)]1056fn collect_hoistable_property_loads_impl(1057    func: &HirFunction,1058    env: &Environment,1059    ctx: &CollectHoistableContext,1060    registry: &mut PropertyPathRegistry,1061) -> FxHashMap<BlockId, BlockInfo> {1062    let nodes = collect_non_nulls_in_blocks(func, env, ctx, registry);1063    let working = propagate_non_null(func, &nodes, registry);1064    // Return the propagated results, converting FxHashSet<usize> back to BlockInfo1065    working1066        .into_iter()1067        .map(|(k, v)| {1068            (1069                k,1070                BlockInfo {1071                    assumed_non_null_objects: v,1072                },1073            )1074        })1075        .collect()1076}10771078/// Corresponds to TS `getAssumedInvokedFunctions`.1079/// Returns the set of LoweredFunction FunctionIds that are assumed to be invoked.1080/// The `temporaries` map is shared across recursive calls (matching TS behavior where1081/// the same Map is passed to recursive invocations for inner functions).1082fn get_assumed_invoked_functions(func: &HirFunction, env: &Environment) -> FxHashSet<FunctionId> {1083    let mut temporaries: FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)> =1084        FxHashMap::default();1085    get_assumed_invoked_functions_impl(func, env, &mut temporaries)1086}10871088fn get_assumed_invoked_functions_impl(1089    func: &HirFunction,1090    env: &Environment,1091    temporaries: &mut FxHashMap<IdentifierId, (FunctionId, FxHashSet<FunctionId>)>,1092) -> FxHashSet<FunctionId> {1093    let mut hoistable: FxHashSet<FunctionId> = FxHashSet::default();10941095    // Step 1: Collect identifier to function expression mappings1096    for (_block_id, block) in &func.body.blocks {1097        for &instr_id in &block.instructions {1098            let instr = &func.instructions[instr_id.0 as usize];1099            match &instr.value {1100                InstructionValue::FunctionExpression { lowered_func, .. } => {1101                    temporaries.insert(1102                        instr.lvalue.identifier,1103                        (lowered_func.func, FxHashSet::default()),1104                    );1105                }1106                InstructionValue::StoreLocal {1107                    value: val, lvalue, ..1108                } => {1109                    if let Some(entry) = temporaries.get(&val.identifier).cloned() {1110                        temporaries.insert(lvalue.place.identifier, entry);1111                    }1112                }1113                InstructionValue::LoadLocal { place, .. } => {1114                    if let Some(entry) = temporaries.get(&place.identifier).cloned() {1115                        temporaries.insert(instr.lvalue.identifier, entry);1116                    }1117                }1118                _ => {}1119            }1120        }1121    }11221123    // Step 2: Forward pass to analyze assumed function calls1124    for (_block_id, block) in &func.body.blocks {1125        for &instr_id in &block.instructions {1126            let instr = &func.instructions[instr_id.0 as usize];1127            match &instr.value {1128                InstructionValue::CallExpression { callee, args, .. } => {1129                    let callee_ty =1130                        &env.types[env.identifiers[callee.identifier.0 as usize].type_.0 as usize];1131                    let maybe_hook = env.get_hook_kind_for_type(callee_ty).ok().flatten();1132                    if let Some(entry) = temporaries.get(&callee.identifier) {1133                        // Direct calls1134                        hoistable.insert(entry.0);1135                    } else if maybe_hook.is_some() {1136                        // Assume arguments to all hooks are safe to invoke1137                        for arg in args {1138                            if let PlaceOrSpread::Place(p) = arg {1139                                if let Some(entry) = temporaries.get(&p.identifier) {1140                                    hoistable.insert(entry.0);1141                                }1142                            }1143                        }1144                    }1145                }1146                InstructionValue::JsxExpression {1147                    props, children, ..1148                } => {1149                    // Assume JSX attributes and children are safe to invoke1150                    for prop in props {1151                        if let react_compiler_hir::JsxAttribute::Attribute { place, .. } = prop {1152                            if let Some(entry) = temporaries.get(&place.identifier) {1153                                hoistable.insert(entry.0);1154                            }1155                        }1156                    }1157                    if let Some(children) = children {1158                        for child in children {1159                            if let Some(entry) = temporaries.get(&child.identifier) {1160                                hoistable.insert(entry.0);1161                            }1162                        }1163                    }1164                }1165                InstructionValue::JsxFragment { children, .. } => {1166                    for child in children {1167                        if let Some(entry) = temporaries.get(&child.identifier) {1168                            hoistable.insert(entry.0);1169                        }1170                    }1171                }1172                InstructionValue::FunctionExpression { lowered_func, .. } => {1173                    // Recursively traverse into other function expressions1174                    // TS passes the shared temporaries map to the recursive call1175                    let inner_func = &env.functions[lowered_func.func.0 as usize];1176                    let lambdas_called =1177                        get_assumed_invoked_functions_impl(inner_func, env, temporaries);1178                    if let Some(entry) = temporaries.get_mut(&instr.lvalue.identifier) {1179                        for called in lambdas_called {1180                            entry.1.insert(called);1181                        }1182                    }1183                }1184                _ => {}1185            }1186        }11871188        // Assume directly returned functions are safe to call1189        if let Terminal::Return { value, .. } = &block.terminal {1190            if let Some(entry) = temporaries.get(&value.identifier) {1191                hoistable.insert(entry.0);1192            }1193        }1194    }11951196    // Step 3: Propagate assumed-invoked status through mayInvoke chains1197    let mut changed = true;1198    while changed {1199        changed = false;1200        // Two-phase: collect then insert1201        let mut to_add = Vec::new();1202        for (_, (func_id, may_invoke)) in temporaries.iter() {1203            if hoistable.contains(func_id) {1204                for &called in may_invoke {1205                    if !hoistable.contains(&called) {1206                        to_add.push(called);1207                    }1208                }1209            }1210        }1211        for id in to_add {1212            changed = true;1213            hoistable.insert(id);1214        }1215        if !changed {1216            break;1217        }1218    }12191220    hoistable1221}12221223fn collect_non_nulls_in_blocks(1224    func: &HirFunction,1225    env: &Environment,1226    ctx: &CollectHoistableContext,1227    registry: &mut PropertyPathRegistry,1228) -> FxHashMap<BlockId, BlockInfo> {1229    // Known non-null identifiers (e.g. component props)1230    let mut known_non_null: BTreeSet<usize> = BTreeSet::new();1231    if func.fn_type == ReactFunctionType::Component && !func.params.is_empty() {1232        if let ParamPattern::Place(place) = &func.params[0] {1233            let node_idx = registry.get_or_create_identifier(place.identifier, true, place.loc);1234            known_non_null.insert(node_idx);1235        }1236    }12371238    let mut nodes: FxHashMap<BlockId, BlockInfo> = FxHashMap::default();12391240    for (block_id, block) in &func.body.blocks {1241        let mut assumed = known_non_null.clone();12421243        // Check hoistable from optionals1244        if let Some(optional_chain) = ctx.hoistable_from_optionals.get(block_id) {1245            let node_idx = registry.get_or_create_property(optional_chain);1246            assumed.insert(node_idx);1247        }12481249        for &instr_id in &block.instructions {1250            let instr = &func.instructions[instr_id.0 as usize];1251            if let Some(path) = get_maybe_non_null_in_instruction(&instr.value, ctx.temporaries) {1252                let path_ident = path.identifier;1253                if is_immutable_at_instr(path_ident, instr.id, env, ctx) {1254                    let node_idx = registry.get_or_create_property(&path);1255                    assumed.insert(node_idx);1256                }1257            }12581259            // Handle StartMemoize deps for enablePreserveExistingMemoizationGuarantees1260            if env.enable_preserve_existing_memoization_guarantees {1261                if let InstructionValue::StartMemoize {1262                    deps: Some(deps), ..1263                } = &instr.value1264                {1265                    for dep in deps {1266                        if let react_compiler_hir::ManualMemoDependencyRoot::NamedLocal {1267                            value: val,1268                            ..1269                        } = &dep.root1270                        {1271                            if !is_immutable_at_instr(val.identifier, instr.id, env, ctx) {1272                                continue;1273                            }1274                            for i in 0..dep.path.len() {1275                                if dep.path[i].optional {1276                                    break;1277                                }1278                                let sub_dep = ReactiveScopeDependency {1279                                    identifier: val.identifier,1280                                    reactive: val.reactive,1281                                    path: dep.path[..i].to_vec(),1282                                    loc: dep.loc,1283                                };1284                                let node_idx = registry.get_or_create_property(&sub_dep);1285                                assumed.insert(node_idx);1286                            }1287                        }1288                    }1289                }1290            }12911292            // Handle assumed-invoked inner functions1293            if let InstructionValue::FunctionExpression { lowered_func, .. } = &instr.value {1294                if ctx.assumed_invoked_fns.contains(&lowered_func.func) {1295                    let inner_func = &env.functions[lowered_func.func.0 as usize];1296                    // Build nested fn immutable context1297                    let nested_fn_immutable_context: FxHashSet<IdentifierId> =1298                        if ctx.nested_fn_immutable_context.is_some() {1299                            // Already in a nested fn context, use existing1300                            ctx.nested_fn_immutable_context.unwrap().clone()1301                        } else {1302                            inner_func1303                                .context1304                                .iter()1305                                .filter(|place| {1306                                    is_immutable_at_instr(place.identifier, instr.id, env, ctx)1307                                })1308                                .map(|place| place.identifier)1309                                .collect()1310                        };1311                    let inner_assumed = get_assumed_invoked_functions(inner_func, env);1312                    let inner_ctx = CollectHoistableContext {1313                        temporaries: ctx.temporaries,1314                        known_immutable_identifiers: &FxHashSet::default(),1315                        hoistable_from_optionals: ctx.hoistable_from_optionals,1316                        nested_fn_immutable_context: Some(&nested_fn_immutable_context),1317                        assumed_invoked_fns: &inner_assumed,1318                    };1319                    let inner_nodes =1320                        collect_non_nulls_in_blocks(inner_func, env, &inner_ctx, registry);1321                    // Propagate non-null from inner function1322                    let inner_working = propagate_non_null(inner_func, &inner_nodes, registry);1323                    // Get hoistables from inner function's entry block (after propagation)1324                    let inner_entry = inner_func.body.entry;1325                    if let Some(inner_set) = inner_working.get(&inner_entry) {1326                        for &node_idx in inner_set {1327                            assumed.insert(node_idx);1328                        }1329                    }1330                }1331            }1332        }13331334        nodes.insert(1335            *block_id,1336            BlockInfo {1337                assumed_non_null_objects: assumed,1338            },1339        );1340    }13411342    nodes1343}13441345/// Recursive DFS propagation of non-null information through the CFG.1346/// Uses 'active'/'done' state tracking to correctly handle cycles (backedges in loops).1347///1348/// Port of TS `propagateNonNull` which uses `recursivelyPropagateNonNull`.1349/// Key insight: when computing the intersection of neighbor sets, only include1350/// neighbors that are 'done' (not 'active'). Active neighbors are part of a cycle1351/// and should be filtered out, allowing non-null info to propagate through non-cyclic paths.1352fn propagate_non_null(1353    func: &HirFunction,1354    nodes: &FxHashMap<BlockId, BlockInfo>,1355    registry: &mut PropertyPathRegistry,1356) -> FxHashMap<BlockId, BTreeSet<usize>> {1357    // Build successor map. Use BTreeSet to iterate successors in sorted BlockId1358    // order, matching the TS Set<BlockId> insertion order (blocks are created in1359    // ascending BlockId order).1360    let mut block_successors: FxHashMap<BlockId, BTreeSet<BlockId>> = FxHashMap::default();1361    for (block_id, block) in &func.body.blocks {1362        for pred in &block.preds {1363            block_successors.entry(*pred).or_default().insert(*block_id);1364        }1365    }13661367    // Clone nodes into mutable working set1368    let mut working: FxHashMap<BlockId, BTreeSet<usize>> = nodes1369        .iter()1370        .map(|(k, v)| (*k, v.assumed_non_null_objects.clone()))1371        .collect();13721373    let block_ids: Vec<BlockId> = func.body.blocks.keys().copied().collect();1374    let mut reversed_block_ids = block_ids.clone();1375    reversed_block_ids.reverse();13761377    for _ in 0..100 {1378        let mut changed = false;13791380        // Forward pass (using predecessors)1381        let mut traversal_state: FxHashMap<BlockId, TraversalState> = FxHashMap::default();1382        for &block_id in &block_ids {1383            let block_changed = recursively_propagate_non_null(1384                block_id,1385                PropagationDirection::Forward,1386                &mut traversal_state,1387                &mut working,1388                func,1389                &block_successors,1390                registry,1391            );1392            changed |= block_changed;1393        }13941395        // Backward pass (using successors)1396        traversal_state.clear();1397        for &block_id in &reversed_block_ids {1398            let block_changed = recursively_propagate_non_null(1399                block_id,1400                PropagationDirection::Backward,1401                &mut traversal_state,1402                &mut working,1403                func,1404                &block_successors,1405                registry,1406            );1407            changed |= block_changed;1408        }14091410        if !changed {1411            break;1412        }1413    }14141415    working1416}14171418#[derive(Debug, Clone, Copy, PartialEq, Eq)]1419enum TraversalState {1420    Active,1421    Done,1422}14231424#[derive(Debug, Clone, Copy, PartialEq, Eq)]1425enum PropagationDirection {1426    Forward,1427    Backward,1428}14291430fn recursively_propagate_non_null(1431    node_id: BlockId,1432    direction: PropagationDirection,1433    traversal_state: &mut FxHashMap<BlockId, TraversalState>,1434    working: &mut FxHashMap<BlockId, BTreeSet<usize>>,1435    func: &HirFunction,1436    block_successors: &FxHashMap<BlockId, BTreeSet<BlockId>>,1437    registry: &mut PropertyPathRegistry,1438) -> bool {1439    // Avoid re-visiting computed or currently active nodes1440    if traversal_state.contains_key(&node_id) {1441        return false;1442    }1443    traversal_state.insert(node_id, TraversalState::Active);14441445    let neighbors: Vec<BlockId> = match direction {1446        PropagationDirection::Backward => block_successors1447            .get(&node_id)1448            .map(|s| s.iter().copied().collect())1449            .unwrap_or_default(),1450        PropagationDirection::Forward => func1451            .body1452            .blocks1453            .get(&node_id)1454            .map(|b| b.preds.iter().copied().collect())1455            .unwrap_or_default(),1456    };14571458    let mut changed = false;1459    for &neighbor in &neighbors {1460        if !traversal_state.contains_key(&neighbor) {1461            let neighbor_changed = recursively_propagate_non_null(1462                neighbor,1463                direction,1464                traversal_state,1465                working,1466                func,1467                block_successors,1468                registry,1469            );1470            changed |= neighbor_changed;1471        }1472    }14731474    // Compute intersection of 'done' neighbors only (filter out 'active' = cycle nodes)1475    let neighbor_intersection = {1476        let done_neighbor_sets: Vec<&BTreeSet<usize>> = neighbors1477            .iter()1478            .filter(|n| traversal_state.get(n) == Some(&TraversalState::Done))1479            .filter_map(|n| working.get(n))1480            .collect();14811482        match done_neighbor_sets.split_first() {1483            None => BTreeSet::new(),1484            Some((first, rest)) => rest.iter().fold((*first).clone(), |acc, s| {1485                acc.intersection(s).copied().collect()1486            }),1487        }1488    };14891490    // Temporarily remove the previous set out of the map so it can be safely1491    // borrowed and compared without a heavy deep clone.1492    let prev_objects = working.remove(&node_id).unwrap_or_default();1493    let mut merged: BTreeSet<usize> = prev_objects1494        .union(&neighbor_intersection)1495        .copied()1496        .collect();1497    reduce_maybe_optional_chains(&mut merged, registry);14981499    // Compare with previous value — can't just check size due to reduce_maybe_optional_chains1500    changed |= prev_objects != merged;15011502    working.insert(node_id, merged);1503    traversal_state.insert(node_id, TraversalState::Done);15041505    changed1506}15071508fn collect_hoistable_and_propagate(1509    func: &HirFunction,1510    env: &Environment,1511    temporaries: &FxHashMap<IdentifierId, ReactiveScopeDependency>,1512    hoistable_from_optionals: &FxHashMap<BlockId, ReactiveScopeDependency>,1513) -> (FxHashMap<BlockId, BTreeSet<usize>>, PropertyPathRegistry) {1514    let mut registry = PropertyPathRegistry::new();1515    let assumed_invoked_fns = get_assumed_invoked_functions(func, env);1516    let known_immutable_identifiers: FxHashSet<IdentifierId> = if func.fn_type1517        == ReactFunctionType::Component1518        || func.fn_type == ReactFunctionType::Hook1519    {1520        func.params1521            .iter()1522            .filter_map(|p| match p {1523                ParamPattern::Place(place) => Some(place.identifier),1524                _ => None,1525            })1526            .collect()1527    } else {1528        FxHashSet::default()1529    };15301531    let ctx = CollectHoistableContext {1532        temporaries,1533        known_immutable_identifiers: &known_immutable_identifiers,1534        hoistable_from_optionals,1535        nested_fn_immutable_context: None,1536        assumed_invoked_fns: &assumed_invoked_fns,1537    };15381539    let nodes = collect_non_nulls_in_blocks(func, env, &ctx, &mut registry);1540    let working = propagate_non_null(func, &nodes, &mut registry);15411542    (working, registry)1543}15441545// Restructured version used by the main entry point1546#[allow(dead_code)]1547fn key_by_scope_id(1548    func: &HirFunction,1549    block_keyed: &FxHashMap<BlockId, BlockInfo>,1550) -> FxHashMap<ScopeId, BlockInfo> {1551    let mut keyed: FxHashMap<ScopeId, BlockInfo> = FxHashMap::default();1552    for (_block_id, block) in &func.body.blocks {1553        if let Terminal::Scope {1554            scope,1555            block: inner_block,1556            ..1557        } = &block.terminal1558        {1559            if let Some(info) = block_keyed.get(inner_block) {1560                keyed.insert(*scope, info.clone());1561            }1562        }1563    }1564    keyed1565}15661567// =============================================================================1568// DeriveMinimalDependenciesHIR1569// =============================================================================15701571#[derive(Debug, Clone, Copy, PartialEq, Eq)]1572enum PropertyAccessType {1573    OptionalAccess,1574    UnconditionalAccess,1575    OptionalDependency,1576    UnconditionalDependency,1577}15781579fn is_optional_access(access: PropertyAccessType) -> bool {1580    matches!(1581        access,1582        PropertyAccessType::OptionalAccess | PropertyAccessType::OptionalDependency1583    )1584}15851586fn is_dependency_access(access: PropertyAccessType) -> bool {1587    matches!(1588        access,1589        PropertyAccessType::OptionalDependency | PropertyAccessType::UnconditionalDependency1590    )1591}15921593fn merge_access(a: PropertyAccessType, b: PropertyAccessType) -> PropertyAccessType {1594    let is_unconditional = !(is_optional_access(a) && is_optional_access(b));1595    let is_dep = is_dependency_access(a) || is_dependency_access(b);1596    match (is_unconditional, is_dep) {1597        (true, true) => PropertyAccessType::UnconditionalDependency,1598        (true, false) => PropertyAccessType::UnconditionalAccess,1599        (false, true) => PropertyAccessType::OptionalDependency,1600        (false, false) => PropertyAccessType::OptionalAccess,1601    }1602}16031604#[derive(Debug, Clone, Copy, PartialEq, Eq)]1605enum HoistableAccessType {1606    Optional,1607    NonNull,1608}16091610struct HoistableNode {1611    properties: FxHashMap<PropertyLiteral, Box<HoistableNodeEntry>>,1612    access_type: HoistableAccessType,1613}16141615struct HoistableNodeEntry {1616    node: HoistableNode,1617}16181619struct DependencyNode {1620    properties: IndexMap<PropertyLiteral, Box<DependencyNodeEntry>, FxBuildHasher>,1621    access_type: PropertyAccessType,1622    loc: Option<react_compiler_hir::SourceLocation>,1623}16241625struct DependencyNodeEntry {1626    node: DependencyNode,1627}16281629struct ReactiveScopeDependencyTreeHIR {1630    hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)>, // node + reactive1631    dep_roots: IndexMap<IdentifierId, (DependencyNode, bool), FxBuildHasher>, // node + reactive (preserves insertion order like JS Map)1632}16331634impl ReactiveScopeDependencyTreeHIR {1635    fn new<'a>(1636        hoistable_objects: impl Iterator<Item = &'a ReactiveScopeDependency>,1637        _env: &Environment,1638    ) -> Self {1639        let mut hoistable_roots: FxHashMap<IdentifierId, (HoistableNode, bool)> =1640            FxHashMap::default();16411642        // Sort hoistable objects so that entries with optional first path come1643        // before non-optional ones. This matches the TS behavior where1644        // hoistableFromOptionals entries are inserted into the JS Set before1645        // instruction-based entries, and the first insertion determines the1646        // root access type.1647        let mut sorted_deps: Vec<&ReactiveScopeDependency> = hoistable_objects.collect();1648        sorted_deps.sort_by(|a, b| {1649            let a_optional = !a.path.is_empty() && a.path[0].optional;1650            let b_optional = !b.path.is_empty() && b.path[0].optional;1651            b_optional.cmp(&a_optional)1652        });16531654        for dep in sorted_deps {1655            let root = hoistable_roots.entry(dep.identifier).or_insert_with(|| {1656                let access_type = if !dep.path.is_empty() && dep.path[0].optional {1657                    HoistableAccessType::Optional1658                } else {1659                    HoistableAccessType::NonNull1660                };1661                (1662                    HoistableNode {1663                        properties: FxHashMap::default(),1664                        access_type,1665                    },1666                    dep.reactive,1667                )1668            });16691670            let mut curr = &mut root.0;1671            for i in 0..dep.path.len() {1672                let access_type = if i + 1 < dep.path.len() && dep.path[i + 1].optional {1673                    HoistableAccessType::Optional1674                } else {1675                    HoistableAccessType::NonNull1676                };1677                let entry = curr1678                    .properties1679                    .entry(dep.path[i].property.clone())1680                    .or_insert_with(|| {1681                        Box::new(HoistableNodeEntry {1682                            node: HoistableNode {1683                                properties: FxHashMap::default(),1684                                access_type,1685                            },1686                        })1687                    });1688                curr = &mut entry.node;1689            }1690        }16911692        Self {1693            hoistable_roots,1694            dep_roots: IndexMap::default(),1695        }1696    }16971698    fn add_dependency(&mut self, dep: ReactiveScopeDependency, _env: &Environment) {1699        let root = self.dep_roots.entry(dep.identifier).or_insert_with(|| {1700            (1701                DependencyNode {1702                    properties: IndexMap::default(),1703                    access_type: PropertyAccessType::UnconditionalAccess,1704                    loc: dep.loc,1705                },1706                dep.reactive,1707            )1708        });17091710        let mut dep_cursor = &mut root.0;1711        let hoistable_cursor_root = self.hoistable_roots.get(&dep.identifier);1712        let mut hoistable_ptr: Option<&HoistableNode> = hoistable_cursor_root.map(|(n, _)| n);17131714        for entry in &dep.path {1715            let next_hoistable: Option<&HoistableNode>;1716            let access_type: PropertyAccessType;17171718            if entry.optional {1719                next_hoistable =1720                    hoistable_ptr.and_then(|h| h.properties.get(&entry.property).map(|e| &e.node));17211722                if hoistable_ptr.is_some()1723                    && hoistable_ptr.unwrap().access_type == HoistableAccessType::NonNull1724                {1725                    access_type = PropertyAccessType::UnconditionalAccess;1726                } else {1727                    access_type = PropertyAccessType::OptionalAccess;1728                }1729            } else if hoistable_ptr.is_some()1730                && hoistable_ptr.unwrap().access_type == HoistableAccessType::NonNull1731            {1732                next_hoistable =1733                    hoistable_ptr.and_then(|h| h.properties.get(&entry.property).map(|e| &e.node));1734                access_type = PropertyAccessType::UnconditionalAccess;1735            } else {1736                // Break: truncate dependency1737                break;1738            }17391740            // make_or_merge_property1741            let child = dep_cursor1742                .properties1743                .entry(entry.property.clone())1744                .or_insert_with(|| {1745                    Box::new(DependencyNodeEntry {1746                        node: DependencyNode {1747                            properties: IndexMap::default(),1748                            access_type,1749                            loc: entry.loc,1750                        },1751                    })1752                });1753            child.node.access_type = merge_access(child.node.access_type, access_type);17541755            dep_cursor = &mut child.node;1756            hoistable_ptr = next_hoistable;1757        }17581759        // Mark final node as dependency1760        dep_cursor.access_type = merge_access(1761            dep_cursor.access_type,1762            PropertyAccessType::OptionalDependency,1763        );1764    }17651766    fn derive_minimal_dependencies(&self, _env: &Environment) -> Vec<ReactiveScopeDependency> {1767        let mut results = Vec::new();1768        for (&root_id, (root_node, reactive)) in &self.dep_roots {1769            collect_minimal_deps_in_subtree(root_node, *reactive, root_id, &[], &mut results);1770        }1771        results1772    }1773}17741775fn collect_minimal_deps_in_subtree(1776    node: &DependencyNode,1777    reactive: bool,1778    root_id: IdentifierId,1779    path: &[DependencyPathEntry],1780    results: &mut Vec<ReactiveScopeDependency>,1781) {1782    if is_dependency_access(node.access_type) {1783        results.push(ReactiveScopeDependency {1784            identifier: root_id,1785            reactive,1786            path: path.to_vec(),1787            loc: node.loc,1788        });1789    } else {1790        for (child_name, child_entry) in &node.properties {1791            let mut new_path = path.to_vec();1792            new_path.push(DependencyPathEntry {1793                property: child_name.clone(),1794                optional: is_optional_access(child_entry.node.access_type),1795                loc: child_entry.node.loc,1796            });1797            collect_minimal_deps_in_subtree(1798                &child_entry.node,1799                reactive,1800                root_id,1801                &new_path,1802                results,1803            );1804        }1805    }1806}18071808// =============================================================================1809// collectDependencies1810// =============================================================================18111812/// A declaration record: instruction id + scope stack at declaration time.1813#[derive(Clone)]1814struct Decl {1815    id: EvaluationOrder,1816    scope_stack: Vec<ScopeId>, // copy of the scope stack at time of declaration1817}18181819/// Context for dependency collection.1820struct DependencyCollectionContext<'a> {1821    declarations: FxHashMap<DeclarationId, Decl>,1822    reassignments: FxHashMap<IdentifierId, Decl>,1823    scope_stack: Vec<ScopeId>,1824    dep_stack: Vec<Vec<ReactiveScopeDependency>>,1825    deps: IndexMap<ScopeId, Vec<ReactiveScopeDependency>, FxBuildHasher>,1826    temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,1827    #[allow(dead_code)]1828    temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,1829    processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,1830    inner_fn_context: Option<EvaluationOrder>,1831}18321833impl<'a> DependencyCollectionContext<'a> {1834    fn new(1835        temporaries_used_outside_scope: &'a FxHashSet<DeclarationId>,1836        temporaries: &'a FxHashMap<IdentifierId, ReactiveScopeDependency>,1837        processed_instrs_in_optional: &'a FxHashSet<ProcessedInstr>,1838    ) -> Self {1839        Self {1840            declarations: FxHashMap::default(),1841            reassignments: FxHashMap::default(),1842            scope_stack: Vec::new(),1843            dep_stack: Vec::new(),1844            deps: IndexMap::default(),1845            temporaries,1846            temporaries_used_outside_scope,1847            processed_instrs_in_optional,1848            inner_fn_context: None,1849        }1850    }18511852    fn enter_scope(&mut self, scope_id: ScopeId) {1853        self.dep_stack.push(Vec::new());1854        self.scope_stack.push(scope_id);1855    }18561857    fn exit_scope(&mut self, scope_id: ScopeId, pruned: bool, env: &mut Environment) {1858        let scoped_deps = self1859            .dep_stack1860            .pop()1861            .expect("[PropagateScopeDeps]: Unexpected scope mismatch");1862        self.scope_stack.pop();18631864        // Propagate dependencies upward1865        for dep in &scoped_deps {1866            if self.check_valid_dependency(dep, env) {1867                if let Some(top) = self.dep_stack.last_mut() {1868                    top.push(dep.clone());1869                }1870            }1871        }18721873        if !pruned {1874            self.deps.insert(scope_id, scoped_deps);1875        }1876    }18771878    fn current_scope(&self) -> Option<ScopeId> {1879        self.scope_stack.last().copied()1880    }18811882    fn declare(&mut self, identifier_id: IdentifierId, decl: Decl, env: &Environment) {1883        if self.inner_fn_context.is_some() {1884            return;1885        }1886        let decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;1887        if !self.declarations.contains_key(&decl_id) {1888            self.declarations.insert(decl_id, decl.clone());1889        }1890        self.reassignments.insert(identifier_id, decl);1891    }18921893    fn has_declared(&self, identifier_id: IdentifierId, env: &Environment) -> bool {1894        let decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;1895        self.declarations.contains_key(&decl_id)1896    }18971898    fn check_valid_dependency(&self, dep: &ReactiveScopeDependency, env: &Environment) -> bool {1899        // Ref value is not a valid dep1900        let ty = &env.types[env.identifiers[dep.identifier.0 as usize].type_.0 as usize];1901        if react_compiler_hir::is_ref_value_type(ty) {1902            return false;1903        }1904        // Object methods are not deps1905        if matches!(ty, Type::ObjectMethod) {1906            return false;1907        }19081909        let ident = &env.identifiers[dep.identifier.0 as usize];1910        let current_declaration = self1911            .reassignments1912            .get(&dep.identifier)1913            .or_else(|| self.declarations.get(&ident.declaration_id));19141915        if let Some(current_scope) = self.current_scope() {1916            if let Some(decl) = current_declaration {1917                let scope_range_start = env.scopes[current_scope.0 as usize].range.start;1918                return decl.id < scope_range_start;1919            }1920        }1921        false1922    }19231924    fn visit_operand(&mut self, place: &Place, env: &mut Environment) {1925        let dep = self1926            .temporaries1927            .get(&place.identifier)1928            .cloned()1929            .unwrap_or_else(|| ReactiveScopeDependency {1930                identifier: place.identifier,1931                reactive: place.reactive,1932                path: vec![],1933                loc: place.loc,1934            });1935        self.visit_dependency(dep, env);1936    }19371938    fn visit_property(1939        &mut self,1940        object: &Place,1941        property: &PropertyLiteral,1942        optional: bool,1943        loc: Option<react_compiler_hir::SourceLocation>,1944        env: &mut Environment,1945    ) {1946        let dep = get_property(object, property, optional, loc, self.temporaries, env);1947        self.visit_dependency(dep, env);1948    }19491950    fn visit_dependency(&mut self, dep: ReactiveScopeDependency, env: &mut Environment) {1951        let ident = &env.identifiers[dep.identifier.0 as usize];1952        let decl_id = ident.declaration_id;19531954        // Record scope declarations for values used outside their declaring scope1955        if let Some(original_decl) = self.declarations.get(&decl_id) {1956            if !original_decl.scope_stack.is_empty() {1957                let orig_scope_stack = original_decl.scope_stack.clone();1958                for &scope_id in &orig_scope_stack {1959                    if !self.scope_stack.contains(&scope_id) {1960                        // Check if already declared in this scope1961                        let scope = &env.scopes[scope_id.0 as usize];1962                        let already_declared = scope.declarations.iter().any(|(_, d)| {1963                            env.identifiers[d.identifier.0 as usize].declaration_id == decl_id1964                        });1965                        if !already_declared {1966                            let orig_scope_id = *orig_scope_stack.last().unwrap();1967                            let new_decl = react_compiler_hir::ReactiveScopeDeclaration {1968                                identifier: dep.identifier,1969                                scope: orig_scope_id,1970                            };1971                            env.scopes[scope_id.0 as usize]1972                                .declarations1973                                .push((dep.identifier, new_decl));1974                        }1975                    }1976                }1977            }1978        }19791980        // Handle ref.current access1981        let dep = if react_compiler_hir::is_use_ref_type(1982            &env.types[env.identifiers[dep.identifier.0 as usize].type_.0 as usize],1983        ) && dep1984            .path1985            .first()1986            .map(|p| p.property == PropertyLiteral::String("current".to_string()))1987            .unwrap_or(false)1988        {1989            ReactiveScopeDependency {1990                identifier: dep.identifier,1991                reactive: dep.reactive,1992                path: vec![],1993                loc: dep.loc,1994            }1995        } else {1996            dep1997        };19981999        if self.check_valid_dependency(&dep, env) {2000            if let Some(top) = self.dep_stack.last_mut() {

Code quality findings 100

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
.map(|&idx| registry.nodes[idx].full_path.clone())
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
hoistables.expect("[PropagateScopeDependencies] Scope not found in tracked blocks");
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 scope = &mut env.scopes[scope_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[existing_dep.identifier.0 as usize].declaration_id;
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[candidate_dep.identifier.0 as usize].declaration_id;
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 decl_id = env.identifiers[place_id.0 as usize].declaration_id;
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[instr.lvalue.identifier.0 as usize].declaration_id;
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
if let Some(scope_id) = env.identifiers[place.identifier.0 as usize].scope {
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 scope_range = &env.scopes[scope_id.0 as usize].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
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 lvalue_decl_id = env.identifiers[instr.lvalue.identifier.0 as usize].declaration_id;
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
if env.identifiers[instr.lvalue.identifier.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[place.identifier.0 as usize].name.is_some()
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[instr.lvalue.identifier.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[place.identifier.0 as usize].name.is_some()
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[lowered_func.func.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 = &env.functions[lowered_func.func.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 instr0 = &func.instructions[consequent_block.instructions[0].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 instr1 = &func.instructions[consequent_block.instructions[1].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 alt_instr0 = &func.instructions[alternate_block.instructions[0].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 alt_instr1 = &func.instructions[alternate_block.instructions[1].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 first_instr = &func.instructions[maybe_test_block.instructions[0].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 curr_instr = &func.instructions[maybe_test_block.instructions[i].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
&func.instructions[maybe_test_block.instructions[i - 1].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 last_instr_id = *maybe_test_block.instructions.last().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 last_instr = &func.instructions[last_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
self.nodes[parent_idx]
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
self.nodes[parent_idx].properties.get(&map_key).copied()
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 parent_full_path = self.nodes[parent_idx].full_path.clone();
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 parent_has_optional = self.nodes[parent_idx].has_optional;
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
self.nodes[parent_idx]
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
self.nodes[parent_idx].properties.insert(map_key, idx);
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
.filter(|&idx| registry.nodes[idx].has_optional)
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 full_path = registry.nodes[original_idx].full_path.clone();
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 ident = &env.identifiers[identifier_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 scope = &env.scopes[ident.scope.unwrap().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 scope = &env.scopes[ident.scope.unwrap().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 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
let inner_func = &env.functions[lowered_func.func.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
if let ParamPattern::Place(place) = &func.params[0] {
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
if dep.path[i].optional {
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
path: dep.path[..i].to_vec(),
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[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
ctx.nested_fn_immutable_context.unwrap().clone()
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 a_optional = !a.path.is_empty() && a.path[0].optional;
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 b_optional = !b.path.is_empty() && b.path[0].optional;
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 access_type = if !dep.path.is_empty() && dep.path[0].optional {
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
&& hoistable_ptr.unwrap().access_type == HoistableAccessType::NonNull
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
&& hoistable_ptr.unwrap().access_type == HoistableAccessType::NonNull
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
.expect("[PropagateScopeDeps]: Unexpected scope mismatch");
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 decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;
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 decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;
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 ty = &env.types[env.identifiers[dep.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 ident = &env.identifiers[dep.identifier.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 scope_range_start = env.scopes[current_scope.0 as usize].range.start;
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 ident = &env.identifiers[dep.identifier.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 scope = &env.scopes[scope_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[d.identifier.0 as usize].declaration_id == decl_id
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 orig_scope_id = *orig_scope_stack.last().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
env.scopes[scope_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[dep.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 scope = &env.scopes[current_scope.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[id.0 as usize].declaration_id
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[place.identifier.0 as usize].declaration_id
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.scopes[current_scope.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_instrs: Vec<Instruction> = env.functions[func_id.0 as usize].instructions.clone();
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.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 inner_instr = &inner_instrs[iid.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];
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
.map(|&idx| registry.nodes[idx].full_path.clone())
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
tree.add_dependency(dep.clone(), env);
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
scope.dependencies.push(candidate_dep);
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 kind {
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
let (test_place, consequent_block_id, alternate_block_id) = match test {
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
let (property_load_object, property, property_load_loc) = match &instr0.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
let store_local_value = match &instr1.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 (&alt_instr0.value, &alt_instr1.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
let (test_block_id, is_optional, fallthrough_block_id) = match &optional_block.terminal {
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
let test_ident = match &maybe_test_block.terminal {
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
let first_place = match &first_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
let inner_alternate = match &test_block.terminal {
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
let test_ident = match &test_block.terminal {
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
let test_alternate = match test_terminal {
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(dead_code)]
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(dead_code)]
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 full_path = registry.nodes[original_idx].full_path.clone();
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
property: entry.property.clone(),
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
entry.clone()
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(dead_code)]
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
.filter_map(|p| match p {
Info: Usage of `#[allow(...)]` suppresses compiler lints. Ensure the allowance is justified, well-scoped, and ideally temporary. Overuse can hide potential issues.
info maintainability allow-lint
#[allow(dead_code)]
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
to_add.push(called);
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 mut assumed = known_non_null.clone();

Get this view in your editor

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