compiler/crates/react_compiler_lowering/src/build_hir.rs RUST 7,417 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 7,417.
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                if name == "arguments" {410                    builder.record_error(CompilerErrorDetail {411                        category: ErrorCategory::UnsupportedSyntax,412                        reason: "Implicit 'arguments' is not supported".to_string(),413                        description: Some(414                            "React Compiler does not support compiling functions that reference the implicit arguments object"415                                .to_string(),416                        ),417                        loc: loc.clone(),418                        suggestions: None,419                    })?;420                }421            }422            let non_local_binding = match binding {423                VariableBinding::Global { name } => NonLocalBinding::Global { name },424                VariableBinding::ImportDefault { name, module } => {425                    NonLocalBinding::ImportDefault { name, module }426                }427                VariableBinding::ImportSpecifier {428                    name,429                    module,430                    imported,431                } => NonLocalBinding::ImportSpecifier {432                    name,433                    module,434                    imported,435                },436                VariableBinding::ImportNamespace { name, module } => {437                    NonLocalBinding::ImportNamespace { name, module }438                }439                VariableBinding::ModuleLocal { name } => NonLocalBinding::ModuleLocal { name },440                VariableBinding::Identifier { .. } => unreachable!(),441            };442            let instr_value = InstructionValue::LoadGlobal {443                binding: non_local_binding,444                loc: loc.clone(),445            };446            Ok(lower_value_to_temporary(builder, instr_value)?)447        }448    }449}450451// =============================================================================452// lower_arguments453// =============================================================================454455fn lower_arguments(456    builder: &mut HirBuilder,457    args: &[react_compiler_ast::expressions::Expression],458) -> Result<Vec<PlaceOrSpread>, CompilerError> {459    use react_compiler_ast::expressions::Expression;460    let mut result = Vec::new();461    for arg in args {462        match arg {463            Expression::SpreadElement(spread) => {464                let place = lower_expression_to_temporary(builder, &spread.argument)?;465                result.push(PlaceOrSpread::Spread(SpreadPattern { place }));466            }467            _ => {468                let place = lower_expression_to_temporary(builder, arg)?;469                result.push(PlaceOrSpread::Place(place));470            }471        }472    }473    Ok(result)474}475476fn convert_update_operator(op: &react_compiler_ast::operators::UpdateOperator) -> UpdateOperator {477    match op {478        react_compiler_ast::operators::UpdateOperator::Increment => UpdateOperator::Increment,479        react_compiler_ast::operators::UpdateOperator::Decrement => UpdateOperator::Decrement,480    }481}482483// =============================================================================484// lower_member_expression485// =============================================================================486487enum MemberProperty {488    Literal(PropertyLiteral),489    Computed(Place),490}491492struct LoweredMemberExpression {493    object: Place,494    property: MemberProperty,495    value: InstructionValue,496}497498fn lower_member_expression(499    builder: &mut HirBuilder,500    member: &react_compiler_ast::expressions::MemberExpression,501) -> Result<LoweredMemberExpression, CompilerError> {502    Ok(lower_member_expression_impl(builder, member, None)?)503}504505fn lower_member_expression_with_object(506    builder: &mut HirBuilder,507    member: &react_compiler_ast::expressions::OptionalMemberExpression,508    lowered_object: Place,509) -> Result<LoweredMemberExpression, CompilerError> {510    // OptionalMemberExpression has the same shape as MemberExpression for property access511    use react_compiler_ast::expressions::Expression;512    let loc = convert_opt_loc(&member.base.loc);513    let object = lowered_object;514515    if !member.computed {516        let prop_literal = match member.property.as_ref() {517            Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),518            Expression::NumericLiteral(lit) => {519                PropertyLiteral::Number(FloatValue::new(lit.precise_value()))520            }521            _ => {522                builder.record_error(CompilerErrorDetail {523                    category: ErrorCategory::Todo,524                    reason: format!(525                        "(BuildHIR::lowerMemberExpression) Handle {:?} property",526                        member.property527                    ),528                    description: None,529                    loc: loc.clone(),530                    suggestions: None,531                })?;532                return Ok(LoweredMemberExpression {533                    object,534                    property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),535                    value: InstructionValue::UnsupportedNode {536                        node_type: Some("OptionalMemberExpression".to_string()),537                        original_node: serialize_expression(538                            &react_compiler_ast::expressions::Expression::OptionalMemberExpression(539                                member.clone(),540                            ),541                        ),542                        loc,543                    },544                });545            }546        };547        let value = InstructionValue::PropertyLoad {548            object: object.clone(),549            property: prop_literal.clone(),550            loc,551        };552        Ok(LoweredMemberExpression {553            object,554            property: MemberProperty::Literal(prop_literal),555            value,556        })557    } else {558        if let Expression::NumericLiteral(lit) = member.property.as_ref() {559            let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));560            let value = InstructionValue::PropertyLoad {561                object: object.clone(),562                property: prop_literal.clone(),563                loc,564            };565            return Ok(LoweredMemberExpression {566                object,567                property: MemberProperty::Literal(prop_literal),568                value,569            });570        }571        let property = lower_expression_to_temporary(builder, &member.property)?;572        let value = InstructionValue::ComputedLoad {573            object: object.clone(),574            property: property.clone(),575            loc,576        };577        Ok(LoweredMemberExpression {578            object,579            property: MemberProperty::Computed(property),580            value,581        })582    }583}584585fn lower_member_expression_impl(586    builder: &mut HirBuilder,587    member: &react_compiler_ast::expressions::MemberExpression,588    lowered_object: Option<Place>,589) -> Result<LoweredMemberExpression, CompilerError> {590    use react_compiler_ast::expressions::Expression;591    let loc = convert_opt_loc(&member.base.loc);592    let object = match lowered_object {593        Some(obj) => obj,594        None => lower_expression_to_temporary(builder, &member.object)?,595    };596597    if !member.computed {598        // Non-computed: property must be an identifier or numeric literal599        let prop_literal = match member.property.as_ref() {600            Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),601            Expression::NumericLiteral(lit) => {602                PropertyLiteral::Number(FloatValue::new(lit.precise_value()))603            }604            _ => {605                builder.record_error(CompilerErrorDetail {606                    category: ErrorCategory::Todo,607                    reason: format!(608                        "(BuildHIR::lowerMemberExpression) Handle {:?} property",609                        member.property610                    ),611                    description: None,612                    loc: loc.clone(),613                    suggestions: None,614                })?;615                return Ok(LoweredMemberExpression {616                    object,617                    property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),618                    value: InstructionValue::UnsupportedNode {619                        node_type: Some("MemberExpression".to_string()),620                        original_node: serialize_expression(621                            &react_compiler_ast::expressions::Expression::MemberExpression(622                                member.clone(),623                            ),624                        ),625                        loc,626                    },627                });628            }629        };630        let value = InstructionValue::PropertyLoad {631            object: object.clone(),632            property: prop_literal.clone(),633            loc,634        };635        Ok(LoweredMemberExpression {636            object,637            property: MemberProperty::Literal(prop_literal),638            value,639        })640    } else {641        // Computed: check for numeric literal first (treated as PropertyLoad in TS)642        if let Expression::NumericLiteral(lit) = member.property.as_ref() {643            let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));644            let value = InstructionValue::PropertyLoad {645                object: object.clone(),646                property: prop_literal.clone(),647                loc,648            };649            return Ok(LoweredMemberExpression {650                object,651                property: MemberProperty::Literal(prop_literal),652                value,653            });654        }655        // Otherwise lower property to temporary for ComputedLoad656        let property = lower_expression_to_temporary(builder, &member.property)?;657        let value = InstructionValue::ComputedLoad {658            object: object.clone(),659            property: property.clone(),660            loc,661        };662        Ok(LoweredMemberExpression {663            object,664            property: MemberProperty::Computed(property),665            value,666        })667    }668}669670// =============================================================================671// lower_expression672// =============================================================================673674fn lower_expression(675    builder: &mut HirBuilder,676    expr: &react_compiler_ast::expressions::Expression,677) -> Result<InstructionValue, CompilerError> {678    use react_compiler_ast::expressions::Expression;679680    match expr {681        Expression::Identifier(ident) => {682            let loc = convert_opt_loc(&ident.base.loc);683            let start = ident.base.start.unwrap_or(0);684            let place =685                lower_identifier(builder, &ident.name, start, loc.clone(), ident.base.node_id)?;686            // Determine LoadLocal vs LoadContext based on context identifier check687            if builder.is_context_identifier(&ident.name, start, ident.base.node_id) {688                Ok(InstructionValue::LoadContext { place, loc })689            } else {690                Ok(InstructionValue::LoadLocal { place, loc })691            }692        }693        Expression::NullLiteral(lit) => {694            let loc = convert_opt_loc(&lit.base.loc);695            Ok(InstructionValue::Primitive {696                value: PrimitiveValue::Null,697                loc,698            })699        }700        Expression::BooleanLiteral(lit) => {701            let loc = convert_opt_loc(&lit.base.loc);702            Ok(InstructionValue::Primitive {703                value: PrimitiveValue::Boolean(lit.value),704                loc,705            })706        }707        Expression::NumericLiteral(lit) => {708            let loc = convert_opt_loc(&lit.base.loc);709            Ok(InstructionValue::Primitive {710                value: PrimitiveValue::Number(FloatValue::new(lit.precise_value())),711                loc,712            })713        }714        Expression::StringLiteral(lit) => {715            let loc = convert_opt_loc(&lit.base.loc);716            Ok(InstructionValue::Primitive {717                value: PrimitiveValue::String(lit.value.clone()),718                loc,719            })720        }721        Expression::BinaryExpression(bin) => {722            let loc = convert_opt_loc(&bin.base.loc);723            // Check for pipeline operator before lowering operands724            if matches!(725                bin.operator,726                react_compiler_ast::operators::BinaryOperator::Pipeline727            ) {728                builder.record_error(CompilerErrorDetail {729                    category: ErrorCategory::Todo,730                    reason: "(BuildHIR::lowerExpression) Pipe operator not supported".to_string(),731                    description: None,732                    loc: loc.clone(),733                    suggestions: None,734                })?;735                return Ok(InstructionValue::UnsupportedNode {736                    node_type: Some("BinaryExpression".to_string()),737                    original_node: serialize_expression(expr),738                    loc,739                });740            }741            let left = lower_expression_to_temporary(builder, &bin.left)?;742            let right = lower_expression_to_temporary(builder, &bin.right)?;743            let operator = convert_binary_operator(&bin.operator);744            Ok(InstructionValue::BinaryExpression {745                operator,746                left,747                right,748                loc,749            })750        }751        Expression::UnaryExpression(unary) => {752            let loc = convert_opt_loc(&unary.base.loc);753            match &unary.operator {754                react_compiler_ast::operators::UnaryOperator::Delete => {755                    // Delete can be on member expressions or identifiers756                    let loc = convert_opt_loc(&unary.base.loc);757                    match &*unary.argument {758                        Expression::MemberExpression(member) => {759                            let object = lower_expression_to_temporary(builder, &member.object)?;760                            if !member.computed {761                                match &*member.property {762                                    Expression::Identifier(prop_id) => {763                                        Ok(InstructionValue::PropertyDelete {764                                            object,765                                            property: PropertyLiteral::String(prop_id.name.clone()),766                                            loc,767                                        })768                                    }769                                    _ => {770                                        builder.record_error(CompilerErrorDetail {771                                            reason: "Unsupported delete target".to_string(),772                                            category: ErrorCategory::Todo,773                                            loc: loc.clone(),774                                            description: None,775                                            suggestions: None,776                                        })?;777                                        Ok(InstructionValue::UnsupportedNode {778                                            node_type: Some("UnaryExpression".to_string()),779                                            original_node: serialize_expression(expr),780                                            loc,781                                        })782                                    }783                                }784                            } else {785                                let property =786                                    lower_expression_to_temporary(builder, &member.property)?;787                                Ok(InstructionValue::ComputedDelete {788                                    object,789                                    property,790                                    loc,791                                })792                            }793                        }794                        _ => {795                            // delete on non-member expression (e.g., optional chain, identifier)796                            builder.record_error(CompilerErrorDetail {797                                reason: "Only object properties can be deleted".to_string(),798                                category: ErrorCategory::Syntax,799                                loc: loc.clone(),800                                description: None,801                                suggestions: None,802                            })?;803                            Ok(InstructionValue::UnsupportedNode {804                                node_type: Some("UnaryExpression".to_string()),805                                original_node: serialize_expression(expr),806                                loc,807                            })808                        }809                    }810                }811                react_compiler_ast::operators::UnaryOperator::Throw => {812                    // throw as unary operator (Babel-specific)813                    let loc = convert_opt_loc(&unary.base.loc);814                    builder.record_error(CompilerErrorDetail {815                        reason: "throw expressions are not supported".to_string(),816                        category: ErrorCategory::Todo,817                        loc: loc.clone(),818                        description: None,819                        suggestions: None,820                    })?;821                    Ok(InstructionValue::UnsupportedNode {822                        node_type: Some("UnaryExpression".to_string()),823                        original_node: serialize_expression(expr),824                        loc,825                    })826                }827                op => {828                    let value = lower_expression_to_temporary(builder, &unary.argument)?;829                    let operator = convert_unary_operator(op);830                    Ok(InstructionValue::UnaryExpression {831                        operator,832                        value,833                        loc,834                    })835                }836            }837        }838        Expression::CallExpression(call) => {839            let loc = convert_opt_loc(&call.base.loc);840            // Check if callee is a MemberExpression => MethodCall841            if let Expression::MemberExpression(member) = call.callee.as_ref() {842                let lowered = lower_member_expression(builder, member)?;843                let property = lower_value_to_temporary(builder, lowered.value)?;844                let args = lower_arguments(builder, &call.arguments)?;845                Ok(InstructionValue::MethodCall {846                    receiver: lowered.object,847                    property,848                    args,849                    loc,850                })851            } else {852                let callee = lower_expression_to_temporary(builder, &call.callee)?;853                let args = lower_arguments(builder, &call.arguments)?;854                Ok(InstructionValue::CallExpression { callee, args, loc })855            }856        }857        Expression::MemberExpression(member) => {858            let lowered = lower_member_expression(builder, member)?;859            Ok(lowered.value)860        }861        Expression::OptionalCallExpression(opt_call) => {862            Ok(lower_optional_call_expression(builder, opt_call)?)863        }864        Expression::OptionalMemberExpression(opt_member) => {865            Ok(lower_optional_member_expression(builder, opt_member)?)866        }867        Expression::LogicalExpression(expr) => {868            let loc = convert_opt_loc(&expr.base.loc);869            let continuation_block = builder.reserve(builder.current_block_kind());870            let continuation_id = continuation_block.id;871            let test_block = builder.reserve(BlockKind::Value);872            let test_block_id = test_block.id;873            let place = build_temporary_place(builder, loc.clone());874            let left_loc = expression_loc(&expr.left);875            let left_place = build_temporary_place(builder, left_loc);876877            // Block for short-circuit case: store left value as result, goto continuation878            let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {879                lower_value_to_temporary(880                    builder,881                    InstructionValue::StoreLocal {882                        lvalue: LValue {883                            kind: InstructionKind::Const,884                            place: place.clone(),885                        },886                        value: left_place.clone(),887                        type_annotation: None,888                        loc: left_place.loc.clone(),889                    },890                )?;891                Ok(Terminal::Goto {892                    block: continuation_id,893                    variant: GotoVariant::Break,894                    id: EvaluationOrder(0),895                    loc: left_place.loc.clone(),896                })897            });898899            // Block for evaluating right side900            let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {901                let right = lower_expression_to_temporary(builder, &expr.right)?;902                let right_loc = right.loc.clone();903                lower_value_to_temporary(904                    builder,905                    InstructionValue::StoreLocal {906                        lvalue: LValue {907                            kind: InstructionKind::Const,908                            place: place.clone(),909                        },910                        value: right,911                        type_annotation: None,912                        loc: right_loc.clone(),913                    },914                )?;915                Ok(Terminal::Goto {916                    block: continuation_id,917                    variant: GotoVariant::Break,918                    id: EvaluationOrder(0),919                    loc: right_loc,920                })921            });922923            let hir_op = match expr.operator {924                react_compiler_ast::operators::LogicalOperator::And => LogicalOperator::And,925                react_compiler_ast::operators::LogicalOperator::Or => LogicalOperator::Or,926                react_compiler_ast::operators::LogicalOperator::NullishCoalescing => {927                    LogicalOperator::NullishCoalescing928                }929            };930931            builder.terminate_with_continuation(932                Terminal::Logical {933                    operator: hir_op,934                    test: test_block_id,935                    fallthrough: continuation_id,936                    id: EvaluationOrder(0),937                    loc: loc.clone(),938                },939                test_block,940            );941942            // Now in test block: lower left expression, copy to left_place943            let left_value = lower_expression_to_temporary(builder, &expr.left)?;944            builder.push(Instruction {945                id: EvaluationOrder(0),946                lvalue: left_place.clone(),947                value: InstructionValue::LoadLocal {948                    place: left_value,949                    loc: loc.clone(),950                },951                effects: None,952                loc: loc.clone(),953            });954955            builder.terminate_with_continuation(956                Terminal::Branch {957                    test: left_place,958                    consequent: consequent_block?,959                    alternate: alternate_block?,960                    fallthrough: continuation_id,961                    id: EvaluationOrder(0),962                    loc: loc.clone(),963                },964                continuation_block,965            );966967            Ok(InstructionValue::LoadLocal {968                place: place.clone(),969                loc: place.loc.clone(),970            })971        }972        Expression::UpdateExpression(update) => {973            let loc = convert_opt_loc(&update.base.loc);974            match update.argument.as_ref() {975                Expression::MemberExpression(member) => {976                    let binary_op = match &update.operator {977                        react_compiler_ast::operators::UpdateOperator::Increment => {978                            BinaryOperator::Add979                        }980                        react_compiler_ast::operators::UpdateOperator::Decrement => {981                            BinaryOperator::Subtract982                        }983                    };984                    // Use the member expression's loc (not the update expression's)985                    // to match TS behavior where the inner operations use leftExpr.node.loc986                    let member_loc = convert_opt_loc(&member.base.loc);987                    let lowered = lower_member_expression(builder, member)?;988                    let object = lowered.object;989                    let lowered_property = lowered.property;990                    let prev_value = lower_value_to_temporary(builder, lowered.value)?;991992                    let one = lower_value_to_temporary(993                        builder,994                        InstructionValue::Primitive {995                            value: PrimitiveValue::Number(FloatValue::new(1.0)),996                            loc: None,997                        },998                    )?;999                    let updated = lower_value_to_temporary(1000                        builder,1001                        InstructionValue::BinaryExpression {1002                            operator: binary_op,1003                            left: prev_value.clone(),1004                            right: one,1005                            loc: member_loc.clone(),1006                        },1007                    )?;10081009                    // Store back using the property from the lowered member expression.1010                    // For prefix, the result is the PropertyStore/ComputedStore lvalue1011                    // (matching TS which uses newValuePlace). For postfix, it's prev_value.1012                    let new_value_place = match lowered_property {1013                        MemberProperty::Literal(prop_literal) => lower_value_to_temporary(1014                            builder,1015                            InstructionValue::PropertyStore {1016                                object,1017                                property: prop_literal,1018                                value: updated.clone(),1019                                loc: member_loc,1020                            },1021                        )?,1022                        MemberProperty::Computed(prop_place) => lower_value_to_temporary(1023                            builder,1024                            InstructionValue::ComputedStore {1025                                object,1026                                property: prop_place,1027                                value: updated.clone(),1028                                loc: member_loc,1029                            },1030                        )?,1031                    };10321033                    // Return previous for postfix, newValuePlace for prefix1034                    let result_place = if update.prefix {1035                        new_value_place1036                    } else {1037                        prev_value1038                    };1039                    Ok(InstructionValue::LoadLocal {1040                        place: result_place.clone(),1041                        loc: result_place.loc.clone(),1042                    })1043                }1044                Expression::Identifier(ident) => {1045                    let start = ident.base.start.unwrap_or(0);1046                    let ident_loc = convert_opt_loc(&ident.base.loc);1047                    let binding = builder.resolve_identifier(1048                        &ident.name,1049                        start,1050                        ident_loc.clone(),1051                        ident.base.node_id,1052                    )?;1053                    match &binding {1054                        VariableBinding::Global { .. } => {1055                            builder.record_error(CompilerErrorDetail {1056                                category: ErrorCategory::Todo,1057                                reason: "UpdateExpression where argument is a global is not yet supported".to_string(),1058                                description: None,1059                                loc: loc.clone(),1060                                suggestions: None,1061                            })?;1062                            return Ok(InstructionValue::UnsupportedNode {1063                                node_type: Some("UpdateExpression".to_string()),1064                                original_node: serialize_expression(expr),1065                                loc,1066                            });1067                        }1068                        _ => {}1069                    }1070                    let identifier = match binding {1071                        VariableBinding::Identifier { identifier, .. } => identifier,1072                        _ => {1073                            builder.record_error(CompilerErrorDetail {1074                                category: ErrorCategory::Todo,1075                                reason: "(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global".to_string(),1076                                description: None,1077                                loc: loc.clone(),1078                                suggestions: None,1079                            })?;1080                            return Ok(InstructionValue::UnsupportedNode {1081                                node_type: Some("UpdateExpression".to_string()),1082                                original_node: serialize_expression(expr),1083                                loc,1084                            });1085                        }1086                    };1087                    let lvalue_place = Place {1088                        identifier,1089                        effect: Effect::Unknown,1090                        reactive: false,1091                        loc: ident_loc.clone(),1092                    };10931094                    // Load the current value1095                    let value = lower_identifier(1096                        builder,1097                        &ident.name,1098                        start,1099                        ident_loc,1100                        ident.base.node_id,1101                    )?;11021103                    let operation = convert_update_operator(&update.operator);1104                    let is_context =1105                        builder.is_context_identifier(&ident.name, start, ident.base.node_id);11061107                    if update.prefix {1108                        let value = if is_context {1109                            InstructionValue::PrefixUpdateContext {1110                                lvalue: lvalue_place,1111                                operation,1112                                value,1113                                loc,1114                            }1115                        } else {1116                            InstructionValue::PrefixUpdateLocal {1117                                lvalue: lvalue_place,1118                                operation,1119                                value,1120                                loc,1121                            }1122                        };1123                        Ok(value)1124                    } else {1125                        let value = if is_context {1126                            InstructionValue::PostfixUpdateContext {1127                                lvalue: lvalue_place,1128                                operation,1129                                value,1130                                loc,1131                            }1132                        } else {1133                            InstructionValue::PostfixUpdateLocal {1134                                lvalue: lvalue_place,1135                                operation,1136                                value,1137                                loc,1138                            }1139                        };1140                        Ok(value)1141                    }1142                }1143                _ => {1144                    builder.record_error(CompilerErrorDetail {1145                        category: ErrorCategory::Todo,1146                        reason: format!("UpdateExpression with unsupported argument type"),1147                        description: None,1148                        loc: loc.clone(),1149                        suggestions: None,1150                    })?;1151                    Ok(InstructionValue::UnsupportedNode {1152                        node_type: Some("UpdateExpression".to_string()),1153                        original_node: serialize_expression(expr),1154                        loc,1155                    })1156                }1157            }1158        }1159        Expression::ConditionalExpression(expr) => {1160            let loc = convert_opt_loc(&expr.base.loc);1161            let continuation_block = builder.reserve(builder.current_block_kind());1162            let continuation_id = continuation_block.id;1163            let test_block = builder.reserve(BlockKind::Value);1164            let test_block_id = test_block.id;1165            let place = build_temporary_place(builder, loc.clone());11661167            // Block for the consequent (test is truthy)1168            let consequent_ast_loc = expression_loc(&expr.consequent);1169            let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {1170                let consequent = lower_expression_to_temporary(builder, &expr.consequent)?;1171                lower_value_to_temporary(1172                    builder,1173                    InstructionValue::StoreLocal {1174                        lvalue: LValue {1175                            kind: InstructionKind::Const,1176                            place: place.clone(),1177                        },1178                        value: consequent,1179                        type_annotation: None,1180                        loc: loc.clone(),1181                    },1182                )?;1183                Ok(Terminal::Goto {1184                    block: continuation_id,1185                    variant: GotoVariant::Break,1186                    id: EvaluationOrder(0),1187                    loc: consequent_ast_loc,1188                })1189            });11901191            // Block for the alternate (test is falsy)1192            let alternate_ast_loc = expression_loc(&expr.alternate);1193            let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {1194                let alternate = lower_expression_to_temporary(builder, &expr.alternate)?;1195                lower_value_to_temporary(1196                    builder,1197                    InstructionValue::StoreLocal {1198                        lvalue: LValue {1199                            kind: InstructionKind::Const,1200                            place: place.clone(),1201                        },1202                        value: alternate,1203                        type_annotation: None,1204                        loc: loc.clone(),1205                    },1206                )?;1207                Ok(Terminal::Goto {1208                    block: continuation_id,1209                    variant: GotoVariant::Break,1210                    id: EvaluationOrder(0),1211                    loc: alternate_ast_loc,1212                })1213            });12141215            builder.terminate_with_continuation(1216                Terminal::Ternary {1217                    test: test_block_id,1218                    fallthrough: continuation_id,1219                    id: EvaluationOrder(0),1220                    loc: loc.clone(),1221                },1222                test_block,1223            );12241225            // Now in test block: lower test expression1226            let test_place = lower_expression_to_temporary(builder, &expr.test)?;1227            builder.terminate_with_continuation(1228                Terminal::Branch {1229                    test: test_place,1230                    consequent: consequent_block?,1231                    alternate: alternate_block?,1232                    fallthrough: continuation_id,1233                    id: EvaluationOrder(0),1234                    loc: loc.clone(),1235                },1236                continuation_block,1237            );12381239            Ok(InstructionValue::LoadLocal {1240                place: place.clone(),1241                loc: place.loc.clone(),1242            })1243        }1244        Expression::AssignmentExpression(expr) => {1245            use react_compiler_ast::operators::AssignmentOperator;1246            let loc = convert_opt_loc(&expr.base.loc);12471248            if matches!(expr.operator, AssignmentOperator::Assign) {1249                // Simple `=` assignment1250                match &*expr.left {1251                    react_compiler_ast::patterns::PatternLike::Identifier(ident) => {1252                        // Handle simple identifier assignment directly1253                        let start = ident.base.start.unwrap_or(0);1254                        let right = lower_expression_to_temporary(builder, &expr.right)?;1255                        let ident_loc = convert_opt_loc(&ident.base.loc);1256                        let binding = builder.resolve_identifier(1257                            &ident.name,1258                            start,1259                            ident_loc.clone(),1260                            ident.base.node_id,1261                        )?;1262                        match binding {1263                            VariableBinding::Identifier {1264                                identifier,1265                                binding_kind,1266                            } => {1267                                // Check for const reassignment1268                                if binding_kind == BindingKind::Const {1269                                    builder.record_error(CompilerErrorDetail {1270                                        reason: "Cannot reassign a `const` variable".to_string(),1271                                        category: ErrorCategory::Syntax,1272                                        loc: ident_loc.clone(),1273                                        description: Some(format!(1274                                            "`{}` is declared as const",1275                                            &ident.name1276                                        )),1277                                        suggestions: None,1278                                    })?;1279                                    return Ok(InstructionValue::UnsupportedNode {1280                                        node_type: Some("Identifier".to_string()),1281                                        original_node: serialize_expression(1282                                            &Expression::AssignmentExpression(expr.clone()),1283                                        ),1284                                        loc: ident_loc,1285                                    });1286                                }1287                                let place = Place {1288                                    identifier,1289                                    reactive: false,1290                                    effect: Effect::Unknown,1291                                    loc: ident_loc,1292                                };1293                                if builder.is_context_identifier(1294                                    &ident.name,1295                                    start,1296                                    ident.base.node_id,1297                                ) {1298                                    let temp = lower_value_to_temporary(1299                                        builder,1300                                        InstructionValue::StoreContext {1301                                            lvalue: LValue {1302                                                kind: InstructionKind::Reassign,1303                                                place: place.clone(),1304                                            },1305                                            value: right,1306                                            loc: place.loc.clone(),1307                                        },1308                                    )?;1309                                    Ok(InstructionValue::LoadLocal {1310                                        place: temp.clone(),1311                                        loc: temp.loc.clone(),1312                                    })1313                                } else {1314                                    let temp = lower_value_to_temporary(1315                                        builder,1316                                        InstructionValue::StoreLocal {1317                                            lvalue: LValue {1318                                                kind: InstructionKind::Reassign,1319                                                place: place.clone(),1320                                            },1321                                            value: right,1322                                            type_annotation: None,1323                                            loc: place.loc.clone(),1324                                        },1325                                    )?;1326                                    Ok(InstructionValue::LoadLocal {1327                                        place: temp.clone(),1328                                        loc: temp.loc.clone(),1329                                    })1330                                }1331                            }1332                            _ => {1333                                // Global or import assignment1334                                let name = ident.name.clone();1335                                let temp = lower_value_to_temporary(1336                                    builder,1337                                    InstructionValue::StoreGlobal {1338                                        name,1339                                        value: right,1340                                        loc: ident_loc,1341                                    },1342                                )?;1343                                Ok(InstructionValue::LoadLocal {1344                                    place: temp.clone(),1345                                    loc: temp.loc.clone(),1346                                })1347                            }1348                        }1349                    }1350                    react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {1351                        // Member expression assignment: a.b = value or a[b] = value1352                        let right = lower_expression_to_temporary(builder, &expr.right)?;1353                        let left_loc = convert_opt_loc(&member.base.loc);1354                        let object = lower_expression_to_temporary(builder, &member.object)?;1355                        let temp = if !member.computed1356                            || matches!(1357                                &*member.property,1358                                react_compiler_ast::expressions::Expression::NumericLiteral(_)1359                            ) {1360                            match &*member.property {1361                                react_compiler_ast::expressions::Expression::Identifier(1362                                    prop_id,1363                                ) => lower_value_to_temporary(1364                                    builder,1365                                    InstructionValue::PropertyStore {1366                                        object,1367                                        property: PropertyLiteral::String(prop_id.name.clone()),1368                                        value: right,1369                                        loc: left_loc,1370                                    },1371                                )?,1372                                react_compiler_ast::expressions::Expression::NumericLiteral(1373                                    num,1374                                ) => lower_value_to_temporary(1375                                    builder,1376                                    InstructionValue::PropertyStore {1377                                        object,1378                                        property: PropertyLiteral::Number(FloatValue::new(1379                                            num.precise_value(),1380                                        )),1381                                        value: right,1382                                        loc: left_loc,1383                                    },1384                                )?,1385                                _ => {1386                                    let prop =1387                                        lower_expression_to_temporary(builder, &member.property)?;1388                                    lower_value_to_temporary(1389                                        builder,1390                                        InstructionValue::ComputedStore {1391                                            object,1392                                            property: prop,1393                                            value: right,1394                                            loc: left_loc,1395                                        },1396                                    )?1397                                }1398                            }1399                        } else {1400                            let prop = lower_expression_to_temporary(builder, &member.property)?;1401                            lower_value_to_temporary(1402                                builder,1403                                InstructionValue::ComputedStore {1404                                    object,1405                                    property: prop,1406                                    value: right,1407                                    loc: left_loc,1408                                },1409                            )?1410                        };1411                        Ok(InstructionValue::LoadLocal {1412                            place: temp.clone(),1413                            loc: temp.loc.clone(),1414                        })1415                    }1416                    _ => {1417                        // Destructuring assignment1418                        let right = lower_expression_to_temporary(builder, &expr.right)?;1419                        let left_loc = pattern_like_hir_loc(&expr.left);1420                        let result = lower_assignment(1421                            builder,1422                            left_loc,1423                            InstructionKind::Reassign,1424                            &expr.left,1425                            right.clone(),1426                            AssignmentStyle::Destructure,1427                        )?;1428                        match result {1429                            Some(place) => Ok(InstructionValue::LoadLocal {1430                                place: place.clone(),1431                                loc: place.loc.clone(),1432                            }),1433                            None => Ok(InstructionValue::LoadLocal { place: right, loc }),1434                        }1435                    }1436                }1437            } else {1438                // Compound assignment operators1439                let binary_op = match expr.operator {1440                    AssignmentOperator::AddAssign => Some(BinaryOperator::Add),1441                    AssignmentOperator::SubAssign => Some(BinaryOperator::Subtract),1442                    AssignmentOperator::MulAssign => Some(BinaryOperator::Multiply),1443                    AssignmentOperator::DivAssign => Some(BinaryOperator::Divide),1444                    AssignmentOperator::RemAssign => Some(BinaryOperator::Modulo),1445                    AssignmentOperator::ExpAssign => Some(BinaryOperator::Exponent),1446                    AssignmentOperator::ShlAssign => Some(BinaryOperator::ShiftLeft),1447                    AssignmentOperator::ShrAssign => Some(BinaryOperator::ShiftRight),1448                    AssignmentOperator::UShrAssign => Some(BinaryOperator::UnsignedShiftRight),1449                    AssignmentOperator::BitOrAssign => Some(BinaryOperator::BitwiseOr),1450                    AssignmentOperator::BitXorAssign => Some(BinaryOperator::BitwiseXor),1451                    AssignmentOperator::BitAndAssign => Some(BinaryOperator::BitwiseAnd),1452                    AssignmentOperator::OrAssign1453                    | AssignmentOperator::AndAssign1454                    | AssignmentOperator::NullishAssign => {1455                        // Logical assignment operators (||=, &&=, ??=) - not yet supported1456                        builder.record_error(CompilerErrorDetail {1457                            reason:1458                                "Logical assignment operators (||=, &&=, ??=) are not yet supported"1459                                    .to_string(),1460                            category: ErrorCategory::Todo,1461                            loc: loc.clone(),1462                            description: None,1463                            suggestions: None,1464                        })?;1465                        return Ok(InstructionValue::UnsupportedNode {1466                            node_type: Some("AssignmentExpression".to_string()),1467                            original_node: serialize_expression(&Expression::AssignmentExpression(1468                                expr.clone(),1469                            )),1470                            loc,1471                        });1472                    }1473                    AssignmentOperator::Assign => unreachable!(),1474                };1475                let binary_op = match binary_op {1476                    Some(op) => op,1477                    None => {1478                        return Ok(InstructionValue::UnsupportedNode {1479                            node_type: Some("AssignmentExpression".to_string()),1480                            original_node: serialize_expression(&Expression::AssignmentExpression(1481                                expr.clone(),1482                            )),1483                            loc,1484                        });1485                    }1486                };14871488                match &*expr.left {1489                    react_compiler_ast::patterns::PatternLike::Identifier(ident) => {1490                        let start = ident.base.start.unwrap_or(0);1491                        let left_place = lower_expression_to_temporary(1492                            builder,1493                            &react_compiler_ast::expressions::Expression::Identifier(ident.clone()),1494                        )?;1495                        let right = lower_expression_to_temporary(builder, &expr.right)?;1496                        let binary_place = lower_value_to_temporary(1497                            builder,1498                            InstructionValue::BinaryExpression {1499                                operator: binary_op,1500                                left: left_place,1501                                right,1502                                loc: loc.clone(),1503                            },1504                        )?;1505                        let ident_loc = convert_opt_loc(&ident.base.loc);1506                        let binding = builder.resolve_identifier(1507                            &ident.name,1508                            start,1509                            ident_loc.clone(),1510                            ident.base.node_id,1511                        )?;1512                        match binding {1513                            VariableBinding::Identifier { identifier, .. } => {1514                                let place = Place {1515                                    identifier,1516                                    reactive: false,1517                                    effect: Effect::Unknown,1518                                    loc: ident_loc,1519                                };1520                                if builder.is_context_identifier(1521                                    &ident.name,1522                                    start,1523                                    ident.base.node_id,1524                                ) {1525                                    lower_value_to_temporary(1526                                        builder,1527                                        InstructionValue::StoreContext {1528                                            lvalue: LValue {1529                                                kind: InstructionKind::Reassign,1530                                                place: place.clone(),1531                                            },1532                                            value: binary_place,1533                                            loc: loc.clone(),1534                                        },1535                                    )?;1536                                    Ok(InstructionValue::LoadContext { place, loc })1537                                } else {1538                                    lower_value_to_temporary(1539                                        builder,1540                                        InstructionValue::StoreLocal {1541                                            lvalue: LValue {1542                                                kind: InstructionKind::Reassign,1543                                                place: place.clone(),1544                                            },1545                                            value: binary_place,1546                                            type_annotation: None,1547                                            loc: loc.clone(),1548                                        },1549                                    )?;1550                                    Ok(InstructionValue::LoadLocal { place, loc })1551                                }1552                            }1553                            _ => {1554                                // Global assignment1555                                let name = ident.name.clone();1556                                let temp = lower_value_to_temporary(1557                                    builder,1558                                    InstructionValue::StoreGlobal {1559                                        name,1560                                        value: binary_place,1561                                        loc: loc.clone(),1562                                    },1563                                )?;1564                                Ok(InstructionValue::LoadLocal {1565                                    place: temp.clone(),1566                                    loc: temp.loc.clone(),1567                                })1568                            }1569                        }1570                    }1571                    react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {1572                        // a.b += right: read, compute, store1573                        // Match TS behavior: return the PropertyStore/ComputedStore value1574                        // directly (let the caller lower it to a temporary)1575                        let member_loc = convert_opt_loc(&member.base.loc);1576                        let lowered = lower_member_expression(builder, member)?;1577                        let object = lowered.object;1578                        let lowered_property = lowered.property;1579                        let current_value = lower_value_to_temporary(builder, lowered.value)?;1580                        let right = lower_expression_to_temporary(builder, &expr.right)?;1581                        let result = lower_value_to_temporary(1582                            builder,1583                            InstructionValue::BinaryExpression {1584                                operator: binary_op,1585                                left: current_value,1586                                right,1587                                loc: member_loc.clone(),1588                            },1589                        )?;1590                        // Return the store instruction value directly (matching TS behavior)1591                        match lowered_property {1592                            MemberProperty::Literal(prop_literal) => {1593                                Ok(InstructionValue::PropertyStore {1594                                    object,1595                                    property: prop_literal,1596                                    value: result,1597                                    loc: member_loc,1598                                })1599                            }1600                            MemberProperty::Computed(prop_place) => {1601                                Ok(InstructionValue::ComputedStore {1602                                    object,1603                                    property: prop_place,1604                                    value: result,1605                                    loc: member_loc,1606                                })1607                            }1608                        }1609                    }1610                    _ => {1611                        builder.record_error(CompilerErrorDetail {1612                            reason: "Compound assignment to complex pattern is not yet supported"1613                                .to_string(),1614                            category: ErrorCategory::Todo,1615                            loc: loc.clone(),1616                            description: None,1617                            suggestions: None,1618                        })?;1619                        Ok(InstructionValue::UnsupportedNode {1620                            node_type: Some("AssignmentExpression".to_string()),1621                            original_node: serialize_expression(&Expression::AssignmentExpression(1622                                expr.clone(),1623                            )),1624                            loc,1625                        })1626                    }1627                }1628            }1629        }1630        Expression::SequenceExpression(seq) => {1631            let loc = convert_opt_loc(&seq.base.loc);16321633            if seq.expressions.is_empty() {1634                builder.record_error(CompilerErrorDetail {1635                    category: ErrorCategory::Syntax,1636                    reason: "Expected sequence expression to have at least one expression"1637                        .to_string(),1638                    description: None,1639                    loc: loc.clone(),1640                    suggestions: None,1641                })?;1642                return Ok(InstructionValue::UnsupportedNode {1643                    node_type: Some("SequenceExpression".to_string()),1644                    original_node: serialize_expression(expr),1645                    loc,1646                });1647            }16481649            let continuation_block = builder.reserve(builder.current_block_kind());1650            let continuation_id = continuation_block.id;1651            let place = build_temporary_place(builder, loc.clone());16521653            let sequence_block = builder.try_enter(BlockKind::Sequence, |builder, _block_id| {1654                let mut last: Option<Place> = None;1655                for item in &seq.expressions {1656                    last = Some(lower_expression_to_temporary(builder, item)?);1657                }1658                if let Some(last) = last {1659                    lower_value_to_temporary(1660                        builder,1661                        InstructionValue::StoreLocal {1662                            lvalue: LValue {1663                                kind: InstructionKind::Const,1664                                place: place.clone(),1665                            },1666                            value: last,1667                            type_annotation: None,1668                            loc: loc.clone(),1669                        },1670                    )?;1671                }1672                Ok(Terminal::Goto {1673                    block: continuation_id,1674                    variant: GotoVariant::Break,1675                    id: EvaluationOrder(0),1676                    loc: loc.clone(),1677                })1678            });16791680            builder.terminate_with_continuation(1681                Terminal::Sequence {1682                    block: sequence_block?,1683                    fallthrough: continuation_id,1684                    id: EvaluationOrder(0),1685                    loc: loc.clone(),1686                },1687                continuation_block,1688            );1689            Ok(InstructionValue::LoadLocal { place, loc })1690        }1691        Expression::ArrowFunctionExpression(_) => Ok(lower_function_to_value(1692            builder,1693            expr,1694            FunctionExpressionType::ArrowFunctionExpression,1695        )?),1696        Expression::FunctionExpression(_) => Ok(lower_function_to_value(1697            builder,1698            expr,1699            FunctionExpressionType::FunctionExpression,1700        )?),1701        Expression::ObjectExpression(obj) => {1702            let loc = convert_opt_loc(&obj.base.loc);1703            let mut properties: Vec<ObjectPropertyOrSpread> = Vec::new();1704            for prop in &obj.properties {1705                match prop {1706                    react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(1707                        p,1708                    ) => {1709                        let key = lower_object_property_key(builder, &p.key, p.computed)?;1710                        let key = match key {1711                            Some(k) => k,1712                            None => continue,1713                        };1714                        let value = lower_expression_to_temporary(builder, &p.value)?;1715                        properties.push(ObjectPropertyOrSpread::Property(ObjectProperty {1716                            key,1717                            property_type: ObjectPropertyType::Property,1718                            place: value,1719                        }));1720                    }1721                    react_compiler_ast::expressions::ObjectExpressionProperty::SpreadElement(1722                        spread,1723                    ) => {1724                        let place = lower_expression_to_temporary(builder, &spread.argument)?;1725                        properties.push(ObjectPropertyOrSpread::Spread(SpreadPattern { place }));1726                    }1727                    react_compiler_ast::expressions::ObjectExpressionProperty::ObjectMethod(1728                        method,1729                    ) => {1730                        if let Some(prop) = lower_object_method(builder, method)? {1731                            properties.push(ObjectPropertyOrSpread::Property(prop));1732                        }1733                    }1734                }1735            }1736            Ok(InstructionValue::ObjectExpression { properties, loc })1737        }1738        Expression::ArrayExpression(arr) => {1739            let loc = convert_opt_loc(&arr.base.loc);1740            let mut elements: Vec<ArrayElement> = Vec::new();1741            for element in &arr.elements {1742                match element {1743                    None => {1744                        elements.push(ArrayElement::Hole);1745                    }1746                    Some(Expression::SpreadElement(spread)) => {1747                        let place = lower_expression_to_temporary(builder, &spread.argument)?;1748                        elements.push(ArrayElement::Spread(SpreadPattern { place }));1749                    }1750                    Some(expr) => {1751                        let place = lower_expression_to_temporary(builder, expr)?;1752                        elements.push(ArrayElement::Place(place));1753                    }1754                }1755            }1756            Ok(InstructionValue::ArrayExpression { elements, loc })1757        }1758        Expression::NewExpression(new_expr) => {1759            let loc = convert_opt_loc(&new_expr.base.loc);1760            let callee = lower_expression_to_temporary(builder, &new_expr.callee)?;1761            let args = lower_arguments(builder, &new_expr.arguments)?;1762            Ok(InstructionValue::NewExpression { callee, args, loc })1763        }1764        Expression::TemplateLiteral(tmpl) => {1765            let loc = convert_opt_loc(&tmpl.base.loc);1766            let subexprs: Vec<Place> = tmpl1767                .expressions1768                .iter()1769                .map(|e| lower_expression_to_temporary(builder, e))1770                .collect::<Result<Vec<_>, _>>()?;1771            let quasis: Vec<TemplateQuasi> = tmpl1772                .quasis1773                .iter()1774                .map(|q| TemplateQuasi {1775                    raw: q.value.raw.clone(),1776                    cooked: q.value.cooked.clone(),1777                })1778                .collect();1779            Ok(InstructionValue::TemplateLiteral {1780                subexprs,1781                quasis,1782                loc,1783            })1784        }1785        Expression::TaggedTemplateExpression(tagged) => {1786            let loc = convert_opt_loc(&tagged.base.loc);1787            if !tagged.quasi.expressions.is_empty() {1788                builder.record_error(CompilerErrorDetail {1789                    category: ErrorCategory::Todo,1790                    reason:1791                        "(BuildHIR::lowerExpression) Handle tagged template with interpolations"1792                            .to_string(),1793                    description: None,1794                    loc: loc.clone(),1795                    suggestions: None,1796                })?;1797                return Ok(InstructionValue::UnsupportedNode {1798                    node_type: Some("TaggedTemplateExpression".to_string()),1799                    original_node: serialize_expression(expr),1800                    loc,1801                });1802            }1803            assert!(1804                tagged.quasi.quasis.len() == 1,1805                "there should be only one quasi as we don't support interpolations yet"1806            );1807            let quasi = &tagged.quasi.quasis[0];1808            // Check if raw and cooked values differ (e.g., graphql tagged templates)1809            if quasi.value.raw != quasi.value.cooked.clone().unwrap_or_default() {1810                builder.record_error(CompilerErrorDetail {1811                    category: ErrorCategory::Todo,1812                    reason: "(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value".to_string(),1813                    description: None,1814                    loc: loc.clone(),1815                    suggestions: None,1816                })?;1817                return Ok(InstructionValue::UnsupportedNode {1818                    node_type: Some("TaggedTemplateExpression".to_string()),1819                    original_node: serialize_expression(expr),1820                    loc,1821                });1822            }1823            let value = TemplateQuasi {1824                raw: quasi.value.raw.clone(),1825                cooked: quasi.value.cooked.clone(),1826            };1827            let tag = lower_expression_to_temporary(builder, &tagged.tag)?;1828            Ok(InstructionValue::TaggedTemplateExpression { tag, value, loc })1829        }1830        Expression::AwaitExpression(await_expr) => {1831            let loc = convert_opt_loc(&await_expr.base.loc);1832            let value = lower_expression_to_temporary(builder, &await_expr.argument)?;1833            Ok(InstructionValue::Await { value, loc })1834        }1835        Expression::YieldExpression(yld) => {1836            let loc = convert_opt_loc(&yld.base.loc);1837            builder.record_error(CompilerErrorDetail {1838                category: ErrorCategory::Todo,1839                reason: "(BuildHIR::lowerExpression) Handle YieldExpression expressions"1840                    .to_string(),1841                description: None,1842                loc: loc.clone(),1843                suggestions: None,1844            })?;1845            Ok(InstructionValue::UnsupportedNode {1846                node_type: Some("YieldExpression".to_string()),1847                original_node: serialize_expression(expr),1848                loc,1849            })1850        }1851        Expression::SpreadElement(spread) => {1852            // SpreadElement should be handled by the parent context (array/object/call)1853            // If we reach here, just lower the argument expression1854            Ok(lower_expression(builder, &spread.argument)?)1855        }1856        Expression::MetaProperty(meta) => {1857            let loc = convert_opt_loc(&meta.base.loc);1858            if meta.meta.name == "import" && meta.property.name == "meta" {1859                Ok(InstructionValue::MetaProperty {1860                    meta: meta.meta.name.clone(),1861                    property: meta.property.name.clone(),1862                    loc,1863                })1864            } else {1865                builder.record_error(CompilerErrorDetail {1866                    category: ErrorCategory::Todo,1867                    reason: "(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta".to_string(),1868                    description: None,1869                    loc: loc.clone(),1870                    suggestions: None,1871                })?;1872                Ok(InstructionValue::UnsupportedNode {1873                    node_type: Some("MetaProperty".to_string()),1874                    original_node: serialize_expression(expr),1875                    loc,1876                })1877            }1878        }1879        Expression::ClassExpression(cls) => {1880            let loc = convert_opt_loc(&cls.base.loc);1881            builder.record_error(CompilerErrorDetail {1882                category: ErrorCategory::Todo,1883                reason: "(BuildHIR::lowerExpression) Handle ClassExpression expressions"1884                    .to_string(),1885                description: None,1886                loc: loc.clone(),1887                suggestions: None,1888            })?;1889            Ok(InstructionValue::UnsupportedNode {1890                node_type: Some("ClassExpression".to_string()),1891                original_node: serialize_expression(expr),1892                loc,1893            })1894        }1895        Expression::PrivateName(pn) => {1896            let loc = convert_opt_loc(&pn.base.loc);1897            builder.record_error(CompilerErrorDetail {1898                category: ErrorCategory::Todo,1899                reason: "(BuildHIR::lowerExpression) Handle PrivateName expressions".to_string(),1900                description: None,1901                loc: loc.clone(),1902                suggestions: None,1903            })?;1904            Ok(InstructionValue::UnsupportedNode {1905                node_type: Some("PrivateName".to_string()),1906                original_node: serialize_expression(expr),1907                loc,1908            })1909        }1910        Expression::Super(sup) => {1911            let loc = convert_opt_loc(&sup.base.loc);1912            builder.record_error(CompilerErrorDetail {1913                category: ErrorCategory::Todo,1914                reason: "(BuildHIR::lowerExpression) Handle Super expressions".to_string(),1915                description: None,1916                loc: loc.clone(),1917                suggestions: None,1918            })?;1919            Ok(InstructionValue::UnsupportedNode {1920                node_type: Some("Super".to_string()),1921                original_node: serialize_expression(expr),1922                loc,1923            })1924        }1925        Expression::Import(imp) => {1926            let loc = convert_opt_loc(&imp.base.loc);1927            builder.record_error(CompilerErrorDetail {1928                category: ErrorCategory::Todo,1929                reason: "(BuildHIR::lowerExpression) Handle Import expressions".to_string(),1930                description: None,1931                loc: loc.clone(),1932                suggestions: None,1933            })?;1934            Ok(InstructionValue::UnsupportedNode {1935                node_type: Some("Import".to_string()),1936                original_node: serialize_expression(expr),1937                loc,1938            })1939        }1940        Expression::ThisExpression(this) => {1941            let loc = convert_opt_loc(&this.base.loc);1942            builder.record_error(CompilerErrorDetail {1943                category: ErrorCategory::Todo,1944                reason: "(BuildHIR::lowerExpression) Handle ThisExpression expressions".to_string(),1945                description: None,1946                loc: loc.clone(),1947                suggestions: None,1948            })?;1949            Ok(InstructionValue::UnsupportedNode {1950                node_type: Some("ThisExpression".to_string()),1951                original_node: serialize_expression(expr),1952                loc,1953            })1954        }1955        Expression::ParenthesizedExpression(paren) => {1956            Ok(lower_expression(builder, &paren.expression)?)1957        }1958        Expression::JSXElement(jsx_element) => {1959            let loc = convert_opt_loc(&jsx_element.base.loc);1960            let opening_loc = convert_opt_loc(&jsx_element.opening_element.base.loc);1961            let closing_loc = jsx_element1962                .closing_element1963                .as_ref()1964                .and_then(|c| convert_opt_loc(&c.base.loc));19651966            // Lower the tag name1967            let tag = lower_jsx_element_name(builder, &jsx_element.opening_element.name)?;19681969            // Lower attributes (props)1970            let mut props: Vec<JsxAttribute> = Vec::new();1971            for attr_item in &jsx_element.opening_element.attributes {1972                use react_compiler_ast::jsx::JSXAttributeItem;1973                use react_compiler_ast::jsx::JSXAttributeName;1974                use react_compiler_ast::jsx::JSXAttributeValue;1975                match attr_item {1976                    JSXAttributeItem::JSXSpreadAttribute(spread) => {1977                        let argument = lower_expression_to_temporary(builder, &spread.argument)?;1978                        props.push(JsxAttribute::SpreadAttribute { argument });1979                    }1980                    JSXAttributeItem::JSXAttribute(attr) => {1981                        // Get the attribute name1982                        let prop_name = match &attr.name {1983                            JSXAttributeName::JSXIdentifier(id) => {1984                                let name = &id.name;1985                                if name.contains(':') {1986                                    builder.record_error(CompilerErrorDetail {1987                                        category: ErrorCategory::Todo,1988                                        reason: format!(1989                                            "(BuildHIR::lowerExpression) Unexpected colon in attribute name `{}`",1990                                            name1991                                        ),1992                                        description: None,1993                                        loc: convert_opt_loc(&id.base.loc),1994                                        suggestions: None,1995                                    })?;1996                                }1997                                name.clone()1998                            }1999                            JSXAttributeName::JSXNamespacedName(ns) => {2000                                format!("{}:{}", ns.namespace.name, ns.name.name)

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.

Generated by searchcode.com · powered by code_get_file + code_get_findings