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();