compiler/crates/react_compiler_validation/src/validate_context_variable_lvalues.rs RUST 242 lines View on github.com → Search inside
1use rustc_hash::FxHashMap;23use react_compiler_diagnostics::{4    CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory,5};6use react_compiler_hir::environment::Environment;7use react_compiler_hir::visitors::{each_instruction_value_lvalue, each_pattern_operand};8use react_compiler_hir::{9    FunctionId, HirFunction, Identifier, IdentifierId, InstructionValue, Place,10};1112/// Variable reference kind: local, context, or destructure.13#[derive(Debug, Clone, Copy, PartialEq, Eq)]14enum VarRefKind {15    Local,16    Context,17    Destructure,18}1920impl std::fmt::Display for VarRefKind {21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {22        match self {23            VarRefKind::Local => write!(f, "local"),24            VarRefKind::Context => write!(f, "context"),25            VarRefKind::Destructure => write!(f, "destructure"),26        }27    }28}2930type IdentifierKinds = FxHashMap<IdentifierId, (Place, VarRefKind)>;3132/// Validates that context variable lvalues are used consistently.33///34/// Port of ValidateContextVariableLValues.ts35pub fn validate_context_variable_lvalues(36    func: &HirFunction,37    env: &mut Environment,38) -> Result<(), CompilerDiagnostic> {39    validate_context_variable_lvalues_with_errors(40        func,41        &env.functions,42        &env.identifiers,43        &mut env.errors,44    )45}4647/// Like [`validate_context_variable_lvalues`], but writes diagnostics into the48/// provided `errors` instead of `env.errors`. Useful when the caller wants to49/// discard the diagnostics (e.g. when lowering is incomplete).50pub fn validate_context_variable_lvalues_with_errors(51    func: &HirFunction,52    functions: &[HirFunction],53    identifiers: &[Identifier],54    errors: &mut CompilerError,55) -> Result<(), CompilerDiagnostic> {56    let mut identifier_kinds: IdentifierKinds = FxHashMap::default();57    validate_context_variable_lvalues_impl(58        func,59        &mut identifier_kinds,60        functions,61        identifiers,62        errors,63    )64}6566fn validate_context_variable_lvalues_impl(67    func: &HirFunction,68    identifier_kinds: &mut IdentifierKinds,69    functions: &[HirFunction],70    identifiers: &[Identifier],71    errors: &mut CompilerError,72) -> Result<(), CompilerDiagnostic> {73    let mut inner_function_ids: Vec<FunctionId> = Vec::new();7475    for (_block_id, block) in &func.body.blocks {76        for &instr_id in &block.instructions {77            let instr = &func.instructions[instr_id.0 as usize];78            let value = &instr.value;7980            match value {81                InstructionValue::DeclareContext { lvalue, .. }82                | InstructionValue::StoreContext { lvalue, .. } => {83                    visit(84                        identifier_kinds,85                        &lvalue.place,86                        VarRefKind::Context,87                        identifiers,88                        errors,89                    )?;90                }91                InstructionValue::LoadContext { place, .. } => {92                    visit(93                        identifier_kinds,94                        place,95                        VarRefKind::Context,96                        identifiers,97                        errors,98                    )?;99                }100                InstructionValue::StoreLocal { lvalue, .. }101                | InstructionValue::DeclareLocal { lvalue, .. } => {102                    visit(103                        identifier_kinds,104                        &lvalue.place,105                        VarRefKind::Local,106                        identifiers,107                        errors,108                    )?;109                }110                InstructionValue::LoadLocal { place, .. } => {111                    visit(112                        identifier_kinds,113                        place,114                        VarRefKind::Local,115                        identifiers,116                        errors,117                    )?;118                }119                InstructionValue::PostfixUpdate { lvalue, .. }120                | InstructionValue::PrefixUpdate { lvalue, .. } => {121                    visit(122                        identifier_kinds,123                        lvalue,124                        VarRefKind::Local,125                        identifiers,126                        errors,127                    )?;128                }129                InstructionValue::Destructure { lvalue, .. } => {130                    for place in each_pattern_operand(&lvalue.pattern) {131                        visit(132                            identifier_kinds,133                            &place,134                            VarRefKind::Destructure,135                            identifiers,136                            errors,137                        )?;138                    }139                }140                InstructionValue::FunctionExpression { lowered_func, .. }141                | InstructionValue::ObjectMethod { lowered_func, .. } => {142                    inner_function_ids.push(lowered_func.func);143                }144                _ => {145                    for _ in each_instruction_value_lvalue(value) {146                        errors.push_diagnostic(147                            CompilerDiagnostic::new(148                                ErrorCategory::Todo,149                                "ValidateContextVariableLValues: unhandled instruction variant",150                                None,151                            )152                            .with_detail(153                                CompilerDiagnosticDetail::Error {154                                    loc: value.loc().copied(),155                                    message: None,156                                    identifier_name: None,157                                },158                            ),159                        );160                    }161                }162            }163        }164    }165166    // Process inner functions after the block loop to avoid borrow conflicts167    for func_id in inner_function_ids {168        let inner_func = &functions[func_id.0 as usize];169        validate_context_variable_lvalues_impl(170            inner_func,171            identifier_kinds,172            functions,173            identifiers,174            errors,175        )?;176    }177178    Ok(())179}180181/// Format a place like TS `printPlace()`: `<effect> <name>$<id>`182fn format_place(place: &Place, identifiers: &[Identifier]) -> String {183    let id = place.identifier;184    let ident = &identifiers[id.0 as usize];185    let name = match &ident.name {186        Some(n) => n.value().to_string(),187        None => String::new(),188    };189    format!("{} {}${}", place.effect, name, id.0)190}191192fn visit(193    identifiers: &mut IdentifierKinds,194    place: &Place,195    kind: VarRefKind,196    env_identifiers: &[Identifier],197    errors: &mut CompilerError,198) -> Result<(), CompilerDiagnostic> {199    if let Some((prev_place, prev_kind)) = identifiers.get(&place.identifier) {200        let was_context = *prev_kind == VarRefKind::Context;201        let is_context = kind == VarRefKind::Context;202        if was_context != is_context {203            if *prev_kind == VarRefKind::Destructure || kind == VarRefKind::Destructure {204                let loc = if kind == VarRefKind::Destructure {205                    place.loc206                } else {207                    prev_place.loc208                };209                errors.push_diagnostic(210                    CompilerDiagnostic::new(211                        ErrorCategory::Todo,212                        "Support destructuring of context variables",213                        None,214                    )215                    .with_detail(CompilerDiagnosticDetail::Error {216                        loc,217                        message: None,218                        identifier_name: None,219                    }),220                );221                return Ok(());222            }223            let place_str = format_place(place, env_identifiers);224            return Err(CompilerDiagnostic::new(225                ErrorCategory::Invariant,226                "Expected all references to a variable to be consistently local or context references",227                Some(format!(228                    "Identifier {} is referenced as a {} variable, but was previously referenced as a {} variable",229                    place_str, kind, prev_kind230                )),231            )232            .with_detail(CompilerDiagnosticDetail::Error {233                loc: place.loc,234                message: Some(format!("this is {}", prev_kind)),235                identifier_name: None,236            }));237        }238    }239    identifiers.insert(place.identifier, (place.clone(), kind));240    Ok(())241}

Code quality findings 5

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
/// Like [`validate_context_variable_lvalues`], but writes diagnostics into the
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 = &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 ident = &identifiers[id.0 as usize];
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
inner_function_ids.push(lowered_func.func);

Get this view in your editor

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