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