compiler/crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs RUST 578 lines View on github.com → Search inside
1// Copyright (c) Meta Platforms, Inc. and affiliates.2//3// This source code is licensed under the MIT license found in the4// LICENSE file in the root directory of this source tree.56//! Validates against calling setState in the body of an effect (useEffect and friends),7//! while allowing calling setState in callbacks scheduled by the effect.8//!9//! Calling setState during execution of a useEffect triggers a re-render, which is10//! often bad for performance and frequently has more efficient and straightforward11//! alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples.12//!13//! Port of ValidateNoSetStateInEffects.ts.1415use rustc_hash::{FxHashMap, FxHashSet};1617use react_compiler_diagnostics::{18    CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,19};20use react_compiler_hir::dominator::{compute_post_dominator_tree, post_dominator_frontier};21use react_compiler_hir::environment::Environment;22use react_compiler_hir::{23    BlockId, HirFunction, Identifier, IdentifierId, IdentifierName, InstructionValue,24    PlaceOrSpread, PropertyLiteral, SourceLocation, Terminal, Type, is_ref_value_type,25    is_set_state_type, is_use_effect_event_type, is_use_effect_hook_type,26    is_use_insertion_effect_hook_type, is_use_layout_effect_hook_type, is_use_ref_type, visitors,27};2829pub fn validate_no_set_state_in_effects(30    func: &HirFunction,31    env: &Environment,32) -> Result<CompilerError, CompilerDiagnostic> {33    let identifiers = &env.identifiers;34    let types = &env.types;35    let functions = &env.functions;36    let enable_verbose = env.config.enable_verbose_no_set_state_in_effect;37    let enable_allow_set_state_from_refs = env.config.enable_allow_set_state_from_refs_in_effects;3839    // Map from IdentifierId to the Place where the setState originated40    let mut set_state_functions: FxHashMap<IdentifierId, SetStateInfo> = FxHashMap::default();41    let mut errors = CompilerError::new();4243    for (_block_id, block) in &func.body.blocks {44        for &instr_id in &block.instructions {45            let instr = &func.instructions[instr_id.0 as usize];46            match &instr.value {47                InstructionValue::LoadLocal { place, .. } => {48                    if set_state_functions.contains_key(&place.identifier) {49                        let info = set_state_functions[&place.identifier].clone();50                        set_state_functions.insert(instr.lvalue.identifier, info);51                    }52                }53                InstructionValue::StoreLocal { lvalue, value, .. } => {54                    if set_state_functions.contains_key(&value.identifier) {55                        let info = set_state_functions[&value.identifier].clone();56                        set_state_functions.insert(lvalue.place.identifier, info.clone());57                        set_state_functions.insert(instr.lvalue.identifier, info);58                    }59                }60                InstructionValue::FunctionExpression { lowered_func, .. } => {61                    // Check if any context capture references a setState62                    let inner_func = &functions[lowered_func.func.0 as usize];63                    let has_set_state_operand = inner_func.context.iter().any(|ctx_place| {64                        is_set_state_type_by_id(ctx_place.identifier, identifiers, types)65                            || set_state_functions.contains_key(&ctx_place.identifier)66                    });6768                    if has_set_state_operand {69                        let callee = get_set_state_call(70                            inner_func,71                            &mut set_state_functions,72                            identifiers,73                            types,74                            functions,75                            enable_allow_set_state_from_refs,76                            env.next_block_id_counter,77                            env.code.as_deref(),78                        )?;79                        if let Some(info) = callee {80                            set_state_functions.insert(instr.lvalue.identifier, info);81                        }82                    }83                }84                InstructionValue::MethodCall { property, args, .. } => {85                    let prop_type =86                        &types[identifiers[property.identifier.0 as usize].type_.0 as usize];87                    if is_use_effect_event_type(prop_type) {88                        if let Some(first_arg) = args.first() {89                            if let PlaceOrSpread::Place(arg_place) = first_arg {90                                if let Some(info) = set_state_functions.get(&arg_place.identifier) {91                                    set_state_functions92                                        .insert(instr.lvalue.identifier, info.clone());93                                }94                            }95                        }96                    } else if is_use_effect_hook_type(prop_type)97                        || is_use_layout_effect_hook_type(prop_type)98                        || is_use_insertion_effect_hook_type(prop_type)99                    {100                        if let Some(first_arg) = args.first() {101                            if let PlaceOrSpread::Place(arg_place) = first_arg {102                                if let Some(info) = set_state_functions.get(&arg_place.identifier) {103                                    push_error(&mut errors, info, enable_verbose);104                                }105                            }106                        }107                    }108                }109                InstructionValue::CallExpression { callee, args, .. } => {110                    let callee_type =111                        &types[identifiers[callee.identifier.0 as usize].type_.0 as usize];112                    if is_use_effect_event_type(callee_type) {113                        if let Some(first_arg) = args.first() {114                            if let PlaceOrSpread::Place(arg_place) = first_arg {115                                if let Some(info) = set_state_functions.get(&arg_place.identifier) {116                                    set_state_functions117                                        .insert(instr.lvalue.identifier, info.clone());118                                }119                            }120                        }121                    } else if is_use_effect_hook_type(callee_type)122                        || is_use_layout_effect_hook_type(callee_type)123                        || is_use_insertion_effect_hook_type(callee_type)124                    {125                        if let Some(first_arg) = args.first() {126                            if let PlaceOrSpread::Place(arg_place) = first_arg {127                                if let Some(info) = set_state_functions.get(&arg_place.identifier) {128                                    push_error(&mut errors, info, enable_verbose);129                                }130                            }131                        }132                    }133                }134                _ => {}135            }136        }137    }138139    Ok(errors)140}141142#[derive(Debug, Clone)]143struct SetStateInfo {144    loc: Option<SourceLocation>,145    identifier_name: Option<String>,146}147148/// Get the user-visible name for an identifier, matching Babel's149/// loc.identifierName behavior. First checks the identifier's own name,150/// then falls back to extracting the name from the source code at the151/// given source location (the callee's loc). This handles SSA identifiers152/// whose names were lost during compiler passes.153fn get_identifier_name_with_loc(154    id: IdentifierId,155    identifiers: &[Identifier],156    loc: &Option<SourceLocation>,157    source_code: Option<&str>,158) -> Option<String> {159    let ident = &identifiers[id.0 as usize];160    if let Some(IdentifierName::Named(name)) = &ident.name {161        return Some(name.clone());162    }163    // Fall back to extracting from source code164    if let (Some(loc), Some(code)) = (loc, source_code) {165        let start_idx = loc.start.index? as usize;166        let end_idx = loc.end.index? as usize;167        if start_idx < code.len() && end_idx <= code.len() && start_idx < end_idx {168            let slice = &code[start_idx..end_idx];169            if !slice.is_empty()170                && slice171                    .chars()172                    .all(|c| c.is_alphanumeric() || c == '_' || c == '$')173            {174                return Some(slice.to_string());175            }176        }177    }178    None179}180181fn is_set_state_type_by_id(182    identifier_id: IdentifierId,183    identifiers: &[Identifier],184    types: &[Type],185) -> bool {186    let ident = &identifiers[identifier_id.0 as usize];187    let ty = &types[ident.type_.0 as usize];188    is_set_state_type(ty)189}190191fn push_error(errors: &mut CompilerError, info: &SetStateInfo, enable_verbose: bool) {192    if enable_verbose {193        errors.push_diagnostic(194            CompilerDiagnostic::new(195                ErrorCategory::EffectSetState,196                "Calling setState synchronously within an effect can trigger cascading renders",197                Some(198                    "Effects are intended to synchronize state between React and external systems. \199                     Calling setState synchronously causes cascading renders that hurt performance.\n\n\200                     This pattern may indicate one of several issues:\n\n\201                     **1. Non-local derived data**: If the value being set could be computed from props/state \202                     but requires data from a parent component, consider restructuring state ownership so the \203                     derivation can happen during render in the component that owns the relevant state.\n\n\204                     **2. Derived event pattern**: If you're detecting when a prop changes (e.g., `isPlaying` \205                     transitioning from false to true), this often indicates the parent should provide an event \206                     callback (like `onPlay`) instead of just the current state. Request access to the original event.\n\n\207                     **3. Force update / external sync**: If you're forcing a re-render to sync with an external \208                     data source (mutable values outside React), use `useSyncExternalStore` to properly subscribe \209                     to external state changes.\n\n\210                     See: https://react.dev/learn/you-might-not-need-an-effect".to_string(),211                ),212            )213            .with_detail(CompilerDiagnosticDetail::Error {214                loc: info.loc,215                message: Some(216                    "Avoid calling setState() directly within an effect".to_string(),217                ),218                identifier_name: info.identifier_name.clone(),219            }),220        );221    } else {222        errors.push_diagnostic(223            CompilerDiagnostic::new(224                ErrorCategory::EffectSetState,225                "Calling setState synchronously within an effect can trigger cascading renders",226                Some(227                    "Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. \228                     In general, the body of an effect should do one or both of the following:\n\229                     * Update external systems with the latest state from React.\n\230                     * Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n\231                     Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. \232                     (https://react.dev/learn/you-might-not-need-an-effect)".to_string(),233                ),234            )235            .with_detail(CompilerDiagnosticDetail::Error {236                loc: info.loc,237                message: Some(238                    "Avoid calling setState() directly within an effect".to_string(),239                ),240                identifier_name: info.identifier_name.clone(),241            }),242        );243    }244}245246/// Recursively collect all Place identifiers from a destructure pattern.247fn collect_destructure_places(248    pattern: &react_compiler_hir::Pattern,249    ref_derived_values: &mut FxHashSet<IdentifierId>,250) {251    match pattern {252        react_compiler_hir::Pattern::Array(arr) => {253            for item in &arr.items {254                match item {255                    react_compiler_hir::ArrayPatternElement::Place(p) => {256                        ref_derived_values.insert(p.identifier);257                    }258                    react_compiler_hir::ArrayPatternElement::Spread(s) => {259                        ref_derived_values.insert(s.place.identifier);260                    }261                    react_compiler_hir::ArrayPatternElement::Hole => {}262                }263            }264        }265        react_compiler_hir::Pattern::Object(obj) => {266            for prop in &obj.properties {267                match prop {268                    react_compiler_hir::ObjectPropertyOrSpread::Property(p) => {269                        ref_derived_values.insert(p.place.identifier);270                    }271                    react_compiler_hir::ObjectPropertyOrSpread::Spread(s) => {272                        ref_derived_values.insert(s.place.identifier);273                    }274                }275            }276        }277    }278}279280fn is_derived_from_ref(281    id: IdentifierId,282    ref_derived_values: &FxHashSet<IdentifierId>,283    identifiers: &[Identifier],284    types: &[Type],285) -> bool {286    if ref_derived_values.contains(&id) {287        return true;288    }289    let ident = &identifiers[id.0 as usize];290    let ty = &types[ident.type_.0 as usize];291    is_use_ref_type(ty) || is_ref_value_type(ty)292}293294/// Collects all operand IdentifierIds from an instruction value.295/// Uses the canonical `each_instruction_value_operand_with_functions` from visitors.296fn collect_operands(value: &InstructionValue, functions: &[HirFunction]) -> Vec<IdentifierId> {297    visitors::each_instruction_value_operand_with_functions(value, functions)298        .into_iter()299        .map(|p| p.identifier)300        .collect()301}302303/// Creates a function that checks whether a block is "control-dominated" by304/// a ref-derived condition. A block is ref-controlled if its post-dominator305/// frontier contains a block whose terminal tests a ref-derived value.306fn create_ref_controlled_block_checker(307    func: &HirFunction,308    next_block_id_counter: u32,309    ref_derived_values: &FxHashSet<IdentifierId>,310    identifiers: &[Identifier],311    types: &[Type],312) -> Result<FxHashMap<BlockId, bool>, CompilerDiagnostic> {313    let post_dominators = compute_post_dominator_tree(func, next_block_id_counter, false)?;314    let mut cache: FxHashMap<BlockId, bool> = FxHashMap::default();315316    for (block_id, _block) in &func.body.blocks {317        let frontier = post_dominator_frontier(func, &post_dominators, *block_id);318        let mut is_controlled = false;319320        for frontier_block_id in &frontier {321            let control_block = &func.body.blocks[frontier_block_id];322            match &control_block.terminal {323                Terminal::If { test, .. } | Terminal::Branch { test, .. } => {324                    if is_derived_from_ref(test.identifier, ref_derived_values, identifiers, types)325                    {326                        is_controlled = true;327                        break;328                    }329                }330                Terminal::Switch { test, cases, .. } => {331                    if is_derived_from_ref(test.identifier, ref_derived_values, identifiers, types)332                    {333                        is_controlled = true;334                        break;335                    }336                    for case in cases {337                        if let Some(case_test) = &case.test {338                            if is_derived_from_ref(339                                case_test.identifier,340                                ref_derived_values,341                                identifiers,342                                types,343                            ) {344                                is_controlled = true;345                                break;346                            }347                        }348                    }349                    if is_controlled {350                        break;351                    }352                }353                _ => {}354            }355        }356357        cache.insert(*block_id, is_controlled);358    }359360    Ok(cache)361}362363/// Checks inner function body for direct setState calls. Returns the callee Place info364/// if a setState call is found in the function body.365/// Tracks ref-derived values to allow setState when the value being set comes from a ref.366fn get_set_state_call(367    func: &HirFunction,368    set_state_functions: &mut FxHashMap<IdentifierId, SetStateInfo>,369    identifiers: &[Identifier],370    types: &[Type],371    functions: &[HirFunction],372    enable_allow_set_state_from_refs: bool,373    next_block_id_counter: u32,374    source_code: Option<&str>,375) -> Result<Option<SetStateInfo>, CompilerDiagnostic> {376    let mut ref_derived_values: FxHashSet<IdentifierId> = FxHashSet::default();377378    // First pass: collect ref-derived values (needed before building control dominator checker)379    // We do a pre-pass to seed ref_derived_values so the control dominator checker has them.380    if enable_allow_set_state_from_refs {381        for (_block_id, block) in &func.body.blocks {382            for phi in &block.phis {383                let is_phi_derived = phi.operands.values().any(|operand| {384                    is_derived_from_ref(operand.identifier, &ref_derived_values, identifiers, types)385                });386                if is_phi_derived {387                    ref_derived_values.insert(phi.place.identifier);388                }389            }390391            for &instr_id in &block.instructions {392                let instr = &func.instructions[instr_id.0 as usize];393394                let operands = collect_operands(&instr.value, functions);395                let has_ref_operand = operands.iter().any(|op_id| {396                    is_derived_from_ref(*op_id, &ref_derived_values, identifiers, types)397                });398399                if has_ref_operand {400                    ref_derived_values.insert(instr.lvalue.identifier);401                    if let InstructionValue::Destructure { lvalue, .. } = &instr.value {402                        collect_destructure_places(&lvalue.pattern, &mut ref_derived_values);403                    }404                    if let InstructionValue::StoreLocal { lvalue, .. } = &instr.value {405                        ref_derived_values.insert(lvalue.place.identifier);406                    }407                }408409                if let InstructionValue::PropertyLoad {410                    object, property, ..411                } = &instr.value412                {413                    if *property == PropertyLiteral::String("current".to_string()) {414                        let obj_ident = &identifiers[object.identifier.0 as usize];415                        let obj_ty = &types[obj_ident.type_.0 as usize];416                        if is_use_ref_type(obj_ty) || is_ref_value_type(obj_ty) {417                            ref_derived_values.insert(instr.lvalue.identifier);418                        }419                    }420                }421            }422        }423    }424425    // Build control dominator checker after collecting ref-derived values426    let ref_controlled_blocks = if enable_allow_set_state_from_refs {427        create_ref_controlled_block_checker(428            func,429            next_block_id_counter,430            &ref_derived_values,431            identifiers,432            types,433        )?434    } else {435        FxHashMap::default()436    };437438    let is_ref_controlled_block = |block_id: BlockId| -> bool {439        ref_controlled_blocks440            .get(&block_id)441            .copied()442            .unwrap_or(false)443    };444445    // Reset and redo: second pass with control dominator info available446    ref_derived_values.clear();447448    for (_block_id, block) in &func.body.blocks {449        // Track ref-derived values through phis450        if enable_allow_set_state_from_refs {451            for phi in &block.phis {452                if is_derived_from_ref(453                    phi.place.identifier,454                    &ref_derived_values,455                    identifiers,456                    types,457                ) {458                    continue;459                }460                let is_phi_derived = phi.operands.values().any(|operand| {461                    is_derived_from_ref(operand.identifier, &ref_derived_values, identifiers, types)462                });463                if is_phi_derived {464                    ref_derived_values.insert(phi.place.identifier);465                } else {466                    // Fallback: check if any predecessor block is ref-controlled467                    let mut found = false;468                    for pred in phi.operands.keys() {469                        if is_ref_controlled_block(*pred) {470                            ref_derived_values.insert(phi.place.identifier);471                            found = true;472                            break;473                        }474                    }475                    if found {476                        continue;477                    }478                }479            }480        }481482        for &instr_id in &block.instructions {483            let instr = &func.instructions[instr_id.0 as usize];484485            // Track ref-derived values through instructions486            if enable_allow_set_state_from_refs {487                let operands = collect_operands(&instr.value, functions);488                let has_ref_operand = operands.iter().any(|op_id| {489                    is_derived_from_ref(*op_id, &ref_derived_values, identifiers, types)490                });491492                if has_ref_operand {493                    ref_derived_values.insert(instr.lvalue.identifier);494                    // For Destructure, also mark all pattern places as ref-derived495                    if let InstructionValue::Destructure { lvalue, .. } = &instr.value {496                        collect_destructure_places(&lvalue.pattern, &mut ref_derived_values);497                    }498                    // For StoreLocal, propagate to the local variable499                    if let InstructionValue::StoreLocal { lvalue, .. } = &instr.value {500                        ref_derived_values.insert(lvalue.place.identifier);501                    }502                }503504                // Special case: PropertyLoad of .current on ref/refValue505                if let InstructionValue::PropertyLoad {506                    object, property, ..507                } = &instr.value508                {509                    if *property == PropertyLiteral::String("current".to_string()) {510                        let obj_ident = &identifiers[object.identifier.0 as usize];511                        let obj_ty = &types[obj_ident.type_.0 as usize];512                        if is_use_ref_type(obj_ty) || is_ref_value_type(obj_ty) {513                            ref_derived_values.insert(instr.lvalue.identifier);514                        }515                    }516                }517            }518519            match &instr.value {520                InstructionValue::LoadLocal { place, .. } => {521                    if set_state_functions.contains_key(&place.identifier) {522                        let info = set_state_functions[&place.identifier].clone();523                        set_state_functions.insert(instr.lvalue.identifier, info);524                    }525                }526                InstructionValue::StoreLocal { lvalue, value, .. } => {527                    if set_state_functions.contains_key(&value.identifier) {528                        let info = set_state_functions[&value.identifier].clone();529                        set_state_functions.insert(lvalue.place.identifier, info.clone());530                        set_state_functions.insert(instr.lvalue.identifier, info);531                    }532                }533                InstructionValue::CallExpression { callee, args, .. } => {534                    if is_set_state_type_by_id(callee.identifier, identifiers, types)535                        || set_state_functions.contains_key(&callee.identifier)536                    {537                        if enable_allow_set_state_from_refs {538                            // Check if the first argument is ref-derived539                            if let Some(first_arg) = args.first() {540                                if let PlaceOrSpread::Place(arg_place) = first_arg {541                                    if is_derived_from_ref(542                                        arg_place.identifier,543                                        &ref_derived_values,544                                        identifiers,545                                        types,546                                    ) {547                                        // Allow setState when value is derived from ref548                                        return Ok(None);549                                    }550                                }551                            }552                            // Check if the current block is controlled by a ref-derived condition553                            if is_ref_controlled_block(block.id) {554                                continue;555                            }556                        }557                        // Get the user-visible identifier name, matching Babel's558                        // loc.identifierName behavior. Uses declaration_id to find559                        // the original named identifier when SSA creates unnamed copies.560                        let callee_name = get_identifier_name_with_loc(561                            callee.identifier,562                            identifiers,563                            &callee.loc,564                            source_code,565                        );566                        return Ok(Some(SetStateInfo {567                            loc: callee.loc,568                            identifier_name: callee_name,569                        }));570                    }571                }572                _ => {}573            }574        }575    }576    Ok(None)577}

Code quality findings 20

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 info = set_state_functions[&place.identifier].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 info = set_state_functions[&value.identifier].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 inner_func = &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
&types[identifiers[property.identifier.0 as usize].type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&types[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 ident = &identifiers[identifier_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 ty = &types[ident.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 = &identifiers[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 ty = &types[ident.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 control_block = &func.body.blocks[frontier_block_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
let obj_ident = &identifiers[object.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 obj_ty = &types[obj_ident.type_.0 as usize];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let instr = &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 obj_ident = &identifiers[object.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 obj_ty = &types[obj_ident.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 info = set_state_functions[&place.identifier].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 info = set_state_functions[&value.identifier].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
let info = set_state_functions[&place.identifier].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.