compiler/crates/react_compiler_lowering/src/build_hir.rs RUST 7,383 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 7,383.
1use rustc_hash::{FxBuildHasher, FxHashSet};23use indexmap::{IndexMap, IndexSet};4use react_compiler_ast::scope::BindingId;5use react_compiler_ast::scope::BindingKind as AstBindingKind;6use react_compiler_ast::scope::ScopeId;7use react_compiler_ast::scope::ScopeInfo;8use react_compiler_ast::scope::ScopeKind;9use react_compiler_diagnostics::CompilerDiagnostic;10use react_compiler_diagnostics::CompilerDiagnosticDetail;11use react_compiler_diagnostics::CompilerError;12use react_compiler_diagnostics::CompilerErrorDetail;13use react_compiler_diagnostics::ErrorCategory;14use react_compiler_hir::environment::Environment;15use react_compiler_hir::*;1617use crate::FunctionNode;18use crate::find_context_identifiers::find_context_identifiers;19use crate::hir_builder::HirBuilder;20use crate::hir_builder::is_always_reserved_word;21use crate::hir_builder::reserved_identifier_diagnostic;22use crate::identifier_loc_index::IdentifierLocIndex;23use crate::identifier_loc_index::build_identifier_loc_index;2425// =============================================================================26// Source location conversion27// =============================================================================2829/// Convert an AST SourceLocation to an HIR SourceLocation.30fn convert_loc(loc: &react_compiler_ast::common::SourceLocation) -> SourceLocation {31    SourceLocation {32        start: Position {33            line: loc.start.line,34            column: loc.start.column,35            index: loc.start.index,36        },37        end: Position {38            line: loc.end.line,39            column: loc.end.column,40            index: loc.end.index,41        },42    }43}4445/// Convert an optional AST SourceLocation to an optional HIR SourceLocation.46fn convert_opt_loc(47    loc: &Option<react_compiler_ast::common::SourceLocation>,48) -> Option<SourceLocation> {49    loc.as_ref().map(convert_loc)50}5152/// Serialize an expression to a serde_json::Value for UnsupportedNode's original_node.53/// Returns None if serialization fails (should not happen for valid AST nodes).54/// This should ONLY be called on error/bail paths — never eagerly before deciding55/// to create an UnsupportedNode.56fn serialize_expression(57    expr: &react_compiler_ast::expressions::Expression,58) -> Option<serde_json::Value> {59    serde_json::to_value(expr).ok()60}6162/// Serialize a statement to a serde_json::Value for UnsupportedNode's original_node.63fn serialize_statement(64    stmt: &react_compiler_ast::statements::Statement,65) -> Option<serde_json::Value> {66    serde_json::to_value(stmt).ok()67}6869/// Serialize a pattern to a serde_json::Value for UnsupportedNode's original_node.70fn serialize_pattern(pat: &react_compiler_ast::patterns::PatternLike) -> Option<serde_json::Value> {71    serde_json::to_value(pat).ok()72}7374fn pattern_like_loc(75    pattern: &react_compiler_ast::patterns::PatternLike,76) -> Option<react_compiler_ast::common::SourceLocation> {77    use react_compiler_ast::patterns::PatternLike;78    match pattern {79        PatternLike::Identifier(id) => id.base.loc.clone(),80        PatternLike::ObjectPattern(p) => p.base.loc.clone(),81        PatternLike::ArrayPattern(p) => p.base.loc.clone(),82        PatternLike::AssignmentPattern(p) => p.base.loc.clone(),83        PatternLike::RestElement(p) => p.base.loc.clone(),84        PatternLike::MemberExpression(p) => p.base.loc.clone(),85        PatternLike::TSAsExpression(p) => p.base.loc.clone(),86        PatternLike::TSSatisfiesExpression(p) => p.base.loc.clone(),87        PatternLike::TSNonNullExpression(p) => p.base.loc.clone(),88        PatternLike::TSTypeAssertion(p) => p.base.loc.clone(),89        PatternLike::TypeCastExpression(p) => p.base.loc.clone(),90    }91}9293/// Extract the HIR SourceLocation from an Expression AST node.94fn expression_loc(expr: &react_compiler_ast::expressions::Expression) -> Option<SourceLocation> {95    use react_compiler_ast::expressions::Expression;96    let loc = match expr {97        Expression::Identifier(e) => e.base.loc.clone(),98        Expression::StringLiteral(e) => e.base.loc.clone(),99        Expression::NumericLiteral(e) => e.base.loc.clone(),100        Expression::BooleanLiteral(e) => e.base.loc.clone(),101        Expression::NullLiteral(e) => e.base.loc.clone(),102        Expression::BigIntLiteral(e) => e.base.loc.clone(),103        Expression::RegExpLiteral(e) => e.base.loc.clone(),104        Expression::CallExpression(e) => e.base.loc.clone(),105        Expression::MemberExpression(e) => e.base.loc.clone(),106        Expression::OptionalCallExpression(e) => e.base.loc.clone(),107        Expression::OptionalMemberExpression(e) => e.base.loc.clone(),108        Expression::BinaryExpression(e) => e.base.loc.clone(),109        Expression::LogicalExpression(e) => e.base.loc.clone(),110        Expression::UnaryExpression(e) => e.base.loc.clone(),111        Expression::UpdateExpression(e) => e.base.loc.clone(),112        Expression::ConditionalExpression(e) => e.base.loc.clone(),113        Expression::AssignmentExpression(e) => e.base.loc.clone(),114        Expression::SequenceExpression(e) => e.base.loc.clone(),115        Expression::ArrowFunctionExpression(e) => e.base.loc.clone(),116        Expression::FunctionExpression(e) => e.base.loc.clone(),117        Expression::ObjectExpression(e) => e.base.loc.clone(),118        Expression::ArrayExpression(e) => e.base.loc.clone(),119        Expression::NewExpression(e) => e.base.loc.clone(),120        Expression::TemplateLiteral(e) => e.base.loc.clone(),121        Expression::TaggedTemplateExpression(e) => e.base.loc.clone(),122        Expression::AwaitExpression(e) => e.base.loc.clone(),123        Expression::YieldExpression(e) => e.base.loc.clone(),124        Expression::SpreadElement(e) => e.base.loc.clone(),125        Expression::MetaProperty(e) => e.base.loc.clone(),126        Expression::ClassExpression(e) => e.base.loc.clone(),127        Expression::PrivateName(e) => e.base.loc.clone(),128        Expression::Super(e) => e.base.loc.clone(),129        Expression::Import(e) => e.base.loc.clone(),130        Expression::ThisExpression(e) => e.base.loc.clone(),131        Expression::ParenthesizedExpression(e) => e.base.loc.clone(),132        Expression::JSXElement(e) => e.base.loc.clone(),133        Expression::JSXFragment(e) => e.base.loc.clone(),134        Expression::AssignmentPattern(e) => e.base.loc.clone(),135        Expression::TSAsExpression(e) => e.base.loc.clone(),136        Expression::TSSatisfiesExpression(e) => e.base.loc.clone(),137        Expression::TSNonNullExpression(e) => e.base.loc.clone(),138        Expression::TSTypeAssertion(e) => e.base.loc.clone(),139        Expression::TSInstantiationExpression(e) => e.base.loc.clone(),140        Expression::TypeCastExpression(e) => e.base.loc.clone(),141    };142    convert_opt_loc(&loc)143}144145fn validate_ts_this_parameter(146    scope_info: &ScopeInfo,147    function_scope: ScopeId,148) -> Result<(), CompilerError> {149    let Some(scope) = scope_info.scopes.get(function_scope.0 as usize) else {150        return Ok(());151    };152    let Some(binding_id) = scope.bindings.get("this") else {153        return Ok(());154    };155    let Some(binding) = scope_info.bindings.get(binding_id.0 as usize) else {156        return Ok(());157    };158    if matches!(binding.kind, AstBindingKind::Param) {159        return Err(CompilerError::from(reserved_identifier_diagnostic("this")));160    }161    Ok(())162}163164fn is_class_scope_descendant(scope_info: &ScopeInfo, mut scope_id: ScopeId) -> bool {165    while let Some(scope) = scope_info.scopes.get(scope_id.0 as usize) {166        let Some(parent) = scope.parent else {167            return false;168        };169        let Some(parent_scope) = scope_info.scopes.get(parent.0 as usize) else {170            return false;171        };172        if matches!(parent_scope.kind, ScopeKind::Class) {173            return true;174        }175        scope_id = parent;176    }177    false178}179180fn validate_ts_this_parameters_in_function_range(181    scope_info: &ScopeInfo,182    start: u32,183    end: u32,184) -> Result<(), CompilerError> {185    if start >= end {186        return Ok(());187    }188    for (node_start, scope_id) in &scope_info.node_to_scope {189        if *node_start < start || *node_start >= end {190            continue;191        }192        let Some(scope) = scope_info.scopes.get(scope_id.0 as usize) else {193            continue;194        };195        if !matches!(scope.kind, ScopeKind::Function)196            || is_class_scope_descendant(scope_info, *scope_id)197        {198            continue;199        }200        validate_ts_this_parameter(scope_info, *scope_id)?;201    }202    Ok(())203}204205/// Get the Babel-style type name of an Expression node (e.g. "Identifier", "NumericLiteral").206fn expression_type_name(expr: &react_compiler_ast::expressions::Expression) -> &'static str {207    use react_compiler_ast::expressions::Expression;208    match expr {209        Expression::Identifier(_) => "Identifier",210        Expression::StringLiteral(_) => "StringLiteral",211        Expression::NumericLiteral(_) => "NumericLiteral",212        Expression::BooleanLiteral(_) => "BooleanLiteral",213        Expression::NullLiteral(_) => "NullLiteral",214        Expression::BigIntLiteral(_) => "BigIntLiteral",215        Expression::RegExpLiteral(_) => "RegExpLiteral",216        Expression::CallExpression(_) => "CallExpression",217        Expression::MemberExpression(_) => "MemberExpression",218        Expression::OptionalCallExpression(_) => "OptionalCallExpression",219        Expression::OptionalMemberExpression(_) => "OptionalMemberExpression",220        Expression::BinaryExpression(_) => "BinaryExpression",221        Expression::LogicalExpression(_) => "LogicalExpression",222        Expression::UnaryExpression(_) => "UnaryExpression",223        Expression::UpdateExpression(_) => "UpdateExpression",224        Expression::ConditionalExpression(_) => "ConditionalExpression",225        Expression::AssignmentExpression(_) => "AssignmentExpression",226        Expression::SequenceExpression(_) => "SequenceExpression",227        Expression::ArrowFunctionExpression(_) => "ArrowFunctionExpression",228        Expression::FunctionExpression(_) => "FunctionExpression",229        Expression::ObjectExpression(_) => "ObjectExpression",230        Expression::ArrayExpression(_) => "ArrayExpression",231        Expression::NewExpression(_) => "NewExpression",232        Expression::TemplateLiteral(_) => "TemplateLiteral",233        Expression::TaggedTemplateExpression(_) => "TaggedTemplateExpression",234        Expression::AwaitExpression(_) => "AwaitExpression",235        Expression::YieldExpression(_) => "YieldExpression",236        Expression::SpreadElement(_) => "SpreadElement",237        Expression::MetaProperty(_) => "MetaProperty",238        Expression::ClassExpression(_) => "ClassExpression",239        Expression::PrivateName(_) => "PrivateName",240        Expression::Super(_) => "Super",241        Expression::Import(_) => "Import",242        Expression::ThisExpression(_) => "ThisExpression",243        Expression::ParenthesizedExpression(_) => "ParenthesizedExpression",244        Expression::JSXElement(_) => "JSXElement",245        Expression::JSXFragment(_) => "JSXFragment",246        Expression::AssignmentPattern(_) => "AssignmentPattern",247        Expression::TSAsExpression(_) => "TSAsExpression",248        Expression::TSSatisfiesExpression(_) => "TSSatisfiesExpression",249        Expression::TSNonNullExpression(_) => "TSNonNullExpression",250        Expression::TSTypeAssertion(_) => "TSTypeAssertion",251        Expression::TSInstantiationExpression(_) => "TSInstantiationExpression",252        Expression::TypeCastExpression(_) => "TypeCastExpression",253    }254}255256/// Extract the type annotation name from an identifier's typeAnnotation field.257/// The Babel AST stores type annotations as:258/// { "type": "TSTypeAnnotation", "typeAnnotation": { "type": "TSTypeReference", ... } }259/// or { "type": "TypeAnnotation", "typeAnnotation": { "type": "GenericTypeAnnotation", ... } }260/// We extract the inner typeAnnotation's `type` field name.261fn extract_type_annotation_name(262    type_annotation: &Option<react_compiler_ast::common::RawNode>,263) -> Option<String> {264    let val = type_annotation.as_ref()?.parse_value();265    // Navigate: typeAnnotation.typeAnnotation.type266    let inner = val.get("typeAnnotation")?;267    let type_name = inner.get("type")?.as_str()?;268    Some(type_name.to_string())269}270271// =============================================================================272// Helper functions273// =============================================================================274275fn build_temporary_place(builder: &mut HirBuilder, loc: Option<SourceLocation>) -> Place {276    let id = builder.make_temporary(loc.clone());277    Place {278        identifier: id,279        reactive: false,280        effect: Effect::Unknown,281        loc,282    }283}284285/// Promote a temporary identifier to a named identifier (for destructuring).286/// Corresponds to TS `promoteTemporary(identifier)`.287fn promote_temporary(builder: &mut HirBuilder, identifier_id: IdentifierId) {288    let env = builder.environment_mut();289    let decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;290    env.identifiers[identifier_id.0 as usize].name =291        Some(IdentifierName::Promoted(format!("#t{}", decl_id.0)));292}293294fn lower_value_to_temporary(295    builder: &mut HirBuilder,296    value: InstructionValue,297) -> Result<Place, CompilerError> {298    // Optimization: if loading an unnamed temporary, skip creating a new instruction299    if let InstructionValue::LoadLocal { ref place, .. } = value {300        let ident = &builder.environment().identifiers[place.identifier.0 as usize];301        if ident.name.is_none() {302            return Ok(place.clone());303        }304    }305    let loc = value.loc().cloned();306    let place = build_temporary_place(builder, loc.clone());307    builder.push(Instruction {308        id: EvaluationOrder(0),309        lvalue: place.clone(),310        value,311        loc,312        effects: None,313    });314    Ok(place)315}316317fn lower_expression_to_temporary(318    builder: &mut HirBuilder,319    expr: &react_compiler_ast::expressions::Expression,320) -> Result<Place, CompilerError> {321    let value = lower_expression(builder, expr)?;322    Ok(lower_value_to_temporary(builder, value)?)323}324325// =============================================================================326// Operator conversion327// =============================================================================328329fn convert_binary_operator(op: &react_compiler_ast::operators::BinaryOperator) -> BinaryOperator {330    use react_compiler_ast::operators::BinaryOperator as AstOp;331    match op {332        AstOp::Add => BinaryOperator::Add,333        AstOp::Sub => BinaryOperator::Subtract,334        AstOp::Mul => BinaryOperator::Multiply,335        AstOp::Div => BinaryOperator::Divide,336        AstOp::Rem => BinaryOperator::Modulo,337        AstOp::Exp => BinaryOperator::Exponent,338        AstOp::Eq => BinaryOperator::Equal,339        AstOp::StrictEq => BinaryOperator::StrictEqual,340        AstOp::Neq => BinaryOperator::NotEqual,341        AstOp::StrictNeq => BinaryOperator::StrictNotEqual,342        AstOp::Lt => BinaryOperator::LessThan,343        AstOp::Lte => BinaryOperator::LessEqual,344        AstOp::Gt => BinaryOperator::GreaterThan,345        AstOp::Gte => BinaryOperator::GreaterEqual,346        AstOp::Shl => BinaryOperator::ShiftLeft,347        AstOp::Shr => BinaryOperator::ShiftRight,348        AstOp::UShr => BinaryOperator::UnsignedShiftRight,349        AstOp::BitOr => BinaryOperator::BitwiseOr,350        AstOp::BitXor => BinaryOperator::BitwiseXor,351        AstOp::BitAnd => BinaryOperator::BitwiseAnd,352        AstOp::In => BinaryOperator::In,353        AstOp::Instanceof => BinaryOperator::InstanceOf,354        AstOp::Pipeline => {355            unreachable!("Pipeline operator is checked before calling convert_binary_operator")356        }357    }358}359360fn convert_unary_operator(op: &react_compiler_ast::operators::UnaryOperator) -> UnaryOperator {361    use react_compiler_ast::operators::UnaryOperator as AstOp;362    match op {363        AstOp::Neg => UnaryOperator::Minus,364        AstOp::Plus => UnaryOperator::Plus,365        AstOp::Not => UnaryOperator::Not,366        AstOp::BitNot => UnaryOperator::BitwiseNot,367        AstOp::TypeOf => UnaryOperator::TypeOf,368        AstOp::Void => UnaryOperator::Void,369        AstOp::Delete | AstOp::Throw => unreachable!("delete/throw handled separately"),370    }371}372373// =============================================================================374// lower_identifier375// =============================================================================376377/// Resolve an identifier to a Place.378///379/// For local/context identifiers, returns a Place referencing the binding's identifier.380/// For globals/imports, emits a LoadGlobal instruction and returns the temporary Place.381fn lower_identifier(382    builder: &mut HirBuilder,383    name: &str,384    start: u32,385    loc: Option<SourceLocation>,386    node_id: Option<u32>,387) -> Result<Place, CompilerError> {388    let binding = builder.resolve_identifier(name, start, loc.clone(), node_id)?;389    match binding {390        VariableBinding::Identifier { identifier, .. } => Ok(Place {391            identifier,392            effect: Effect::Unknown,393            reactive: false,394            loc,395        }),396        _ => {397            if let VariableBinding::Global { ref name } = binding {398                if name == "eval" {399                    builder.record_error(CompilerErrorDetail {400                        category: ErrorCategory::UnsupportedSyntax,401                        reason: "The 'eval' function is not supported".to_string(),402                        description: Some(403                            "Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler".to_string(),404                        ),405                        loc: loc.clone(),406                        suggestions: None,407                    })?;408                }409            }410            let non_local_binding = match binding {411                VariableBinding::Global { name } => NonLocalBinding::Global { name },412                VariableBinding::ImportDefault { name, module } => {413                    NonLocalBinding::ImportDefault { name, module }414                }415                VariableBinding::ImportSpecifier {416                    name,417                    module,418                    imported,419                } => NonLocalBinding::ImportSpecifier {420                    name,421                    module,422                    imported,423                },424                VariableBinding::ImportNamespace { name, module } => {425                    NonLocalBinding::ImportNamespace { name, module }426                }427                VariableBinding::ModuleLocal { name } => NonLocalBinding::ModuleLocal { name },428                VariableBinding::Identifier { .. } => unreachable!(),429            };430            let instr_value = InstructionValue::LoadGlobal {431                binding: non_local_binding,432                loc: loc.clone(),433            };434            Ok(lower_value_to_temporary(builder, instr_value)?)435        }436    }437}438439// =============================================================================440// lower_arguments441// =============================================================================442443fn lower_arguments(444    builder: &mut HirBuilder,445    args: &[react_compiler_ast::expressions::Expression],446) -> Result<Vec<PlaceOrSpread>, CompilerError> {447    use react_compiler_ast::expressions::Expression;448    let mut result = Vec::new();449    for arg in args {450        match arg {451            Expression::SpreadElement(spread) => {452                let place = lower_expression_to_temporary(builder, &spread.argument)?;453                result.push(PlaceOrSpread::Spread(SpreadPattern { place }));454            }455            _ => {456                let place = lower_expression_to_temporary(builder, arg)?;457                result.push(PlaceOrSpread::Place(place));458            }459        }460    }461    Ok(result)462}463464fn convert_update_operator(op: &react_compiler_ast::operators::UpdateOperator) -> UpdateOperator {465    match op {466        react_compiler_ast::operators::UpdateOperator::Increment => UpdateOperator::Increment,467        react_compiler_ast::operators::UpdateOperator::Decrement => UpdateOperator::Decrement,468    }469}470471// =============================================================================472// lower_member_expression473// =============================================================================474475enum MemberProperty {476    Literal(PropertyLiteral),477    Computed(Place),478}479480struct LoweredMemberExpression {481    object: Place,482    property: MemberProperty,483    value: InstructionValue,484}485486fn lower_member_expression(487    builder: &mut HirBuilder,488    member: &react_compiler_ast::expressions::MemberExpression,489) -> Result<LoweredMemberExpression, CompilerError> {490    Ok(lower_member_expression_impl(builder, member, None)?)491}492493fn lower_member_expression_with_object(494    builder: &mut HirBuilder,495    member: &react_compiler_ast::expressions::OptionalMemberExpression,496    lowered_object: Place,497) -> Result<LoweredMemberExpression, CompilerError> {498    // OptionalMemberExpression has the same shape as MemberExpression for property access499    use react_compiler_ast::expressions::Expression;500    let loc = convert_opt_loc(&member.base.loc);501    let object = lowered_object;502503    if !member.computed {504        let prop_literal = match member.property.as_ref() {505            Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),506            Expression::NumericLiteral(lit) => {507                PropertyLiteral::Number(FloatValue::new(lit.precise_value()))508            }509            _ => {510                builder.record_error(CompilerErrorDetail {511                    category: ErrorCategory::Todo,512                    reason: format!(513                        "(BuildHIR::lowerMemberExpression) Handle {:?} property",514                        member.property515                    ),516                    description: None,517                    loc: loc.clone(),518                    suggestions: None,519                })?;520                return Ok(LoweredMemberExpression {521                    object,522                    property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),523                    value: InstructionValue::UnsupportedNode {524                        node_type: Some("OptionalMemberExpression".to_string()),525                        original_node: serialize_expression(526                            &react_compiler_ast::expressions::Expression::OptionalMemberExpression(527                                member.clone(),528                            ),529                        ),530                        loc,531                    },532                });533            }534        };535        let value = InstructionValue::PropertyLoad {536            object: object.clone(),537            property: prop_literal.clone(),538            loc,539        };540        Ok(LoweredMemberExpression {541            object,542            property: MemberProperty::Literal(prop_literal),543            value,544        })545    } else {546        if let Expression::NumericLiteral(lit) = member.property.as_ref() {547            let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));548            let value = InstructionValue::PropertyLoad {549                object: object.clone(),550                property: prop_literal.clone(),551                loc,552            };553            return Ok(LoweredMemberExpression {554                object,555                property: MemberProperty::Literal(prop_literal),556                value,557            });558        }559        let property = lower_expression_to_temporary(builder, &member.property)?;560        let value = InstructionValue::ComputedLoad {561            object: object.clone(),562            property: property.clone(),563            loc,564        };565        Ok(LoweredMemberExpression {566            object,567            property: MemberProperty::Computed(property),568            value,569        })570    }571}572573fn lower_member_expression_impl(574    builder: &mut HirBuilder,575    member: &react_compiler_ast::expressions::MemberExpression,576    lowered_object: Option<Place>,577) -> Result<LoweredMemberExpression, CompilerError> {578    use react_compiler_ast::expressions::Expression;579    let loc = convert_opt_loc(&member.base.loc);580    let object = match lowered_object {581        Some(obj) => obj,582        None => lower_expression_to_temporary(builder, &member.object)?,583    };584585    if !member.computed {586        // Non-computed: property must be an identifier or numeric literal587        let prop_literal = match member.property.as_ref() {588            Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),589            Expression::NumericLiteral(lit) => {590                PropertyLiteral::Number(FloatValue::new(lit.precise_value()))591            }592            _ => {593                builder.record_error(CompilerErrorDetail {594                    category: ErrorCategory::Todo,595                    reason: format!(596                        "(BuildHIR::lowerMemberExpression) Handle {:?} property",597                        member.property598                    ),599                    description: None,600                    loc: loc.clone(),601                    suggestions: None,602                })?;603                return Ok(LoweredMemberExpression {604                    object,605                    property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),606                    value: InstructionValue::UnsupportedNode {607                        node_type: Some("MemberExpression".to_string()),608                        original_node: serialize_expression(609                            &react_compiler_ast::expressions::Expression::MemberExpression(610                                member.clone(),611                            ),612                        ),613                        loc,614                    },615                });616            }617        };618        let value = InstructionValue::PropertyLoad {619            object: object.clone(),620            property: prop_literal.clone(),621            loc,622        };623        Ok(LoweredMemberExpression {624            object,625            property: MemberProperty::Literal(prop_literal),626            value,627        })628    } else {629        // Computed: check for numeric literal first (treated as PropertyLoad in TS)630        if let Expression::NumericLiteral(lit) = member.property.as_ref() {631            let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));632            let value = InstructionValue::PropertyLoad {633                object: object.clone(),634                property: prop_literal.clone(),635                loc,636            };637            return Ok(LoweredMemberExpression {638                object,639                property: MemberProperty::Literal(prop_literal),640                value,641            });642        }643        // Otherwise lower property to temporary for ComputedLoad644        let property = lower_expression_to_temporary(builder, &member.property)?;645        let value = InstructionValue::ComputedLoad {646            object: object.clone(),647            property: property.clone(),648            loc,649        };650        Ok(LoweredMemberExpression {651            object,652            property: MemberProperty::Computed(property),653            value,654        })655    }656}657658// =============================================================================659// lower_expression660// =============================================================================661662fn lower_expression(663    builder: &mut HirBuilder,664    expr: &react_compiler_ast::expressions::Expression,665) -> Result<InstructionValue, CompilerError> {666    use react_compiler_ast::expressions::Expression;667668    match expr {669        Expression::Identifier(ident) => {670            let loc = convert_opt_loc(&ident.base.loc);671            let start = ident.base.start.unwrap_or(0);672            let place =673                lower_identifier(builder, &ident.name, start, loc.clone(), ident.base.node_id)?;674            // Determine LoadLocal vs LoadContext based on context identifier check675            if builder.is_context_identifier(&ident.name, start, ident.base.node_id) {676                Ok(InstructionValue::LoadContext { place, loc })677            } else {678                Ok(InstructionValue::LoadLocal { place, loc })679            }680        }681        Expression::NullLiteral(lit) => {682            let loc = convert_opt_loc(&lit.base.loc);683            Ok(InstructionValue::Primitive {684                value: PrimitiveValue::Null,685                loc,686            })687        }688        Expression::BooleanLiteral(lit) => {689            let loc = convert_opt_loc(&lit.base.loc);690            Ok(InstructionValue::Primitive {691                value: PrimitiveValue::Boolean(lit.value),692                loc,693            })694        }695        Expression::NumericLiteral(lit) => {696            let loc = convert_opt_loc(&lit.base.loc);697            Ok(InstructionValue::Primitive {698                value: PrimitiveValue::Number(FloatValue::new(lit.precise_value())),699                loc,700            })701        }702        Expression::StringLiteral(lit) => {703            let loc = convert_opt_loc(&lit.base.loc);704            Ok(InstructionValue::Primitive {705                value: PrimitiveValue::String(lit.value.clone()),706                loc,707            })708        }709        Expression::BinaryExpression(bin) => {710            let loc = convert_opt_loc(&bin.base.loc);711            // Check for pipeline operator before lowering operands712            if matches!(713                bin.operator,714                react_compiler_ast::operators::BinaryOperator::Pipeline715            ) {716                builder.record_error(CompilerErrorDetail {717                    category: ErrorCategory::Todo,718                    reason: "(BuildHIR::lowerExpression) Pipe operator not supported".to_string(),719                    description: None,720                    loc: loc.clone(),721                    suggestions: None,722                })?;723                return Ok(InstructionValue::UnsupportedNode {724                    node_type: Some("BinaryExpression".to_string()),725                    original_node: serialize_expression(expr),726                    loc,727                });728            }729            let left = lower_expression_to_temporary(builder, &bin.left)?;730            let right = lower_expression_to_temporary(builder, &bin.right)?;731            let operator = convert_binary_operator(&bin.operator);732            Ok(InstructionValue::BinaryExpression {733                operator,734                left,735                right,736                loc,737            })738        }739        Expression::UnaryExpression(unary) => {740            let loc = convert_opt_loc(&unary.base.loc);741            match &unary.operator {742                react_compiler_ast::operators::UnaryOperator::Delete => {743                    // Delete can be on member expressions or identifiers744                    let loc = convert_opt_loc(&unary.base.loc);745                    match &*unary.argument {746                        Expression::MemberExpression(member) => {747                            let object = lower_expression_to_temporary(builder, &member.object)?;748                            if !member.computed {749                                match &*member.property {750                                    Expression::Identifier(prop_id) => {751                                        Ok(InstructionValue::PropertyDelete {752                                            object,753                                            property: PropertyLiteral::String(prop_id.name.clone()),754                                            loc,755                                        })756                                    }757                                    _ => {758                                        builder.record_error(CompilerErrorDetail {759                                            reason: "Unsupported delete target".to_string(),760                                            category: ErrorCategory::Todo,761                                            loc: loc.clone(),762                                            description: None,763                                            suggestions: None,764                                        })?;765                                        Ok(InstructionValue::UnsupportedNode {766                                            node_type: Some("UnaryExpression".to_string()),767                                            original_node: serialize_expression(expr),768                                            loc,769                                        })770                                    }771                                }772                            } else {773                                let property =774                                    lower_expression_to_temporary(builder, &member.property)?;775                                Ok(InstructionValue::ComputedDelete {776                                    object,777                                    property,778                                    loc,779                                })780                            }781                        }782                        _ => {783                            // delete on non-member expression (e.g., optional chain, identifier)784                            builder.record_error(CompilerErrorDetail {785                                reason: "Only object properties can be deleted".to_string(),786                                category: ErrorCategory::Syntax,787                                loc: loc.clone(),788                                description: None,789                                suggestions: None,790                            })?;791                            Ok(InstructionValue::UnsupportedNode {792                                node_type: Some("UnaryExpression".to_string()),793                                original_node: serialize_expression(expr),794                                loc,795                            })796                        }797                    }798                }799                react_compiler_ast::operators::UnaryOperator::Throw => {800                    // throw as unary operator (Babel-specific)801                    let loc = convert_opt_loc(&unary.base.loc);802                    builder.record_error(CompilerErrorDetail {803                        reason: "throw expressions are not supported".to_string(),804                        category: ErrorCategory::Todo,805                        loc: loc.clone(),806                        description: None,807                        suggestions: None,808                    })?;809                    Ok(InstructionValue::UnsupportedNode {810                        node_type: Some("UnaryExpression".to_string()),811                        original_node: serialize_expression(expr),812                        loc,813                    })814                }815                op => {816                    let value = lower_expression_to_temporary(builder, &unary.argument)?;817                    let operator = convert_unary_operator(op);818                    Ok(InstructionValue::UnaryExpression {819                        operator,820                        value,821                        loc,822                    })823                }824            }825        }826        Expression::CallExpression(call) => {827            let loc = convert_opt_loc(&call.base.loc);828            // Check if callee is a MemberExpression => MethodCall829            if let Expression::MemberExpression(member) = call.callee.as_ref() {830                let lowered = lower_member_expression(builder, member)?;831                let property = lower_value_to_temporary(builder, lowered.value)?;832                let args = lower_arguments(builder, &call.arguments)?;833                Ok(InstructionValue::MethodCall {834                    receiver: lowered.object,835                    property,836                    args,837                    loc,838                })839            } else {840                let callee = lower_expression_to_temporary(builder, &call.callee)?;841                let args = lower_arguments(builder, &call.arguments)?;842                Ok(InstructionValue::CallExpression { callee, args, loc })843            }844        }845        Expression::MemberExpression(member) => {846            let lowered = lower_member_expression(builder, member)?;847            Ok(lowered.value)848        }849        Expression::OptionalCallExpression(opt_call) => {850            Ok(lower_optional_call_expression(builder, opt_call)?)851        }852        Expression::OptionalMemberExpression(opt_member) => {853            Ok(lower_optional_member_expression(builder, opt_member)?)854        }855        Expression::LogicalExpression(expr) => {856            let loc = convert_opt_loc(&expr.base.loc);857            let continuation_block = builder.reserve(builder.current_block_kind());858            let continuation_id = continuation_block.id;859            let test_block = builder.reserve(BlockKind::Value);860            let test_block_id = test_block.id;861            let place = build_temporary_place(builder, loc.clone());862            let left_loc = expression_loc(&expr.left);863            let left_place = build_temporary_place(builder, left_loc);864865            // Block for short-circuit case: store left value as result, goto continuation866            let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {867                lower_value_to_temporary(868                    builder,869                    InstructionValue::StoreLocal {870                        lvalue: LValue {871                            kind: InstructionKind::Const,872                            place: place.clone(),873                        },874                        value: left_place.clone(),875                        type_annotation: None,876                        loc: left_place.loc.clone(),877                    },878                )?;879                Ok(Terminal::Goto {880                    block: continuation_id,881                    variant: GotoVariant::Break,882                    id: EvaluationOrder(0),883                    loc: left_place.loc.clone(),884                })885            });886887            // Block for evaluating right side888            let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {889                let right = lower_expression_to_temporary(builder, &expr.right)?;890                let right_loc = right.loc.clone();891                lower_value_to_temporary(892                    builder,893                    InstructionValue::StoreLocal {894                        lvalue: LValue {895                            kind: InstructionKind::Const,896                            place: place.clone(),897                        },898                        value: right,899                        type_annotation: None,900                        loc: right_loc.clone(),901                    },902                )?;903                Ok(Terminal::Goto {904                    block: continuation_id,905                    variant: GotoVariant::Break,906                    id: EvaluationOrder(0),907                    loc: right_loc,908                })909            });910911            let hir_op = match expr.operator {912                react_compiler_ast::operators::LogicalOperator::And => LogicalOperator::And,913                react_compiler_ast::operators::LogicalOperator::Or => LogicalOperator::Or,914                react_compiler_ast::operators::LogicalOperator::NullishCoalescing => {915                    LogicalOperator::NullishCoalescing916                }917            };918919            builder.terminate_with_continuation(920                Terminal::Logical {921                    operator: hir_op,922                    test: test_block_id,923                    fallthrough: continuation_id,924                    id: EvaluationOrder(0),925                    loc: loc.clone(),926                },927                test_block,928            );929930            // Now in test block: lower left expression, copy to left_place931            let left_value = lower_expression_to_temporary(builder, &expr.left)?;932            builder.push(Instruction {933                id: EvaluationOrder(0),934                lvalue: left_place.clone(),935                value: InstructionValue::LoadLocal {936                    place: left_value,937                    loc: loc.clone(),938                },939                effects: None,940                loc: loc.clone(),941            });942943            builder.terminate_with_continuation(944                Terminal::Branch {945                    test: left_place,946                    consequent: consequent_block?,947                    alternate: alternate_block?,948                    fallthrough: continuation_id,949                    id: EvaluationOrder(0),950                    loc: loc.clone(),951                },952                continuation_block,953            );954955            Ok(InstructionValue::LoadLocal {956                place: place.clone(),957                loc: place.loc.clone(),958            })959        }960        Expression::UpdateExpression(update) => {961            let loc = convert_opt_loc(&update.base.loc);962            match update.argument.as_ref() {963                Expression::MemberExpression(member) => {964                    let binary_op = match &update.operator {965                        react_compiler_ast::operators::UpdateOperator::Increment => {966                            BinaryOperator::Add967                        }968                        react_compiler_ast::operators::UpdateOperator::Decrement => {969                            BinaryOperator::Subtract970                        }971                    };972                    // Use the member expression's loc (not the update expression's)973                    // to match TS behavior where the inner operations use leftExpr.node.loc974                    let member_loc = convert_opt_loc(&member.base.loc);975                    let lowered = lower_member_expression(builder, member)?;976                    let object = lowered.object;977                    let lowered_property = lowered.property;978                    let prev_value = lower_value_to_temporary(builder, lowered.value)?;979980                    let one = lower_value_to_temporary(981                        builder,982                        InstructionValue::Primitive {983                            value: PrimitiveValue::Number(FloatValue::new(1.0)),984                            loc: None,985                        },986                    )?;987                    let updated = lower_value_to_temporary(988                        builder,989                        InstructionValue::BinaryExpression {990                            operator: binary_op,991                            left: prev_value.clone(),992                            right: one,993                            loc: member_loc.clone(),994                        },995                    )?;996997                    // Store back using the property from the lowered member expression.998                    // For prefix, the result is the PropertyStore/ComputedStore lvalue999                    // (matching TS which uses newValuePlace). For postfix, it's prev_value.1000                    let new_value_place = match lowered_property {1001                        MemberProperty::Literal(prop_literal) => lower_value_to_temporary(1002                            builder,1003                            InstructionValue::PropertyStore {1004                                object,1005                                property: prop_literal,1006                                value: updated.clone(),1007                                loc: member_loc,1008                            },1009                        )?,1010                        MemberProperty::Computed(prop_place) => lower_value_to_temporary(1011                            builder,1012                            InstructionValue::ComputedStore {1013                                object,1014                                property: prop_place,1015                                value: updated.clone(),1016                                loc: member_loc,1017                            },1018                        )?,1019                    };10201021                    // Return previous for postfix, newValuePlace for prefix1022                    let result_place = if update.prefix {1023                        new_value_place1024                    } else {1025                        prev_value1026                    };1027                    Ok(InstructionValue::LoadLocal {1028                        place: result_place.clone(),1029                        loc: result_place.loc.clone(),1030                    })1031                }1032                Expression::Identifier(ident) => {1033                    let start = ident.base.start.unwrap_or(0);1034                    if builder.is_context_identifier(&ident.name, start, ident.base.node_id) {1035                        builder.record_error(CompilerErrorDetail {1036                            category: ErrorCategory::Todo,1037                            reason: "(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.".to_string(),1038                            description: None,1039                            loc: loc.clone(),1040                            suggestions: None,1041                        })?;1042                        return Ok(InstructionValue::UnsupportedNode {1043                            node_type: Some("UpdateExpression".to_string()),1044                            original_node: serialize_expression(expr),1045                            loc,1046                        });1047                    }10481049                    let ident_loc = convert_opt_loc(&ident.base.loc);1050                    let binding = builder.resolve_identifier(1051                        &ident.name,1052                        start,1053                        ident_loc.clone(),1054                        ident.base.node_id,1055                    )?;1056                    match &binding {1057                        VariableBinding::Global { .. } => {1058                            builder.record_error(CompilerErrorDetail {1059                                category: ErrorCategory::Todo,1060                                reason: "UpdateExpression where argument is a global is not yet supported".to_string(),1061                                description: None,1062                                loc: loc.clone(),1063                                suggestions: None,1064                            })?;1065                            return Ok(InstructionValue::UnsupportedNode {1066                                node_type: Some("UpdateExpression".to_string()),1067                                original_node: serialize_expression(expr),1068                                loc,1069                            });1070                        }1071                        _ => {}1072                    }1073                    let identifier = match binding {1074                        VariableBinding::Identifier { identifier, .. } => identifier,1075                        _ => {1076                            builder.record_error(CompilerErrorDetail {1077                                category: ErrorCategory::Todo,1078                                reason: "(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global".to_string(),1079                                description: None,1080                                loc: loc.clone(),1081                                suggestions: None,1082                            })?;1083                            return Ok(InstructionValue::UnsupportedNode {1084                                node_type: Some("UpdateExpression".to_string()),1085                                original_node: serialize_expression(expr),1086                                loc,1087                            });1088                        }1089                    };1090                    let lvalue_place = Place {1091                        identifier,1092                        effect: Effect::Unknown,1093                        reactive: false,1094                        loc: ident_loc.clone(),1095                    };10961097                    // Load the current value1098                    let value = lower_identifier(1099                        builder,1100                        &ident.name,1101                        start,1102                        ident_loc,1103                        ident.base.node_id,1104                    )?;11051106                    let operation = convert_update_operator(&update.operator);11071108                    if update.prefix {1109                        Ok(InstructionValue::PrefixUpdate {1110                            lvalue: lvalue_place,1111                            operation,1112                            value,1113                            loc,1114                        })1115                    } else {1116                        Ok(InstructionValue::PostfixUpdate {1117                            lvalue: lvalue_place,1118                            operation,1119                            value,1120                            loc,1121                        })1122                    }1123                }1124                _ => {1125                    builder.record_error(CompilerErrorDetail {1126                        category: ErrorCategory::Todo,1127                        reason: format!("UpdateExpression with unsupported argument type"),1128                        description: None,1129                        loc: loc.clone(),1130                        suggestions: None,1131                    })?;1132                    Ok(InstructionValue::UnsupportedNode {1133                        node_type: Some("UpdateExpression".to_string()),1134                        original_node: serialize_expression(expr),1135                        loc,1136                    })1137                }1138            }1139        }1140        Expression::ConditionalExpression(expr) => {1141            let loc = convert_opt_loc(&expr.base.loc);1142            let continuation_block = builder.reserve(builder.current_block_kind());1143            let continuation_id = continuation_block.id;1144            let test_block = builder.reserve(BlockKind::Value);1145            let test_block_id = test_block.id;1146            let place = build_temporary_place(builder, loc.clone());11471148            // Block for the consequent (test is truthy)1149            let consequent_ast_loc = expression_loc(&expr.consequent);1150            let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {1151                let consequent = lower_expression_to_temporary(builder, &expr.consequent)?;1152                lower_value_to_temporary(1153                    builder,1154                    InstructionValue::StoreLocal {1155                        lvalue: LValue {1156                            kind: InstructionKind::Const,1157                            place: place.clone(),1158                        },1159                        value: consequent,1160                        type_annotation: None,1161                        loc: loc.clone(),1162                    },1163                )?;1164                Ok(Terminal::Goto {1165                    block: continuation_id,1166                    variant: GotoVariant::Break,1167                    id: EvaluationOrder(0),1168                    loc: consequent_ast_loc,1169                })1170            });11711172            // Block for the alternate (test is falsy)1173            let alternate_ast_loc = expression_loc(&expr.alternate);1174            let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {1175                let alternate = lower_expression_to_temporary(builder, &expr.alternate)?;1176                lower_value_to_temporary(1177                    builder,1178                    InstructionValue::StoreLocal {1179                        lvalue: LValue {1180                            kind: InstructionKind::Const,1181                            place: place.clone(),1182                        },1183                        value: alternate,1184                        type_annotation: None,1185                        loc: loc.clone(),1186                    },1187                )?;1188                Ok(Terminal::Goto {1189                    block: continuation_id,1190                    variant: GotoVariant::Break,1191                    id: EvaluationOrder(0),1192                    loc: alternate_ast_loc,1193                })1194            });11951196            builder.terminate_with_continuation(1197                Terminal::Ternary {1198                    test: test_block_id,1199                    fallthrough: continuation_id,1200                    id: EvaluationOrder(0),1201                    loc: loc.clone(),1202                },1203                test_block,1204            );12051206            // Now in test block: lower test expression1207            let test_place = lower_expression_to_temporary(builder, &expr.test)?;1208            builder.terminate_with_continuation(1209                Terminal::Branch {1210                    test: test_place,1211                    consequent: consequent_block?,1212                    alternate: alternate_block?,1213                    fallthrough: continuation_id,1214                    id: EvaluationOrder(0),1215                    loc: loc.clone(),1216                },1217                continuation_block,1218            );12191220            Ok(InstructionValue::LoadLocal {1221                place: place.clone(),1222                loc: place.loc.clone(),1223            })1224        }1225        Expression::AssignmentExpression(expr) => {1226            use react_compiler_ast::operators::AssignmentOperator;1227            let loc = convert_opt_loc(&expr.base.loc);12281229            if matches!(expr.operator, AssignmentOperator::Assign) {1230                // Simple `=` assignment1231                match &*expr.left {1232                    react_compiler_ast::patterns::PatternLike::Identifier(ident) => {1233                        // Handle simple identifier assignment directly1234                        let start = ident.base.start.unwrap_or(0);1235                        let right = lower_expression_to_temporary(builder, &expr.right)?;1236                        let ident_loc = convert_opt_loc(&ident.base.loc);1237                        let binding = builder.resolve_identifier(1238                            &ident.name,1239                            start,1240                            ident_loc.clone(),1241                            ident.base.node_id,1242                        )?;1243                        match binding {1244                            VariableBinding::Identifier {1245                                identifier,1246                                binding_kind,1247                            } => {1248                                // Check for const reassignment1249                                if binding_kind == BindingKind::Const {1250                                    builder.record_error(CompilerErrorDetail {1251                                        reason: "Cannot reassign a `const` variable".to_string(),1252                                        category: ErrorCategory::Syntax,1253                                        loc: ident_loc.clone(),1254                                        description: Some(format!(1255                                            "`{}` is declared as const",1256                                            &ident.name1257                                        )),1258                                        suggestions: None,1259                                    })?;1260                                    return Ok(InstructionValue::UnsupportedNode {1261                                        node_type: Some("Identifier".to_string()),1262                                        original_node: serialize_expression(1263                                            &Expression::AssignmentExpression(expr.clone()),1264                                        ),1265                                        loc: ident_loc,1266                                    });1267                                }1268                                let place = Place {1269                                    identifier,1270                                    reactive: false,1271                                    effect: Effect::Unknown,1272                                    loc: ident_loc,1273                                };1274                                if builder.is_context_identifier(1275                                    &ident.name,1276                                    start,1277                                    ident.base.node_id,1278                                ) {1279                                    let temp = lower_value_to_temporary(1280                                        builder,1281                                        InstructionValue::StoreContext {1282                                            lvalue: LValue {1283                                                kind: InstructionKind::Reassign,1284                                                place: place.clone(),1285                                            },1286                                            value: right,1287                                            loc: place.loc.clone(),1288                                        },1289                                    )?;1290                                    Ok(InstructionValue::LoadLocal {1291                                        place: temp.clone(),1292                                        loc: temp.loc.clone(),1293                                    })1294                                } else {1295                                    let temp = lower_value_to_temporary(1296                                        builder,1297                                        InstructionValue::StoreLocal {1298                                            lvalue: LValue {1299                                                kind: InstructionKind::Reassign,1300                                                place: place.clone(),1301                                            },1302                                            value: right,1303                                            type_annotation: None,1304                                            loc: place.loc.clone(),1305                                        },1306                                    )?;1307                                    Ok(InstructionValue::LoadLocal {1308                                        place: temp.clone(),1309                                        loc: temp.loc.clone(),1310                                    })1311                                }1312                            }1313                            _ => {1314                                // Global or import assignment1315                                let name = ident.name.clone();1316                                let temp = lower_value_to_temporary(1317                                    builder,1318                                    InstructionValue::StoreGlobal {1319                                        name,1320                                        value: right,1321                                        loc: ident_loc,1322                                    },1323                                )?;1324                                Ok(InstructionValue::LoadLocal {1325                                    place: temp.clone(),1326                                    loc: temp.loc.clone(),1327                                })1328                            }1329                        }1330                    }1331                    react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {1332                        // Member expression assignment: a.b = value or a[b] = value1333                        let right = lower_expression_to_temporary(builder, &expr.right)?;1334                        let left_loc = convert_opt_loc(&member.base.loc);1335                        let object = lower_expression_to_temporary(builder, &member.object)?;1336                        let temp = if !member.computed1337                            || matches!(1338                                &*member.property,1339                                react_compiler_ast::expressions::Expression::NumericLiteral(_)1340                            ) {1341                            match &*member.property {1342                                react_compiler_ast::expressions::Expression::Identifier(1343                                    prop_id,1344                                ) => lower_value_to_temporary(1345                                    builder,1346                                    InstructionValue::PropertyStore {1347                                        object,1348                                        property: PropertyLiteral::String(prop_id.name.clone()),1349                                        value: right,1350                                        loc: left_loc,1351                                    },1352                                )?,1353                                react_compiler_ast::expressions::Expression::NumericLiteral(1354                                    num,1355                                ) => lower_value_to_temporary(1356                                    builder,1357                                    InstructionValue::PropertyStore {1358                                        object,1359                                        property: PropertyLiteral::Number(FloatValue::new(1360                                            num.precise_value(),1361                                        )),1362                                        value: right,1363                                        loc: left_loc,1364                                    },1365                                )?,1366                                _ => {1367                                    let prop =1368                                        lower_expression_to_temporary(builder, &member.property)?;1369                                    lower_value_to_temporary(1370                                        builder,1371                                        InstructionValue::ComputedStore {1372                                            object,1373                                            property: prop,1374                                            value: right,1375                                            loc: left_loc,1376                                        },1377                                    )?1378                                }1379                            }1380                        } else {1381                            let prop = lower_expression_to_temporary(builder, &member.property)?;1382                            lower_value_to_temporary(1383                                builder,1384                                InstructionValue::ComputedStore {1385                                    object,1386                                    property: prop,1387                                    value: right,1388                                    loc: left_loc,1389                                },1390                            )?1391                        };1392                        Ok(InstructionValue::LoadLocal {1393                            place: temp.clone(),1394                            loc: temp.loc.clone(),1395                        })1396                    }1397                    _ => {1398                        // Destructuring assignment1399                        let right = lower_expression_to_temporary(builder, &expr.right)?;1400                        let left_loc = pattern_like_hir_loc(&expr.left);1401                        let result = lower_assignment(1402                            builder,1403                            left_loc,1404                            InstructionKind::Reassign,1405                            &expr.left,1406                            right.clone(),1407                            AssignmentStyle::Destructure,1408                        )?;1409                        match result {1410                            Some(place) => Ok(InstructionValue::LoadLocal {1411                                place: place.clone(),1412                                loc: place.loc.clone(),1413                            }),1414                            None => Ok(InstructionValue::LoadLocal { place: right, loc }),1415                        }1416                    }1417                }1418            } else {1419                // Compound assignment operators1420                let binary_op = match expr.operator {1421                    AssignmentOperator::AddAssign => Some(BinaryOperator::Add),1422                    AssignmentOperator::SubAssign => Some(BinaryOperator::Subtract),1423                    AssignmentOperator::MulAssign => Some(BinaryOperator::Multiply),1424                    AssignmentOperator::DivAssign => Some(BinaryOperator::Divide),1425                    AssignmentOperator::RemAssign => Some(BinaryOperator::Modulo),1426                    AssignmentOperator::ExpAssign => Some(BinaryOperator::Exponent),1427                    AssignmentOperator::ShlAssign => Some(BinaryOperator::ShiftLeft),1428                    AssignmentOperator::ShrAssign => Some(BinaryOperator::ShiftRight),1429                    AssignmentOperator::UShrAssign => Some(BinaryOperator::UnsignedShiftRight),1430                    AssignmentOperator::BitOrAssign => Some(BinaryOperator::BitwiseOr),1431                    AssignmentOperator::BitXorAssign => Some(BinaryOperator::BitwiseXor),1432                    AssignmentOperator::BitAndAssign => Some(BinaryOperator::BitwiseAnd),1433                    AssignmentOperator::OrAssign1434                    | AssignmentOperator::AndAssign1435                    | AssignmentOperator::NullishAssign => {1436                        // Logical assignment operators (||=, &&=, ??=) - not yet supported1437                        builder.record_error(CompilerErrorDetail {1438                            reason:1439                                "Logical assignment operators (||=, &&=, ??=) are not yet supported"1440                                    .to_string(),1441                            category: ErrorCategory::Todo,1442                            loc: loc.clone(),1443                            description: None,1444                            suggestions: None,1445                        })?;1446                        return Ok(InstructionValue::UnsupportedNode {1447                            node_type: Some("AssignmentExpression".to_string()),1448                            original_node: serialize_expression(&Expression::AssignmentExpression(1449                                expr.clone(),1450                            )),1451                            loc,1452                        });1453                    }1454                    AssignmentOperator::Assign => unreachable!(),1455                };1456                let binary_op = match binary_op {1457                    Some(op) => op,1458                    None => {1459                        return Ok(InstructionValue::UnsupportedNode {1460                            node_type: Some("AssignmentExpression".to_string()),1461                            original_node: serialize_expression(&Expression::AssignmentExpression(1462                                expr.clone(),1463                            )),1464                            loc,1465                        });1466                    }1467                };14681469                match &*expr.left {1470                    react_compiler_ast::patterns::PatternLike::Identifier(ident) => {1471                        let start = ident.base.start.unwrap_or(0);1472                        let left_place = lower_expression_to_temporary(1473                            builder,1474                            &react_compiler_ast::expressions::Expression::Identifier(ident.clone()),1475                        )?;1476                        let right = lower_expression_to_temporary(builder, &expr.right)?;1477                        let binary_place = lower_value_to_temporary(1478                            builder,1479                            InstructionValue::BinaryExpression {1480                                operator: binary_op,1481                                left: left_place,1482                                right,1483                                loc: loc.clone(),1484                            },1485                        )?;1486                        let ident_loc = convert_opt_loc(&ident.base.loc);1487                        let binding = builder.resolve_identifier(1488                            &ident.name,1489                            start,1490                            ident_loc.clone(),1491                            ident.base.node_id,1492                        )?;1493                        match binding {1494                            VariableBinding::Identifier { identifier, .. } => {1495                                let place = Place {1496                                    identifier,1497                                    reactive: false,1498                                    effect: Effect::Unknown,1499                                    loc: ident_loc,1500                                };1501                                if builder.is_context_identifier(1502                                    &ident.name,1503                                    start,1504                                    ident.base.node_id,1505                                ) {1506                                    lower_value_to_temporary(1507                                        builder,1508                                        InstructionValue::StoreContext {1509                                            lvalue: LValue {1510                                                kind: InstructionKind::Reassign,1511                                                place: place.clone(),1512                                            },1513                                            value: binary_place,1514                                            loc: loc.clone(),1515                                        },1516                                    )?;1517                                    Ok(InstructionValue::LoadContext { place, loc })1518                                } else {1519                                    lower_value_to_temporary(1520                                        builder,1521                                        InstructionValue::StoreLocal {1522                                            lvalue: LValue {1523                                                kind: InstructionKind::Reassign,1524                                                place: place.clone(),1525                                            },1526                                            value: binary_place,1527                                            type_annotation: None,1528                                            loc: loc.clone(),1529                                        },1530                                    )?;1531                                    Ok(InstructionValue::LoadLocal { place, loc })1532                                }1533                            }1534                            _ => {1535                                // Global assignment1536                                let name = ident.name.clone();1537                                let temp = lower_value_to_temporary(1538                                    builder,1539                                    InstructionValue::StoreGlobal {1540                                        name,1541                                        value: binary_place,1542                                        loc: loc.clone(),1543                                    },1544                                )?;1545                                Ok(InstructionValue::LoadLocal {1546                                    place: temp.clone(),1547                                    loc: temp.loc.clone(),1548                                })1549                            }1550                        }1551                    }1552                    react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {1553                        // a.b += right: read, compute, store1554                        // Match TS behavior: return the PropertyStore/ComputedStore value1555                        // directly (let the caller lower it to a temporary)1556                        let member_loc = convert_opt_loc(&member.base.loc);1557                        let lowered = lower_member_expression(builder, member)?;1558                        let object = lowered.object;1559                        let lowered_property = lowered.property;1560                        let current_value = lower_value_to_temporary(builder, lowered.value)?;1561                        let right = lower_expression_to_temporary(builder, &expr.right)?;1562                        let result = lower_value_to_temporary(1563                            builder,1564                            InstructionValue::BinaryExpression {1565                                operator: binary_op,1566                                left: current_value,1567                                right,1568                                loc: member_loc.clone(),1569                            },1570                        )?;1571                        // Return the store instruction value directly (matching TS behavior)1572                        match lowered_property {1573                            MemberProperty::Literal(prop_literal) => {1574                                Ok(InstructionValue::PropertyStore {1575                                    object,1576                                    property: prop_literal,1577                                    value: result,1578                                    loc: member_loc,1579                                })1580                            }1581                            MemberProperty::Computed(prop_place) => {1582                                Ok(InstructionValue::ComputedStore {1583                                    object,1584                                    property: prop_place,1585                                    value: result,1586                                    loc: member_loc,1587                                })1588                            }1589                        }1590                    }1591                    _ => {1592                        builder.record_error(CompilerErrorDetail {1593                            reason: "Compound assignment to complex pattern is not yet supported"1594                                .to_string(),1595                            category: ErrorCategory::Todo,1596                            loc: loc.clone(),1597                            description: None,1598                            suggestions: None,1599                        })?;1600                        Ok(InstructionValue::UnsupportedNode {1601                            node_type: Some("AssignmentExpression".to_string()),1602                            original_node: serialize_expression(&Expression::AssignmentExpression(1603                                expr.clone(),1604                            )),1605                            loc,1606                        })1607                    }1608                }1609            }1610        }1611        Expression::SequenceExpression(seq) => {1612            let loc = convert_opt_loc(&seq.base.loc);16131614            if seq.expressions.is_empty() {1615                builder.record_error(CompilerErrorDetail {1616                    category: ErrorCategory::Syntax,1617                    reason: "Expected sequence expression to have at least one expression"1618                        .to_string(),1619                    description: None,1620                    loc: loc.clone(),1621                    suggestions: None,1622                })?;1623                return Ok(InstructionValue::UnsupportedNode {1624                    node_type: Some("SequenceExpression".to_string()),1625                    original_node: serialize_expression(expr),1626                    loc,1627                });1628            }16291630            let continuation_block = builder.reserve(builder.current_block_kind());1631            let continuation_id = continuation_block.id;1632            let place = build_temporary_place(builder, loc.clone());16331634            let sequence_block = builder.try_enter(BlockKind::Sequence, |builder, _block_id| {1635                let mut last: Option<Place> = None;1636                for item in &seq.expressions {1637                    last = Some(lower_expression_to_temporary(builder, item)?);1638                }1639                if let Some(last) = last {1640                    lower_value_to_temporary(1641                        builder,1642                        InstructionValue::StoreLocal {1643                            lvalue: LValue {1644                                kind: InstructionKind::Const,1645                                place: place.clone(),1646                            },1647                            value: last,1648                            type_annotation: None,1649                            loc: loc.clone(),1650                        },1651                    )?;1652                }1653                Ok(Terminal::Goto {1654                    block: continuation_id,1655                    variant: GotoVariant::Break,1656                    id: EvaluationOrder(0),1657                    loc: loc.clone(),1658                })1659            });16601661            builder.terminate_with_continuation(1662                Terminal::Sequence {1663                    block: sequence_block?,1664                    fallthrough: continuation_id,1665                    id: EvaluationOrder(0),1666                    loc: loc.clone(),1667                },1668                continuation_block,1669            );1670            Ok(InstructionValue::LoadLocal { place, loc })1671        }1672        Expression::ArrowFunctionExpression(_) => Ok(lower_function_to_value(1673            builder,1674            expr,1675            FunctionExpressionType::ArrowFunctionExpression,1676        )?),1677        Expression::FunctionExpression(_) => Ok(lower_function_to_value(1678            builder,1679            expr,1680            FunctionExpressionType::FunctionExpression,1681        )?),1682        Expression::ObjectExpression(obj) => {1683            let loc = convert_opt_loc(&obj.base.loc);1684            let mut properties: Vec<ObjectPropertyOrSpread> = Vec::new();1685            for prop in &obj.properties {1686                match prop {1687                    react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(1688                        p,1689                    ) => {1690                        let key = lower_object_property_key(builder, &p.key, p.computed)?;1691                        let key = match key {1692                            Some(k) => k,1693                            None => continue,1694                        };1695                        let value = lower_expression_to_temporary(builder, &p.value)?;1696                        properties.push(ObjectPropertyOrSpread::Property(ObjectProperty {1697                            key,1698                            property_type: ObjectPropertyType::Property,1699                            place: value,1700                        }));1701                    }1702                    react_compiler_ast::expressions::ObjectExpressionProperty::SpreadElement(1703                        spread,1704                    ) => {1705                        let place = lower_expression_to_temporary(builder, &spread.argument)?;1706                        properties.push(ObjectPropertyOrSpread::Spread(SpreadPattern { place }));1707                    }1708                    react_compiler_ast::expressions::ObjectExpressionProperty::ObjectMethod(1709                        method,1710                    ) => {1711                        if let Some(prop) = lower_object_method(builder, method)? {1712                            properties.push(ObjectPropertyOrSpread::Property(prop));1713                        }1714                    }1715                }1716            }1717            Ok(InstructionValue::ObjectExpression { properties, loc })1718        }1719        Expression::ArrayExpression(arr) => {1720            let loc = convert_opt_loc(&arr.base.loc);1721            let mut elements: Vec<ArrayElement> = Vec::new();1722            for element in &arr.elements {1723                match element {1724                    None => {1725                        elements.push(ArrayElement::Hole);1726                    }1727                    Some(Expression::SpreadElement(spread)) => {1728                        let place = lower_expression_to_temporary(builder, &spread.argument)?;1729                        elements.push(ArrayElement::Spread(SpreadPattern { place }));1730                    }1731                    Some(expr) => {1732                        let place = lower_expression_to_temporary(builder, expr)?;1733                        elements.push(ArrayElement::Place(place));1734                    }1735                }1736            }1737            Ok(InstructionValue::ArrayExpression { elements, loc })1738        }1739        Expression::NewExpression(new_expr) => {1740            let loc = convert_opt_loc(&new_expr.base.loc);1741            let callee = lower_expression_to_temporary(builder, &new_expr.callee)?;1742            let args = lower_arguments(builder, &new_expr.arguments)?;1743            Ok(InstructionValue::NewExpression { callee, args, loc })1744        }1745        Expression::TemplateLiteral(tmpl) => {1746            let loc = convert_opt_loc(&tmpl.base.loc);1747            let subexprs: Vec<Place> = tmpl1748                .expressions1749                .iter()1750                .map(|e| lower_expression_to_temporary(builder, e))1751                .collect::<Result<Vec<_>, _>>()?;1752            let quasis: Vec<TemplateQuasi> = tmpl1753                .quasis1754                .iter()1755                .map(|q| TemplateQuasi {1756                    raw: q.value.raw.clone(),1757                    cooked: q.value.cooked.clone(),1758                })1759                .collect();1760            Ok(InstructionValue::TemplateLiteral {1761                subexprs,1762                quasis,1763                loc,1764            })1765        }1766        Expression::TaggedTemplateExpression(tagged) => {1767            let loc = convert_opt_loc(&tagged.base.loc);1768            if !tagged.quasi.expressions.is_empty() {1769                builder.record_error(CompilerErrorDetail {1770                    category: ErrorCategory::Todo,1771                    reason:1772                        "(BuildHIR::lowerExpression) Handle tagged template with interpolations"1773                            .to_string(),1774                    description: None,1775                    loc: loc.clone(),1776                    suggestions: None,1777                })?;1778                return Ok(InstructionValue::UnsupportedNode {1779                    node_type: Some("TaggedTemplateExpression".to_string()),1780                    original_node: serialize_expression(expr),1781                    loc,1782                });1783            }1784            assert!(1785                tagged.quasi.quasis.len() == 1,1786                "there should be only one quasi as we don't support interpolations yet"1787            );1788            let quasi = &tagged.quasi.quasis[0];1789            // Check if raw and cooked values differ (e.g., graphql tagged templates)1790            if quasi.value.raw != quasi.value.cooked.clone().unwrap_or_default() {1791                builder.record_error(CompilerErrorDetail {1792                    category: ErrorCategory::Todo,1793                    reason: "(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value".to_string(),1794                    description: None,1795                    loc: loc.clone(),1796                    suggestions: None,1797                })?;1798                return Ok(InstructionValue::UnsupportedNode {1799                    node_type: Some("TaggedTemplateExpression".to_string()),1800                    original_node: serialize_expression(expr),1801                    loc,1802                });1803            }1804            let value = TemplateQuasi {1805                raw: quasi.value.raw.clone(),1806                cooked: quasi.value.cooked.clone(),1807            };1808            let tag = lower_expression_to_temporary(builder, &tagged.tag)?;1809            Ok(InstructionValue::TaggedTemplateExpression { tag, value, loc })1810        }1811        Expression::AwaitExpression(await_expr) => {1812            let loc = convert_opt_loc(&await_expr.base.loc);1813            let value = lower_expression_to_temporary(builder, &await_expr.argument)?;1814            Ok(InstructionValue::Await { value, loc })1815        }1816        Expression::YieldExpression(yld) => {1817            let loc = convert_opt_loc(&yld.base.loc);1818            builder.record_error(CompilerErrorDetail {1819                category: ErrorCategory::Todo,1820                reason: "(BuildHIR::lowerExpression) Handle YieldExpression expressions"1821                    .to_string(),1822                description: None,1823                loc: loc.clone(),1824                suggestions: None,1825            })?;1826            Ok(InstructionValue::UnsupportedNode {1827                node_type: Some("YieldExpression".to_string()),1828                original_node: serialize_expression(expr),1829                loc,1830            })1831        }1832        Expression::SpreadElement(spread) => {1833            // SpreadElement should be handled by the parent context (array/object/call)1834            // If we reach here, just lower the argument expression1835            Ok(lower_expression(builder, &spread.argument)?)1836        }1837        Expression::MetaProperty(meta) => {1838            let loc = convert_opt_loc(&meta.base.loc);1839            if meta.meta.name == "import" && meta.property.name == "meta" {1840                Ok(InstructionValue::MetaProperty {1841                    meta: meta.meta.name.clone(),1842                    property: meta.property.name.clone(),1843                    loc,1844                })1845            } else {1846                builder.record_error(CompilerErrorDetail {1847                    category: ErrorCategory::Todo,1848                    reason: "(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta".to_string(),1849                    description: None,1850                    loc: loc.clone(),1851                    suggestions: None,1852                })?;1853                Ok(InstructionValue::UnsupportedNode {1854                    node_type: Some("MetaProperty".to_string()),1855                    original_node: serialize_expression(expr),1856                    loc,1857                })1858            }1859        }1860        Expression::ClassExpression(cls) => {1861            let loc = convert_opt_loc(&cls.base.loc);1862            builder.record_error(CompilerErrorDetail {1863                category: ErrorCategory::Todo,1864                reason: "(BuildHIR::lowerExpression) Handle ClassExpression expressions"1865                    .to_string(),1866                description: None,1867                loc: loc.clone(),1868                suggestions: None,1869            })?;1870            Ok(InstructionValue::UnsupportedNode {1871                node_type: Some("ClassExpression".to_string()),1872                original_node: serialize_expression(expr),1873                loc,1874            })1875        }1876        Expression::PrivateName(pn) => {1877            let loc = convert_opt_loc(&pn.base.loc);1878            builder.record_error(CompilerErrorDetail {1879                category: ErrorCategory::Todo,1880                reason: "(BuildHIR::lowerExpression) Handle PrivateName expressions".to_string(),1881                description: None,1882                loc: loc.clone(),1883                suggestions: None,1884            })?;1885            Ok(InstructionValue::UnsupportedNode {1886                node_type: Some("PrivateName".to_string()),1887                original_node: serialize_expression(expr),1888                loc,1889            })1890        }1891        Expression::Super(sup) => {1892            let loc = convert_opt_loc(&sup.base.loc);1893            builder.record_error(CompilerErrorDetail {1894                category: ErrorCategory::Todo,1895                reason: "(BuildHIR::lowerExpression) Handle Super expressions".to_string(),1896                description: None,1897                loc: loc.clone(),1898                suggestions: None,1899            })?;1900            Ok(InstructionValue::UnsupportedNode {1901                node_type: Some("Super".to_string()),1902                original_node: serialize_expression(expr),1903                loc,1904            })1905        }1906        Expression::Import(imp) => {1907            let loc = convert_opt_loc(&imp.base.loc);1908            builder.record_error(CompilerErrorDetail {1909                category: ErrorCategory::Todo,1910                reason: "(BuildHIR::lowerExpression) Handle Import expressions".to_string(),1911                description: None,1912                loc: loc.clone(),1913                suggestions: None,1914            })?;1915            Ok(InstructionValue::UnsupportedNode {1916                node_type: Some("Import".to_string()),1917                original_node: serialize_expression(expr),1918                loc,1919            })1920        }1921        Expression::ThisExpression(this) => {1922            let loc = convert_opt_loc(&this.base.loc);1923            builder.record_error(CompilerErrorDetail {1924                category: ErrorCategory::Todo,1925                reason: "(BuildHIR::lowerExpression) Handle ThisExpression expressions".to_string(),1926                description: None,1927                loc: loc.clone(),1928                suggestions: None,1929            })?;1930            Ok(InstructionValue::UnsupportedNode {1931                node_type: Some("ThisExpression".to_string()),1932                original_node: serialize_expression(expr),1933                loc,1934            })1935        }1936        Expression::ParenthesizedExpression(paren) => {1937            Ok(lower_expression(builder, &paren.expression)?)1938        }1939        Expression::JSXElement(jsx_element) => {1940            let loc = convert_opt_loc(&jsx_element.base.loc);1941            let opening_loc = convert_opt_loc(&jsx_element.opening_element.base.loc);1942            let closing_loc = jsx_element1943                .closing_element1944                .as_ref()1945                .and_then(|c| convert_opt_loc(&c.base.loc));19461947            // Lower the tag name1948            let tag = lower_jsx_element_name(builder, &jsx_element.opening_element.name)?;19491950            // Lower attributes (props)1951            let mut props: Vec<JsxAttribute> = Vec::new();1952            for attr_item in &jsx_element.opening_element.attributes {1953                use react_compiler_ast::jsx::JSXAttributeItem;1954                use react_compiler_ast::jsx::JSXAttributeName;1955                use react_compiler_ast::jsx::JSXAttributeValue;1956                match attr_item {1957                    JSXAttributeItem::JSXSpreadAttribute(spread) => {1958                        let argument = lower_expression_to_temporary(builder, &spread.argument)?;1959                        props.push(JsxAttribute::SpreadAttribute { argument });1960                    }1961                    JSXAttributeItem::JSXAttribute(attr) => {1962                        // Get the attribute name1963                        let prop_name = match &attr.name {1964                            JSXAttributeName::JSXIdentifier(id) => {1965                                let name = &id.name;1966                                if name.contains(':') {1967                                    builder.record_error(CompilerErrorDetail {1968                                        category: ErrorCategory::Todo,1969                                        reason: format!(1970                                            "(BuildHIR::lowerExpression) Unexpected colon in attribute name `{}`",1971                                            name1972                                        ),1973                                        description: None,1974                                        loc: convert_opt_loc(&id.base.loc),1975                                        suggestions: None,1976                                    })?;1977                                }1978                                name.clone()1979                            }1980                            JSXAttributeName::JSXNamespacedName(ns) => {1981                                format!("{}:{}", ns.namespace.name, ns.name.name)1982                            }1983                        };19841985                        // Get the attribute value1986                        let value = match &attr.value {1987                            Some(JSXAttributeValue::StringLiteral(s)) => {1988                                let str_loc = convert_opt_loc(&s.base.loc);1989                                lower_value_to_temporary(1990                                    builder,1991                                    InstructionValue::Primitive {1992                                        value: PrimitiveValue::String(s.value.clone()),1993                                        loc: str_loc,1994                                    },1995                                )?1996                            }1997                            Some(JSXAttributeValue::JSXExpressionContainer(container)) => {1998                                use react_compiler_ast::jsx::JSXExpressionContainerExpr;1999                                match &container.expression {2000                                    JSXExpressionContainerExpr::JSXEmptyExpression(_) => {

Findings

✓ No findings reported for this file.

Get this view in your editor

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