1use std::collections::HashSet;
2
3use indexmap::IndexMap;
4use indexmap::IndexSet;
5use react_compiler_ast::scope::BindingId;
6use react_compiler_ast::scope::BindingKind as AstBindingKind;
7use react_compiler_ast::scope::ScopeId;
8use react_compiler_ast::scope::ScopeInfo;
9use react_compiler_ast::scope::ScopeKind;
10use react_compiler_diagnostics::CompilerDiagnostic;
11use react_compiler_diagnostics::CompilerDiagnosticDetail;
12use react_compiler_diagnostics::CompilerError;
13use react_compiler_diagnostics::CompilerErrorDetail;
14use react_compiler_diagnostics::ErrorCategory;
15use react_compiler_hir::environment::Environment;
16use react_compiler_hir::*;
17
18use crate::FunctionNode;
19use crate::find_context_identifiers::find_context_identifiers;
20use crate::hir_builder::HirBuilder;
21use crate::hir_builder::is_always_reserved_word;
22use crate::hir_builder::reserved_identifier_diagnostic;
23use crate::identifier_loc_index::IdentifierLocIndex;
24use crate::identifier_loc_index::build_identifier_loc_index;
25
26fn convert_loc(loc: &react_compiler_ast::common::SourceLocation) -> SourceLocation {
32 SourceLocation {
33 start: Position {
34 line: loc.start.line,
35 column: loc.start.column,
36 index: loc.start.index,
37 },
38 end: Position {
39 line: loc.end.line,
40 column: loc.end.column,
41 index: loc.end.index,
42 },
43 }
44}
45
46fn convert_opt_loc(
48 loc: &Option<react_compiler_ast::common::SourceLocation>,
49) -> Option<SourceLocation> {
50 loc.as_ref().map(convert_loc)
51}
52
53fn serialize_expression(
58 expr: &react_compiler_ast::expressions::Expression,
59) -> Option<serde_json::Value> {
60 serde_json::to_value(expr).ok()
61}
62
63fn serialize_statement(
65 stmt: &react_compiler_ast::statements::Statement,
66) -> Option<serde_json::Value> {
67 serde_json::to_value(stmt).ok()
68}
69
70fn serialize_pattern(pat: &react_compiler_ast::patterns::PatternLike) -> Option<serde_json::Value> {
72 serde_json::to_value(pat).ok()
73}
74
75fn pattern_like_loc(
76 pattern: &react_compiler_ast::patterns::PatternLike,
77) -> Option<react_compiler_ast::common::SourceLocation> {
78 use react_compiler_ast::patterns::PatternLike;
79 match pattern {
80 PatternLike::Identifier(id) => id.base.loc.clone(),
81 PatternLike::ObjectPattern(p) => p.base.loc.clone(),
82 PatternLike::ArrayPattern(p) => p.base.loc.clone(),
83 PatternLike::AssignmentPattern(p) => p.base.loc.clone(),
84 PatternLike::RestElement(p) => p.base.loc.clone(),
85 PatternLike::MemberExpression(p) => p.base.loc.clone(),
86 PatternLike::TSAsExpression(p) => p.base.loc.clone(),
87 PatternLike::TSSatisfiesExpression(p) => p.base.loc.clone(),
88 PatternLike::TSNonNullExpression(p) => p.base.loc.clone(),
89 PatternLike::TSTypeAssertion(p) => p.base.loc.clone(),
90 PatternLike::TypeCastExpression(p) => p.base.loc.clone(),
91 }
92}
93
94fn expression_loc(expr: &react_compiler_ast::expressions::Expression) -> Option<SourceLocation> {
96 use react_compiler_ast::expressions::Expression;
97 let loc = match expr {
98 Expression::Identifier(e) => e.base.loc.clone(),
99 Expression::StringLiteral(e) => e.base.loc.clone(),
100 Expression::NumericLiteral(e) => e.base.loc.clone(),
101 Expression::BooleanLiteral(e) => e.base.loc.clone(),
102 Expression::NullLiteral(e) => e.base.loc.clone(),
103 Expression::BigIntLiteral(e) => e.base.loc.clone(),
104 Expression::RegExpLiteral(e) => e.base.loc.clone(),
105 Expression::CallExpression(e) => e.base.loc.clone(),
106 Expression::MemberExpression(e) => e.base.loc.clone(),
107 Expression::OptionalCallExpression(e) => e.base.loc.clone(),
108 Expression::OptionalMemberExpression(e) => e.base.loc.clone(),
109 Expression::BinaryExpression(e) => e.base.loc.clone(),
110 Expression::LogicalExpression(e) => e.base.loc.clone(),
111 Expression::UnaryExpression(e) => e.base.loc.clone(),
112 Expression::UpdateExpression(e) => e.base.loc.clone(),
113 Expression::ConditionalExpression(e) => e.base.loc.clone(),
114 Expression::AssignmentExpression(e) => e.base.loc.clone(),
115 Expression::SequenceExpression(e) => e.base.loc.clone(),
116 Expression::ArrowFunctionExpression(e) => e.base.loc.clone(),
117 Expression::FunctionExpression(e) => e.base.loc.clone(),
118 Expression::ObjectExpression(e) => e.base.loc.clone(),
119 Expression::ArrayExpression(e) => e.base.loc.clone(),
120 Expression::NewExpression(e) => e.base.loc.clone(),
121 Expression::TemplateLiteral(e) => e.base.loc.clone(),
122 Expression::TaggedTemplateExpression(e) => e.base.loc.clone(),
123 Expression::AwaitExpression(e) => e.base.loc.clone(),
124 Expression::YieldExpression(e) => e.base.loc.clone(),
125 Expression::SpreadElement(e) => e.base.loc.clone(),
126 Expression::MetaProperty(e) => e.base.loc.clone(),
127 Expression::ClassExpression(e) => e.base.loc.clone(),
128 Expression::PrivateName(e) => e.base.loc.clone(),
129 Expression::Super(e) => e.base.loc.clone(),
130 Expression::Import(e) => e.base.loc.clone(),
131 Expression::ThisExpression(e) => e.base.loc.clone(),
132 Expression::ParenthesizedExpression(e) => e.base.loc.clone(),
133 Expression::JSXElement(e) => e.base.loc.clone(),
134 Expression::JSXFragment(e) => e.base.loc.clone(),
135 Expression::AssignmentPattern(e) => e.base.loc.clone(),
136 Expression::TSAsExpression(e) => e.base.loc.clone(),
137 Expression::TSSatisfiesExpression(e) => e.base.loc.clone(),
138 Expression::TSNonNullExpression(e) => e.base.loc.clone(),
139 Expression::TSTypeAssertion(e) => e.base.loc.clone(),
140 Expression::TSInstantiationExpression(e) => e.base.loc.clone(),
141 Expression::TypeCastExpression(e) => e.base.loc.clone(),
142 };
143 convert_opt_loc(&loc)
144}
145
146fn validate_ts_this_parameter(
147 scope_info: &ScopeInfo,
148 function_scope: ScopeId,
149) -> Result<(), CompilerError> {
150 let Some(scope) = scope_info.scopes.get(function_scope.0 as usize) else {
151 return Ok(());
152 };
153 let Some(binding_id) = scope.bindings.get("this") else {
154 return Ok(());
155 };
156 let Some(binding) = scope_info.bindings.get(binding_id.0 as usize) else {
157 return Ok(());
158 };
159 if matches!(binding.kind, AstBindingKind::Param) {
160 return Err(CompilerError::from(reserved_identifier_diagnostic("this")));
161 }
162 Ok(())
163}
164
165fn is_class_scope_descendant(scope_info: &ScopeInfo, mut scope_id: ScopeId) -> bool {
166 while let Some(scope) = scope_info.scopes.get(scope_id.0 as usize) {
167 let Some(parent) = scope.parent else {
168 return false;
169 };
170 let Some(parent_scope) = scope_info.scopes.get(parent.0 as usize) else {
171 return false;
172 };
173 if matches!(parent_scope.kind, ScopeKind::Class) {
174 return true;
175 }
176 scope_id = parent;
177 }
178 false
179}
180
181fn validate_ts_this_parameters_in_function_range(
182 scope_info: &ScopeInfo,
183 start: u32,
184 end: u32,
185) -> Result<(), CompilerError> {
186 if start >= end {
187 return Ok(());
188 }
189 for (node_start, scope_id) in &scope_info.node_to_scope {
190 if *node_start < start || *node_start >= end {
191 continue;
192 }
193 let Some(scope) = scope_info.scopes.get(scope_id.0 as usize) else {
194 continue;
195 };
196 if !matches!(scope.kind, ScopeKind::Function)
197 || is_class_scope_descendant(scope_info, *scope_id)
198 {
199 continue;
200 }
201 validate_ts_this_parameter(scope_info, *scope_id)?;
202 }
203 Ok(())
204}
205
206fn expression_type_name(expr: &react_compiler_ast::expressions::Expression) -> &'static str {
208 use react_compiler_ast::expressions::Expression;
209 match expr {
210 Expression::Identifier(_) => "Identifier",
211 Expression::StringLiteral(_) => "StringLiteral",
212 Expression::NumericLiteral(_) => "NumericLiteral",
213 Expression::BooleanLiteral(_) => "BooleanLiteral",
214 Expression::NullLiteral(_) => "NullLiteral",
215 Expression::BigIntLiteral(_) => "BigIntLiteral",
216 Expression::RegExpLiteral(_) => "RegExpLiteral",
217 Expression::CallExpression(_) => "CallExpression",
218 Expression::MemberExpression(_) => "MemberExpression",
219 Expression::OptionalCallExpression(_) => "OptionalCallExpression",
220 Expression::OptionalMemberExpression(_) => "OptionalMemberExpression",
221 Expression::BinaryExpression(_) => "BinaryExpression",
222 Expression::LogicalExpression(_) => "LogicalExpression",
223 Expression::UnaryExpression(_) => "UnaryExpression",
224 Expression::UpdateExpression(_) => "UpdateExpression",
225 Expression::ConditionalExpression(_) => "ConditionalExpression",
226 Expression::AssignmentExpression(_) => "AssignmentExpression",
227 Expression::SequenceExpression(_) => "SequenceExpression",
228 Expression::ArrowFunctionExpression(_) => "ArrowFunctionExpression",
229 Expression::FunctionExpression(_) => "FunctionExpression",
230 Expression::ObjectExpression(_) => "ObjectExpression",
231 Expression::ArrayExpression(_) => "ArrayExpression",
232 Expression::NewExpression(_) => "NewExpression",
233 Expression::TemplateLiteral(_) => "TemplateLiteral",
234 Expression::TaggedTemplateExpression(_) => "TaggedTemplateExpression",
235 Expression::AwaitExpression(_) => "AwaitExpression",
236 Expression::YieldExpression(_) => "YieldExpression",
237 Expression::SpreadElement(_) => "SpreadElement",
238 Expression::MetaProperty(_) => "MetaProperty",
239 Expression::ClassExpression(_) => "ClassExpression",
240 Expression::PrivateName(_) => "PrivateName",
241 Expression::Super(_) => "Super",
242 Expression::Import(_) => "Import",
243 Expression::ThisExpression(_) => "ThisExpression",
244 Expression::ParenthesizedExpression(_) => "ParenthesizedExpression",
245 Expression::JSXElement(_) => "JSXElement",
246 Expression::JSXFragment(_) => "JSXFragment",
247 Expression::AssignmentPattern(_) => "AssignmentPattern",
248 Expression::TSAsExpression(_) => "TSAsExpression",
249 Expression::TSSatisfiesExpression(_) => "TSSatisfiesExpression",
250 Expression::TSNonNullExpression(_) => "TSNonNullExpression",
251 Expression::TSTypeAssertion(_) => "TSTypeAssertion",
252 Expression::TSInstantiationExpression(_) => "TSInstantiationExpression",
253 Expression::TypeCastExpression(_) => "TypeCastExpression",
254 }
255}
256
257fn extract_type_annotation_name(
263 type_annotation: &Option<Box<serde_json::Value>>,
264) -> Option<String> {
265 let val = type_annotation.as_ref()?;
266 let inner = val.get("typeAnnotation")?;
268 let type_name = inner.get("type")?.as_str()?;
269 Some(type_name.to_string())
270}
271
272fn build_temporary_place(builder: &mut HirBuilder, loc: Option<SourceLocation>) -> Place {
277 let id = builder.make_temporary(loc.clone());
278 Place {
279 identifier: id,
280 reactive: false,
281 effect: Effect::Unknown,
282 loc,
283 }
284}
285
286fn promote_temporary(builder: &mut HirBuilder, identifier_id: IdentifierId) {
289 let env = builder.environment_mut();
290 let decl_id = env.identifiers[identifier_id.0 as usize].declaration_id;
291 env.identifiers[identifier_id.0 as usize].name =
292 Some(IdentifierName::Promoted(format!("#t{}", decl_id.0)));
293}
294
295fn lower_value_to_temporary(
296 builder: &mut HirBuilder,
297 value: InstructionValue,
298) -> Result<Place, CompilerError> {
299 if let InstructionValue::LoadLocal { ref place, .. } = value {
301 let ident = &builder.environment().identifiers[place.identifier.0 as usize];
302 if ident.name.is_none() {
303 return Ok(place.clone());
304 }
305 }
306 let loc = value.loc().cloned();
307 let place = build_temporary_place(builder, loc.clone());
308 builder.push(Instruction {
309 id: EvaluationOrder(0),
310 lvalue: place.clone(),
311 value,
312 loc,
313 effects: None,
314 });
315 Ok(place)
316}
317
318fn lower_expression_to_temporary(
319 builder: &mut HirBuilder,
320 expr: &react_compiler_ast::expressions::Expression,
321) -> Result<Place, CompilerError> {
322 let value = lower_expression(builder, expr)?;
323 Ok(lower_value_to_temporary(builder, value)?)
324}
325
326fn convert_binary_operator(op: &react_compiler_ast::operators::BinaryOperator) -> BinaryOperator {
331 use react_compiler_ast::operators::BinaryOperator as AstOp;
332 match op {
333 AstOp::Add => BinaryOperator::Add,
334 AstOp::Sub => BinaryOperator::Subtract,
335 AstOp::Mul => BinaryOperator::Multiply,
336 AstOp::Div => BinaryOperator::Divide,
337 AstOp::Rem => BinaryOperator::Modulo,
338 AstOp::Exp => BinaryOperator::Exponent,
339 AstOp::Eq => BinaryOperator::Equal,
340 AstOp::StrictEq => BinaryOperator::StrictEqual,
341 AstOp::Neq => BinaryOperator::NotEqual,
342 AstOp::StrictNeq => BinaryOperator::StrictNotEqual,
343 AstOp::Lt => BinaryOperator::LessThan,
344 AstOp::Lte => BinaryOperator::LessEqual,
345 AstOp::Gt => BinaryOperator::GreaterThan,
346 AstOp::Gte => BinaryOperator::GreaterEqual,
347 AstOp::Shl => BinaryOperator::ShiftLeft,
348 AstOp::Shr => BinaryOperator::ShiftRight,
349 AstOp::UShr => BinaryOperator::UnsignedShiftRight,
350 AstOp::BitOr => BinaryOperator::BitwiseOr,
351 AstOp::BitXor => BinaryOperator::BitwiseXor,
352 AstOp::BitAnd => BinaryOperator::BitwiseAnd,
353 AstOp::In => BinaryOperator::In,
354 AstOp::Instanceof => BinaryOperator::InstanceOf,
355 AstOp::Pipeline => {
356 unreachable!("Pipeline operator is checked before calling convert_binary_operator")
357 }
358 }
359}
360
361fn convert_unary_operator(op: &react_compiler_ast::operators::UnaryOperator) -> UnaryOperator {
362 use react_compiler_ast::operators::UnaryOperator as AstOp;
363 match op {
364 AstOp::Neg => UnaryOperator::Minus,
365 AstOp::Plus => UnaryOperator::Plus,
366 AstOp::Not => UnaryOperator::Not,
367 AstOp::BitNot => UnaryOperator::BitwiseNot,
368 AstOp::TypeOf => UnaryOperator::TypeOf,
369 AstOp::Void => UnaryOperator::Void,
370 AstOp::Delete | AstOp::Throw => unreachable!("delete/throw handled separately"),
371 }
372}
373
374fn lower_identifier(
383 builder: &mut HirBuilder,
384 name: &str,
385 start: u32,
386 loc: Option<SourceLocation>,
387 node_id: Option<u32>,
388) -> Result<Place, CompilerError> {
389 let binding = builder.resolve_identifier(name, start, loc.clone(), node_id)?;
390 match binding {
391 VariableBinding::Identifier { identifier, .. } => Ok(Place {
392 identifier,
393 effect: Effect::Unknown,
394 reactive: false,
395 loc,
396 }),
397 _ => {
398 if let VariableBinding::Global { ref name } = binding {
399 if name == "eval" {
400 builder.record_error(CompilerErrorDetail {
401 category: ErrorCategory::UnsupportedSyntax,
402 reason: "The 'eval' function is not supported".to_string(),
403 description: Some(
404 "Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler".to_string(),
405 ),
406 loc: loc.clone(),
407 suggestions: None,
408 })?;
409 }
410 }
411 let non_local_binding = match binding {
412 VariableBinding::Global { name } => NonLocalBinding::Global { name },
413 VariableBinding::ImportDefault { name, module } => {
414 NonLocalBinding::ImportDefault { name, module }
415 }
416 VariableBinding::ImportSpecifier {
417 name,
418 module,
419 imported,
420 } => NonLocalBinding::ImportSpecifier {
421 name,
422 module,
423 imported,
424 },
425 VariableBinding::ImportNamespace { name, module } => {
426 NonLocalBinding::ImportNamespace { name, module }
427 }
428 VariableBinding::ModuleLocal { name } => NonLocalBinding::ModuleLocal { name },
429 VariableBinding::Identifier { .. } => unreachable!(),
430 };
431 let instr_value = InstructionValue::LoadGlobal {
432 binding: non_local_binding,
433 loc: loc.clone(),
434 };
435 Ok(lower_value_to_temporary(builder, instr_value)?)
436 }
437 }
438}
439
440fn lower_arguments(
445 builder: &mut HirBuilder,
446 args: &[react_compiler_ast::expressions::Expression],
447) -> Result<Vec<PlaceOrSpread>, CompilerError> {
448 use react_compiler_ast::expressions::Expression;
449 let mut result = Vec::new();
450 for arg in args {
451 match arg {
452 Expression::SpreadElement(spread) => {
453 let place = lower_expression_to_temporary(builder, &spread.argument)?;
454 result.push(PlaceOrSpread::Spread(SpreadPattern { place }));
455 }
456 _ => {
457 let place = lower_expression_to_temporary(builder, arg)?;
458 result.push(PlaceOrSpread::Place(place));
459 }
460 }
461 }
462 Ok(result)
463}
464
465fn convert_update_operator(op: &react_compiler_ast::operators::UpdateOperator) -> UpdateOperator {
466 match op {
467 react_compiler_ast::operators::UpdateOperator::Increment => UpdateOperator::Increment,
468 react_compiler_ast::operators::UpdateOperator::Decrement => UpdateOperator::Decrement,
469 }
470}
471
472enum MemberProperty {
477 Literal(PropertyLiteral),
478 Computed(Place),
479}
480
481struct LoweredMemberExpression {
482 object: Place,
483 property: MemberProperty,
484 value: InstructionValue,
485}
486
487fn lower_member_expression(
488 builder: &mut HirBuilder,
489 member: &react_compiler_ast::expressions::MemberExpression,
490) -> Result<LoweredMemberExpression, CompilerError> {
491 Ok(lower_member_expression_impl(builder, member, None)?)
492}
493
494fn lower_member_expression_with_object(
495 builder: &mut HirBuilder,
496 member: &react_compiler_ast::expressions::OptionalMemberExpression,
497 lowered_object: Place,
498) -> Result<LoweredMemberExpression, CompilerError> {
499 use react_compiler_ast::expressions::Expression;
501 let loc = convert_opt_loc(&member.base.loc);
502 let object = lowered_object;
503
504 if !member.computed {
505 let prop_literal = match member.property.as_ref() {
506 Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),
507 Expression::NumericLiteral(lit) => {
508 PropertyLiteral::Number(FloatValue::new(lit.precise_value()))
509 }
510 _ => {
511 builder.record_error(CompilerErrorDetail {
512 category: ErrorCategory::Todo,
513 reason: format!(
514 "(BuildHIR::lowerMemberExpression) Handle {:?} property",
515 member.property
516 ),
517 description: None,
518 loc: loc.clone(),
519 suggestions: None,
520 })?;
521 return Ok(LoweredMemberExpression {
522 object,
523 property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),
524 value: InstructionValue::UnsupportedNode {
525 node_type: Some("OptionalMemberExpression".to_string()),
526 original_node: serialize_expression(
527 &react_compiler_ast::expressions::Expression::OptionalMemberExpression(
528 member.clone(),
529 ),
530 ),
531 loc,
532 },
533 });
534 }
535 };
536 let value = InstructionValue::PropertyLoad {
537 object: object.clone(),
538 property: prop_literal.clone(),
539 loc,
540 };
541 Ok(LoweredMemberExpression {
542 object,
543 property: MemberProperty::Literal(prop_literal),
544 value,
545 })
546 } else {
547 if let Expression::NumericLiteral(lit) = member.property.as_ref() {
548 let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));
549 let value = InstructionValue::PropertyLoad {
550 object: object.clone(),
551 property: prop_literal.clone(),
552 loc,
553 };
554 return Ok(LoweredMemberExpression {
555 object,
556 property: MemberProperty::Literal(prop_literal),
557 value,
558 });
559 }
560 let property = lower_expression_to_temporary(builder, &member.property)?;
561 let value = InstructionValue::ComputedLoad {
562 object: object.clone(),
563 property: property.clone(),
564 loc,
565 };
566 Ok(LoweredMemberExpression {
567 object,
568 property: MemberProperty::Computed(property),
569 value,
570 })
571 }
572}
573
574fn lower_member_expression_impl(
575 builder: &mut HirBuilder,
576 member: &react_compiler_ast::expressions::MemberExpression,
577 lowered_object: Option<Place>,
578) -> Result<LoweredMemberExpression, CompilerError> {
579 use react_compiler_ast::expressions::Expression;
580 let loc = convert_opt_loc(&member.base.loc);
581 let object = match lowered_object {
582 Some(obj) => obj,
583 None => lower_expression_to_temporary(builder, &member.object)?,
584 };
585
586 if !member.computed {
587 let prop_literal = match member.property.as_ref() {
589 Expression::Identifier(id) => PropertyLiteral::String(id.name.clone()),
590 Expression::NumericLiteral(lit) => {
591 PropertyLiteral::Number(FloatValue::new(lit.precise_value()))
592 }
593 _ => {
594 builder.record_error(CompilerErrorDetail {
595 category: ErrorCategory::Todo,
596 reason: format!(
597 "(BuildHIR::lowerMemberExpression) Handle {:?} property",
598 member.property
599 ),
600 description: None,
601 loc: loc.clone(),
602 suggestions: None,
603 })?;
604 return Ok(LoweredMemberExpression {
605 object,
606 property: MemberProperty::Literal(PropertyLiteral::String("".to_string())),
607 value: InstructionValue::UnsupportedNode {
608 node_type: Some("MemberExpression".to_string()),
609 original_node: serialize_expression(
610 &react_compiler_ast::expressions::Expression::MemberExpression(
611 member.clone(),
612 ),
613 ),
614 loc,
615 },
616 });
617 }
618 };
619 let value = InstructionValue::PropertyLoad {
620 object: object.clone(),
621 property: prop_literal.clone(),
622 loc,
623 };
624 Ok(LoweredMemberExpression {
625 object,
626 property: MemberProperty::Literal(prop_literal),
627 value,
628 })
629 } else {
630 if let Expression::NumericLiteral(lit) = member.property.as_ref() {
632 let prop_literal = PropertyLiteral::Number(FloatValue::new(lit.precise_value()));
633 let value = InstructionValue::PropertyLoad {
634 object: object.clone(),
635 property: prop_literal.clone(),
636 loc,
637 };
638 return Ok(LoweredMemberExpression {
639 object,
640 property: MemberProperty::Literal(prop_literal),
641 value,
642 });
643 }
644 let property = lower_expression_to_temporary(builder, &member.property)?;
646 let value = InstructionValue::ComputedLoad {
647 object: object.clone(),
648 property: property.clone(),
649 loc,
650 };
651 Ok(LoweredMemberExpression {
652 object,
653 property: MemberProperty::Computed(property),
654 value,
655 })
656 }
657}
658
659fn lower_expression(
664 builder: &mut HirBuilder,
665 expr: &react_compiler_ast::expressions::Expression,
666) -> Result<InstructionValue, CompilerError> {
667 use react_compiler_ast::expressions::Expression;
668
669 match expr {
670 Expression::Identifier(ident) => {
671 let loc = convert_opt_loc(&ident.base.loc);
672 let start = ident.base.start.unwrap_or(0);
673 let place =
674 lower_identifier(builder, &ident.name, start, loc.clone(), ident.base.node_id)?;
675 if builder.is_context_identifier(&ident.name, start, ident.base.node_id) {
677 Ok(InstructionValue::LoadContext { place, loc })
678 } else {
679 Ok(InstructionValue::LoadLocal { place, loc })
680 }
681 }
682 Expression::NullLiteral(lit) => {
683 let loc = convert_opt_loc(&lit.base.loc);
684 Ok(InstructionValue::Primitive {
685 value: PrimitiveValue::Null,
686 loc,
687 })
688 }
689 Expression::BooleanLiteral(lit) => {
690 let loc = convert_opt_loc(&lit.base.loc);
691 Ok(InstructionValue::Primitive {
692 value: PrimitiveValue::Boolean(lit.value),
693 loc,
694 })
695 }
696 Expression::NumericLiteral(lit) => {
697 let loc = convert_opt_loc(&lit.base.loc);
698 Ok(InstructionValue::Primitive {
699 value: PrimitiveValue::Number(FloatValue::new(lit.precise_value())),
700 loc,
701 })
702 }
703 Expression::StringLiteral(lit) => {
704 let loc = convert_opt_loc(&lit.base.loc);
705 Ok(InstructionValue::Primitive {
706 value: PrimitiveValue::String(lit.value.clone()),
707 loc,
708 })
709 }
710 Expression::BinaryExpression(bin) => {
711 let loc = convert_opt_loc(&bin.base.loc);
712 if matches!(
714 bin.operator,
715 react_compiler_ast::operators::BinaryOperator::Pipeline
716 ) {
717 builder.record_error(CompilerErrorDetail {
718 category: ErrorCategory::Todo,
719 reason: "(BuildHIR::lowerExpression) Pipe operator not supported".to_string(),
720 description: None,
721 loc: loc.clone(),
722 suggestions: None,
723 })?;
724 return Ok(InstructionValue::UnsupportedNode {
725 node_type: Some("BinaryExpression".to_string()),
726 original_node: serialize_expression(expr),
727 loc,
728 });
729 }
730 let left = lower_expression_to_temporary(builder, &bin.left)?;
731 let right = lower_expression_to_temporary(builder, &bin.right)?;
732 let operator = convert_binary_operator(&bin.operator);
733 Ok(InstructionValue::BinaryExpression {
734 operator,
735 left,
736 right,
737 loc,
738 })
739 }
740 Expression::UnaryExpression(unary) => {
741 let loc = convert_opt_loc(&unary.base.loc);
742 match &unary.operator {
743 react_compiler_ast::operators::UnaryOperator::Delete => {
744 let loc = convert_opt_loc(&unary.base.loc);
746 match &*unary.argument {
747 Expression::MemberExpression(member) => {
748 let object = lower_expression_to_temporary(builder, &member.object)?;
749 if !member.computed {
750 match &*member.property {
751 Expression::Identifier(prop_id) => {
752 Ok(InstructionValue::PropertyDelete {
753 object,
754 property: PropertyLiteral::String(prop_id.name.clone()),
755 loc,
756 })
757 }
758 _ => {
759 builder.record_error(CompilerErrorDetail {
760 reason: "Unsupported delete target".to_string(),
761 category: ErrorCategory::Todo,
762 loc: loc.clone(),
763 description: None,
764 suggestions: None,
765 })?;
766 Ok(InstructionValue::UnsupportedNode {
767 node_type: Some("UnaryExpression".to_string()),
768 original_node: serialize_expression(expr),
769 loc,
770 })
771 }
772 }
773 } else {
774 let property =
775 lower_expression_to_temporary(builder, &member.property)?;
776 Ok(InstructionValue::ComputedDelete {
777 object,
778 property,
779 loc,
780 })
781 }
782 }
783 _ => {
784 builder.record_error(CompilerErrorDetail {
786 reason: "Only object properties can be deleted".to_string(),
787 category: ErrorCategory::Syntax,
788 loc: loc.clone(),
789 description: None,
790 suggestions: None,
791 })?;
792 Ok(InstructionValue::UnsupportedNode {
793 node_type: Some("UnaryExpression".to_string()),
794 original_node: serialize_expression(expr),
795 loc,
796 })
797 }
798 }
799 }
800 react_compiler_ast::operators::UnaryOperator::Throw => {
801 let loc = convert_opt_loc(&unary.base.loc);
803 builder.record_error(CompilerErrorDetail {
804 reason: "throw expressions are not supported".to_string(),
805 category: ErrorCategory::Todo,
806 loc: loc.clone(),
807 description: None,
808 suggestions: None,
809 })?;
810 Ok(InstructionValue::UnsupportedNode {
811 node_type: Some("UnaryExpression".to_string()),
812 original_node: serialize_expression(expr),
813 loc,
814 })
815 }
816 op => {
817 let value = lower_expression_to_temporary(builder, &unary.argument)?;
818 let operator = convert_unary_operator(op);
819 Ok(InstructionValue::UnaryExpression {
820 operator,
821 value,
822 loc,
823 })
824 }
825 }
826 }
827 Expression::CallExpression(call) => {
828 let loc = convert_opt_loc(&call.base.loc);
829 if let Expression::MemberExpression(member) = call.callee.as_ref() {
831 let lowered = lower_member_expression(builder, member)?;
832 let property = lower_value_to_temporary(builder, lowered.value)?;
833 let args = lower_arguments(builder, &call.arguments)?;
834 Ok(InstructionValue::MethodCall {
835 receiver: lowered.object,
836 property,
837 args,
838 loc,
839 })
840 } else {
841 let callee = lower_expression_to_temporary(builder, &call.callee)?;
842 let args = lower_arguments(builder, &call.arguments)?;
843 Ok(InstructionValue::CallExpression { callee, args, loc })
844 }
845 }
846 Expression::MemberExpression(member) => {
847 let lowered = lower_member_expression(builder, member)?;
848 Ok(lowered.value)
849 }
850 Expression::OptionalCallExpression(opt_call) => {
851 Ok(lower_optional_call_expression(builder, opt_call)?)
852 }
853 Expression::OptionalMemberExpression(opt_member) => {
854 Ok(lower_optional_member_expression(builder, opt_member)?)
855 }
856 Expression::LogicalExpression(expr) => {
857 let loc = convert_opt_loc(&expr.base.loc);
858 let continuation_block = builder.reserve(builder.current_block_kind());
859 let continuation_id = continuation_block.id;
860 let test_block = builder.reserve(BlockKind::Value);
861 let test_block_id = test_block.id;
862 let place = build_temporary_place(builder, loc.clone());
863 let left_loc = expression_loc(&expr.left);
864 let left_place = build_temporary_place(builder, left_loc);
865
866 let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
868 lower_value_to_temporary(
869 builder,
870 InstructionValue::StoreLocal {
871 lvalue: LValue {
872 kind: InstructionKind::Const,
873 place: place.clone(),
874 },
875 value: left_place.clone(),
876 type_annotation: None,
877 loc: left_place.loc.clone(),
878 },
879 )?;
880 Ok(Terminal::Goto {
881 block: continuation_id,
882 variant: GotoVariant::Break,
883 id: EvaluationOrder(0),
884 loc: left_place.loc.clone(),
885 })
886 });
887
888 let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
890 let right = lower_expression_to_temporary(builder, &expr.right)?;
891 let right_loc = right.loc.clone();
892 lower_value_to_temporary(
893 builder,
894 InstructionValue::StoreLocal {
895 lvalue: LValue {
896 kind: InstructionKind::Const,
897 place: place.clone(),
898 },
899 value: right,
900 type_annotation: None,
901 loc: right_loc.clone(),
902 },
903 )?;
904 Ok(Terminal::Goto {
905 block: continuation_id,
906 variant: GotoVariant::Break,
907 id: EvaluationOrder(0),
908 loc: right_loc,
909 })
910 });
911
912 let hir_op = match expr.operator {
913 react_compiler_ast::operators::LogicalOperator::And => LogicalOperator::And,
914 react_compiler_ast::operators::LogicalOperator::Or => LogicalOperator::Or,
915 react_compiler_ast::operators::LogicalOperator::NullishCoalescing => {
916 LogicalOperator::NullishCoalescing
917 }
918 };
919
920 builder.terminate_with_continuation(
921 Terminal::Logical {
922 operator: hir_op,
923 test: test_block_id,
924 fallthrough: continuation_id,
925 id: EvaluationOrder(0),
926 loc: loc.clone(),
927 },
928 test_block,
929 );
930
931 let left_value = lower_expression_to_temporary(builder, &expr.left)?;
933 builder.push(Instruction {
934 id: EvaluationOrder(0),
935 lvalue: left_place.clone(),
936 value: InstructionValue::LoadLocal {
937 place: left_value,
938 loc: loc.clone(),
939 },
940 effects: None,
941 loc: loc.clone(),
942 });
943
944 builder.terminate_with_continuation(
945 Terminal::Branch {
946 test: left_place,
947 consequent: consequent_block?,
948 alternate: alternate_block?,
949 fallthrough: continuation_id,
950 id: EvaluationOrder(0),
951 loc: loc.clone(),
952 },
953 continuation_block,
954 );
955
956 Ok(InstructionValue::LoadLocal {
957 place: place.clone(),
958 loc: place.loc.clone(),
959 })
960 }
961 Expression::UpdateExpression(update) => {
962 let loc = convert_opt_loc(&update.base.loc);
963 match update.argument.as_ref() {
964 Expression::MemberExpression(member) => {
965 let binary_op = match &update.operator {
966 react_compiler_ast::operators::UpdateOperator::Increment => {
967 BinaryOperator::Add
968 }
969 react_compiler_ast::operators::UpdateOperator::Decrement => {
970 BinaryOperator::Subtract
971 }
972 };
973 let member_loc = convert_opt_loc(&member.base.loc);
976 let lowered = lower_member_expression(builder, member)?;
977 let object = lowered.object;
978 let lowered_property = lowered.property;
979 let prev_value = lower_value_to_temporary(builder, lowered.value)?;
980
981 let one = lower_value_to_temporary(
982 builder,
983 InstructionValue::Primitive {
984 value: PrimitiveValue::Number(FloatValue::new(1.0)),
985 loc: None,
986 },
987 )?;
988 let updated = lower_value_to_temporary(
989 builder,
990 InstructionValue::BinaryExpression {
991 operator: binary_op,
992 left: prev_value.clone(),
993 right: one,
994 loc: member_loc.clone(),
995 },
996 )?;
997
998 let new_value_place = match lowered_property {
1002 MemberProperty::Literal(prop_literal) => lower_value_to_temporary(
1003 builder,
1004 InstructionValue::PropertyStore {
1005 object,
1006 property: prop_literal,
1007 value: updated.clone(),
1008 loc: member_loc,
1009 },
1010 )?,
1011 MemberProperty::Computed(prop_place) => lower_value_to_temporary(
1012 builder,
1013 InstructionValue::ComputedStore {
1014 object,
1015 property: prop_place,
1016 value: updated.clone(),
1017 loc: member_loc,
1018 },
1019 )?,
1020 };
1021
1022 let result_place = if update.prefix {
1024 new_value_place
1025 } else {
1026 prev_value
1027 };
1028 Ok(InstructionValue::LoadLocal {
1029 place: result_place.clone(),
1030 loc: result_place.loc.clone(),
1031 })
1032 }
1033 Expression::Identifier(ident) => {
1034 let start = ident.base.start.unwrap_or(0);
1035 if builder.is_context_identifier(&ident.name, start, ident.base.node_id) {
1036 builder.record_error(CompilerErrorDetail {
1037 category: ErrorCategory::Todo,
1038 reason: "(BuildHIR::lowerExpression) Handle UpdateExpression to variables captured within lambdas.".to_string(),
1039 description: None,
1040 loc: loc.clone(),
1041 suggestions: None,
1042 })?;
1043 return Ok(InstructionValue::UnsupportedNode {
1044 node_type: Some("UpdateExpression".to_string()),
1045 original_node: serialize_expression(expr),
1046 loc,
1047 });
1048 }
1049
1050 let ident_loc = convert_opt_loc(&ident.base.loc);
1051 let binding = builder.resolve_identifier(
1052 &ident.name,
1053 start,
1054 ident_loc.clone(),
1055 ident.base.node_id,
1056 )?;
1057 match &binding {
1058 VariableBinding::Global { .. } => {
1059 builder.record_error(CompilerErrorDetail {
1060 category: ErrorCategory::Todo,
1061 reason: "UpdateExpression where argument is a global is not yet supported".to_string(),
1062 description: None,
1063 loc: loc.clone(),
1064 suggestions: None,
1065 })?;
1066 return Ok(InstructionValue::UnsupportedNode {
1067 node_type: Some("UpdateExpression".to_string()),
1068 original_node: serialize_expression(expr),
1069 loc,
1070 });
1071 }
1072 _ => {}
1073 }
1074 let identifier = match binding {
1075 VariableBinding::Identifier { identifier, .. } => identifier,
1076 _ => {
1077 builder.record_error(CompilerErrorDetail {
1078 category: ErrorCategory::Todo,
1079 reason: "(BuildHIR::lowerExpression) Support UpdateExpression where argument is a global".to_string(),
1080 description: None,
1081 loc: loc.clone(),
1082 suggestions: None,
1083 })?;
1084 return Ok(InstructionValue::UnsupportedNode {
1085 node_type: Some("UpdateExpression".to_string()),
1086 original_node: serialize_expression(expr),
1087 loc,
1088 });
1089 }
1090 };
1091 let lvalue_place = Place {
1092 identifier,
1093 effect: Effect::Unknown,
1094 reactive: false,
1095 loc: ident_loc.clone(),
1096 };
1097
1098 let value = lower_identifier(
1100 builder,
1101 &ident.name,
1102 start,
1103 ident_loc,
1104 ident.base.node_id,
1105 )?;
1106
1107 let operation = convert_update_operator(&update.operator);
1108
1109 if update.prefix {
1110 Ok(InstructionValue::PrefixUpdate {
1111 lvalue: lvalue_place,
1112 operation,
1113 value,
1114 loc,
1115 })
1116 } else {
1117 Ok(InstructionValue::PostfixUpdate {
1118 lvalue: lvalue_place,
1119 operation,
1120 value,
1121 loc,
1122 })
1123 }
1124 }
1125 _ => {
1126 builder.record_error(CompilerErrorDetail {
1127 category: ErrorCategory::Todo,
1128 reason: format!("UpdateExpression with unsupported argument type"),
1129 description: None,
1130 loc: loc.clone(),
1131 suggestions: None,
1132 })?;
1133 Ok(InstructionValue::UnsupportedNode {
1134 node_type: Some("UpdateExpression".to_string()),
1135 original_node: serialize_expression(expr),
1136 loc,
1137 })
1138 }
1139 }
1140 }
1141 Expression::ConditionalExpression(expr) => {
1142 let loc = convert_opt_loc(&expr.base.loc);
1143 let continuation_block = builder.reserve(builder.current_block_kind());
1144 let continuation_id = continuation_block.id;
1145 let test_block = builder.reserve(BlockKind::Value);
1146 let test_block_id = test_block.id;
1147 let place = build_temporary_place(builder, loc.clone());
1148
1149 let consequent_ast_loc = expression_loc(&expr.consequent);
1151 let consequent_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
1152 let consequent = lower_expression_to_temporary(builder, &expr.consequent)?;
1153 lower_value_to_temporary(
1154 builder,
1155 InstructionValue::StoreLocal {
1156 lvalue: LValue {
1157 kind: InstructionKind::Const,
1158 place: place.clone(),
1159 },
1160 value: consequent,
1161 type_annotation: None,
1162 loc: loc.clone(),
1163 },
1164 )?;
1165 Ok(Terminal::Goto {
1166 block: continuation_id,
1167 variant: GotoVariant::Break,
1168 id: EvaluationOrder(0),
1169 loc: consequent_ast_loc,
1170 })
1171 });
1172
1173 let alternate_ast_loc = expression_loc(&expr.alternate);
1175 let alternate_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
1176 let alternate = lower_expression_to_temporary(builder, &expr.alternate)?;
1177 lower_value_to_temporary(
1178 builder,
1179 InstructionValue::StoreLocal {
1180 lvalue: LValue {
1181 kind: InstructionKind::Const,
1182 place: place.clone(),
1183 },
1184 value: alternate,
1185 type_annotation: None,
1186 loc: loc.clone(),
1187 },
1188 )?;
1189 Ok(Terminal::Goto {
1190 block: continuation_id,
1191 variant: GotoVariant::Break,
1192 id: EvaluationOrder(0),
1193 loc: alternate_ast_loc,
1194 })
1195 });
1196
1197 builder.terminate_with_continuation(
1198 Terminal::Ternary {
1199 test: test_block_id,
1200 fallthrough: continuation_id,
1201 id: EvaluationOrder(0),
1202 loc: loc.clone(),
1203 },
1204 test_block,
1205 );
1206
1207 let test_place = lower_expression_to_temporary(builder, &expr.test)?;
1209 builder.terminate_with_continuation(
1210 Terminal::Branch {
1211 test: test_place,
1212 consequent: consequent_block?,
1213 alternate: alternate_block?,
1214 fallthrough: continuation_id,
1215 id: EvaluationOrder(0),
1216 loc: loc.clone(),
1217 },
1218 continuation_block,
1219 );
1220
1221 Ok(InstructionValue::LoadLocal {
1222 place: place.clone(),
1223 loc: place.loc.clone(),
1224 })
1225 }
1226 Expression::AssignmentExpression(expr) => {
1227 use react_compiler_ast::operators::AssignmentOperator;
1228 let loc = convert_opt_loc(&expr.base.loc);
1229
1230 if matches!(expr.operator, AssignmentOperator::Assign) {
1231 match &*expr.left {
1233 react_compiler_ast::patterns::PatternLike::Identifier(ident) => {
1234 let start = ident.base.start.unwrap_or(0);
1236 let right = lower_expression_to_temporary(builder, &expr.right)?;
1237 let ident_loc = convert_opt_loc(&ident.base.loc);
1238 let binding = builder.resolve_identifier(
1239 &ident.name,
1240 start,
1241 ident_loc.clone(),
1242 ident.base.node_id,
1243 )?;
1244 match binding {
1245 VariableBinding::Identifier {
1246 identifier,
1247 binding_kind,
1248 } => {
1249 if binding_kind == BindingKind::Const {
1251 builder.record_error(CompilerErrorDetail {
1252 reason: "Cannot reassign a `const` variable".to_string(),
1253 category: ErrorCategory::Syntax,
1254 loc: ident_loc.clone(),
1255 description: Some(format!(
1256 "`{}` is declared as const",
1257 &ident.name
1258 )),
1259 suggestions: None,
1260 })?;
1261 return Ok(InstructionValue::UnsupportedNode {
1262 node_type: Some("Identifier".to_string()),
1263 original_node: serialize_expression(
1264 &Expression::AssignmentExpression(expr.clone()),
1265 ),
1266 loc: ident_loc,
1267 });
1268 }
1269 let place = Place {
1270 identifier,
1271 reactive: false,
1272 effect: Effect::Unknown,
1273 loc: ident_loc,
1274 };
1275 if builder.is_context_identifier(
1276 &ident.name,
1277 start,
1278 ident.base.node_id,
1279 ) {
1280 let temp = lower_value_to_temporary(
1281 builder,
1282 InstructionValue::StoreContext {
1283 lvalue: LValue {
1284 kind: InstructionKind::Reassign,
1285 place: place.clone(),
1286 },
1287 value: right,
1288 loc: place.loc.clone(),
1289 },
1290 )?;
1291 Ok(InstructionValue::LoadLocal {
1292 place: temp.clone(),
1293 loc: temp.loc.clone(),
1294 })
1295 } else {
1296 let temp = lower_value_to_temporary(
1297 builder,
1298 InstructionValue::StoreLocal {
1299 lvalue: LValue {
1300 kind: InstructionKind::Reassign,
1301 place: place.clone(),
1302 },
1303 value: right,
1304 type_annotation: None,
1305 loc: place.loc.clone(),
1306 },
1307 )?;
1308 Ok(InstructionValue::LoadLocal {
1309 place: temp.clone(),
1310 loc: temp.loc.clone(),
1311 })
1312 }
1313 }
1314 _ => {
1315 let name = ident.name.clone();
1317 let temp = lower_value_to_temporary(
1318 builder,
1319 InstructionValue::StoreGlobal {
1320 name,
1321 value: right,
1322 loc: ident_loc,
1323 },
1324 )?;
1325 Ok(InstructionValue::LoadLocal {
1326 place: temp.clone(),
1327 loc: temp.loc.clone(),
1328 })
1329 }
1330 }
1331 }
1332 react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {
1333 let right = lower_expression_to_temporary(builder, &expr.right)?;
1335 let left_loc = convert_opt_loc(&member.base.loc);
1336 let object = lower_expression_to_temporary(builder, &member.object)?;
1337 let temp = if !member.computed
1338 || matches!(
1339 &*member.property,
1340 react_compiler_ast::expressions::Expression::NumericLiteral(_)
1341 ) {
1342 match &*member.property {
1343 react_compiler_ast::expressions::Expression::Identifier(
1344 prop_id,
1345 ) => lower_value_to_temporary(
1346 builder,
1347 InstructionValue::PropertyStore {
1348 object,
1349 property: PropertyLiteral::String(prop_id.name.clone()),
1350 value: right,
1351 loc: left_loc,
1352 },
1353 )?,
1354 react_compiler_ast::expressions::Expression::NumericLiteral(
1355 num,
1356 ) => lower_value_to_temporary(
1357 builder,
1358 InstructionValue::PropertyStore {
1359 object,
1360 property: PropertyLiteral::Number(FloatValue::new(
1361 num.precise_value(),
1362 )),
1363 value: right,
1364 loc: left_loc,
1365 },
1366 )?,
1367 _ => {
1368 let prop =
1369 lower_expression_to_temporary(builder, &member.property)?;
1370 lower_value_to_temporary(
1371 builder,
1372 InstructionValue::ComputedStore {
1373 object,
1374 property: prop,
1375 value: right,
1376 loc: left_loc,
1377 },
1378 )?
1379 }
1380 }
1381 } else {
1382 let prop = lower_expression_to_temporary(builder, &member.property)?;
1383 lower_value_to_temporary(
1384 builder,
1385 InstructionValue::ComputedStore {
1386 object,
1387 property: prop,
1388 value: right,
1389 loc: left_loc,
1390 },
1391 )?
1392 };
1393 Ok(InstructionValue::LoadLocal {
1394 place: temp.clone(),
1395 loc: temp.loc.clone(),
1396 })
1397 }
1398 _ => {
1399 let right = lower_expression_to_temporary(builder, &expr.right)?;
1401 let left_loc = pattern_like_hir_loc(&expr.left);
1402 let result = lower_assignment(
1403 builder,
1404 left_loc,
1405 InstructionKind::Reassign,
1406 &expr.left,
1407 right.clone(),
1408 AssignmentStyle::Destructure,
1409 )?;
1410 match result {
1411 Some(place) => Ok(InstructionValue::LoadLocal {
1412 place: place.clone(),
1413 loc: place.loc.clone(),
1414 }),
1415 None => Ok(InstructionValue::LoadLocal { place: right, loc }),
1416 }
1417 }
1418 }
1419 } else {
1420 let binary_op = match expr.operator {
1422 AssignmentOperator::AddAssign => Some(BinaryOperator::Add),
1423 AssignmentOperator::SubAssign => Some(BinaryOperator::Subtract),
1424 AssignmentOperator::MulAssign => Some(BinaryOperator::Multiply),
1425 AssignmentOperator::DivAssign => Some(BinaryOperator::Divide),
1426 AssignmentOperator::RemAssign => Some(BinaryOperator::Modulo),
1427 AssignmentOperator::ExpAssign => Some(BinaryOperator::Exponent),
1428 AssignmentOperator::ShlAssign => Some(BinaryOperator::ShiftLeft),
1429 AssignmentOperator::ShrAssign => Some(BinaryOperator::ShiftRight),
1430 AssignmentOperator::UShrAssign => Some(BinaryOperator::UnsignedShiftRight),
1431 AssignmentOperator::BitOrAssign => Some(BinaryOperator::BitwiseOr),
1432 AssignmentOperator::BitXorAssign => Some(BinaryOperator::BitwiseXor),
1433 AssignmentOperator::BitAndAssign => Some(BinaryOperator::BitwiseAnd),
1434 AssignmentOperator::OrAssign
1435 | AssignmentOperator::AndAssign
1436 | AssignmentOperator::NullishAssign => {
1437 builder.record_error(CompilerErrorDetail {
1439 reason:
1440 "Logical assignment operators (||=, &&=, ??=) are not yet supported"
1441 .to_string(),
1442 category: ErrorCategory::Todo,
1443 loc: loc.clone(),
1444 description: None,
1445 suggestions: None,
1446 })?;
1447 return Ok(InstructionValue::UnsupportedNode {
1448 node_type: Some("AssignmentExpression".to_string()),
1449 original_node: serialize_expression(&Expression::AssignmentExpression(
1450 expr.clone(),
1451 )),
1452 loc,
1453 });
1454 }
1455 AssignmentOperator::Assign => unreachable!(),
1456 };
1457 let binary_op = match binary_op {
1458 Some(op) => op,
1459 None => {
1460 return Ok(InstructionValue::UnsupportedNode {
1461 node_type: Some("AssignmentExpression".to_string()),
1462 original_node: serialize_expression(&Expression::AssignmentExpression(
1463 expr.clone(),
1464 )),
1465 loc,
1466 });
1467 }
1468 };
1469
1470 match &*expr.left {
1471 react_compiler_ast::patterns::PatternLike::Identifier(ident) => {
1472 let start = ident.base.start.unwrap_or(0);
1473 let left_place = lower_expression_to_temporary(
1474 builder,
1475 &react_compiler_ast::expressions::Expression::Identifier(ident.clone()),
1476 )?;
1477 let right = lower_expression_to_temporary(builder, &expr.right)?;
1478 let binary_place = lower_value_to_temporary(
1479 builder,
1480 InstructionValue::BinaryExpression {
1481 operator: binary_op,
1482 left: left_place,
1483 right,
1484 loc: loc.clone(),
1485 },
1486 )?;
1487 let ident_loc = convert_opt_loc(&ident.base.loc);
1488 let binding = builder.resolve_identifier(
1489 &ident.name,
1490 start,
1491 ident_loc.clone(),
1492 ident.base.node_id,
1493 )?;
1494 match binding {
1495 VariableBinding::Identifier { identifier, .. } => {
1496 let place = Place {
1497 identifier,
1498 reactive: false,
1499 effect: Effect::Unknown,
1500 loc: ident_loc,
1501 };
1502 if builder.is_context_identifier(
1503 &ident.name,
1504 start,
1505 ident.base.node_id,
1506 ) {
1507 lower_value_to_temporary(
1508 builder,
1509 InstructionValue::StoreContext {
1510 lvalue: LValue {
1511 kind: InstructionKind::Reassign,
1512 place: place.clone(),
1513 },
1514 value: binary_place,
1515 loc: loc.clone(),
1516 },
1517 )?;
1518 Ok(InstructionValue::LoadContext { place, loc })
1519 } else {
1520 lower_value_to_temporary(
1521 builder,
1522 InstructionValue::StoreLocal {
1523 lvalue: LValue {
1524 kind: InstructionKind::Reassign,
1525 place: place.clone(),
1526 },
1527 value: binary_place,
1528 type_annotation: None,
1529 loc: loc.clone(),
1530 },
1531 )?;
1532 Ok(InstructionValue::LoadLocal { place, loc })
1533 }
1534 }
1535 _ => {
1536 let name = ident.name.clone();
1538 let temp = lower_value_to_temporary(
1539 builder,
1540 InstructionValue::StoreGlobal {
1541 name,
1542 value: binary_place,
1543 loc: loc.clone(),
1544 },
1545 )?;
1546 Ok(InstructionValue::LoadLocal {
1547 place: temp.clone(),
1548 loc: temp.loc.clone(),
1549 })
1550 }
1551 }
1552 }
1553 react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {
1554 let member_loc = convert_opt_loc(&member.base.loc);
1558 let lowered = lower_member_expression(builder, member)?;
1559 let object = lowered.object;
1560 let lowered_property = lowered.property;
1561 let current_value = lower_value_to_temporary(builder, lowered.value)?;
1562 let right = lower_expression_to_temporary(builder, &expr.right)?;
1563 let result = lower_value_to_temporary(
1564 builder,
1565 InstructionValue::BinaryExpression {
1566 operator: binary_op,
1567 left: current_value,
1568 right,
1569 loc: member_loc.clone(),
1570 },
1571 )?;
1572 match lowered_property {
1574 MemberProperty::Literal(prop_literal) => {
1575 Ok(InstructionValue::PropertyStore {
1576 object,
1577 property: prop_literal,
1578 value: result,
1579 loc: member_loc,
1580 })
1581 }
1582 MemberProperty::Computed(prop_place) => {
1583 Ok(InstructionValue::ComputedStore {
1584 object,
1585 property: prop_place,
1586 value: result,
1587 loc: member_loc,
1588 })
1589 }
1590 }
1591 }
1592 _ => {
1593 builder.record_error(CompilerErrorDetail {
1594 reason: "Compound assignment to complex pattern is not yet supported"
1595 .to_string(),
1596 category: ErrorCategory::Todo,
1597 loc: loc.clone(),
1598 description: None,
1599 suggestions: None,
1600 })?;
1601 Ok(InstructionValue::UnsupportedNode {
1602 node_type: Some("AssignmentExpression".to_string()),
1603 original_node: serialize_expression(&Expression::AssignmentExpression(
1604 expr.clone(),
1605 )),
1606 loc,
1607 })
1608 }
1609 }
1610 }
1611 }
1612 Expression::SequenceExpression(seq) => {
1613 let loc = convert_opt_loc(&seq.base.loc);
1614
1615 if seq.expressions.is_empty() {
1616 builder.record_error(CompilerErrorDetail {
1617 category: ErrorCategory::Syntax,
1618 reason: "Expected sequence expression to have at least one expression"
1619 .to_string(),
1620 description: None,
1621 loc: loc.clone(),
1622 suggestions: None,
1623 })?;
1624 return Ok(InstructionValue::UnsupportedNode {
1625 node_type: Some("SequenceExpression".to_string()),
1626 original_node: serialize_expression(expr),
1627 loc,
1628 });
1629 }
1630
1631 let continuation_block = builder.reserve(builder.current_block_kind());
1632 let continuation_id = continuation_block.id;
1633 let place = build_temporary_place(builder, loc.clone());
1634
1635 let sequence_block = builder.try_enter(BlockKind::Sequence, |builder, _block_id| {
1636 let mut last: Option<Place> = None;
1637 for item in &seq.expressions {
1638 last = Some(lower_expression_to_temporary(builder, item)?);
1639 }
1640 if let Some(last) = last {
1641 lower_value_to_temporary(
1642 builder,
1643 InstructionValue::StoreLocal {
1644 lvalue: LValue {
1645 kind: InstructionKind::Const,
1646 place: place.clone(),
1647 },
1648 value: last,
1649 type_annotation: None,
1650 loc: loc.clone(),
1651 },
1652 )?;
1653 }
1654 Ok(Terminal::Goto {
1655 block: continuation_id,
1656 variant: GotoVariant::Break,
1657 id: EvaluationOrder(0),
1658 loc: loc.clone(),
1659 })
1660 });
1661
1662 builder.terminate_with_continuation(
1663 Terminal::Sequence {
1664 block: sequence_block?,
1665 fallthrough: continuation_id,
1666 id: EvaluationOrder(0),
1667 loc: loc.clone(),
1668 },
1669 continuation_block,
1670 );
1671 Ok(InstructionValue::LoadLocal { place, loc })
1672 }
1673 Expression::ArrowFunctionExpression(_) => Ok(lower_function_to_value(
1674 builder,
1675 expr,
1676 FunctionExpressionType::ArrowFunctionExpression,
1677 )?),
1678 Expression::FunctionExpression(_) => Ok(lower_function_to_value(
1679 builder,
1680 expr,
1681 FunctionExpressionType::FunctionExpression,
1682 )?),
1683 Expression::ObjectExpression(obj) => {
1684 let loc = convert_opt_loc(&obj.base.loc);
1685 let mut properties: Vec<ObjectPropertyOrSpread> = Vec::new();
1686 for prop in &obj.properties {
1687 match prop {
1688 react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(
1689 p,
1690 ) => {
1691 let key = lower_object_property_key(builder, &p.key, p.computed)?;
1692 let key = match key {
1693 Some(k) => k,
1694 None => continue,
1695 };
1696 let value = lower_expression_to_temporary(builder, &p.value)?;
1697 properties.push(ObjectPropertyOrSpread::Property(ObjectProperty {
1698 key,
1699 property_type: ObjectPropertyType::Property,
1700 place: value,
1701 }));
1702 }
1703 react_compiler_ast::expressions::ObjectExpressionProperty::SpreadElement(
1704 spread,
1705 ) => {
1706 let place = lower_expression_to_temporary(builder, &spread.argument)?;
1707 properties.push(ObjectPropertyOrSpread::Spread(SpreadPattern { place }));
1708 }
1709 react_compiler_ast::expressions::ObjectExpressionProperty::ObjectMethod(
1710 method,
1711 ) => {
1712 if let Some(prop) = lower_object_method(builder, method)? {
1713 properties.push(ObjectPropertyOrSpread::Property(prop));
1714 }
1715 }
1716 }
1717 }
1718 Ok(InstructionValue::ObjectExpression { properties, loc })
1719 }
1720 Expression::ArrayExpression(arr) => {
1721 let loc = convert_opt_loc(&arr.base.loc);
1722 let mut elements: Vec<ArrayElement> = Vec::new();
1723 for element in &arr.elements {
1724 match element {
1725 None => {
1726 elements.push(ArrayElement::Hole);
1727 }
1728 Some(Expression::SpreadElement(spread)) => {
1729 let place = lower_expression_to_temporary(builder, &spread.argument)?;
1730 elements.push(ArrayElement::Spread(SpreadPattern { place }));
1731 }
1732 Some(expr) => {
1733 let place = lower_expression_to_temporary(builder, expr)?;
1734 elements.push(ArrayElement::Place(place));
1735 }
1736 }
1737 }
1738 Ok(InstructionValue::ArrayExpression { elements, loc })
1739 }
1740 Expression::NewExpression(new_expr) => {
1741 let loc = convert_opt_loc(&new_expr.base.loc);
1742 let callee = lower_expression_to_temporary(builder, &new_expr.callee)?;
1743 let args = lower_arguments(builder, &new_expr.arguments)?;
1744 Ok(InstructionValue::NewExpression { callee, args, loc })
1745 }
1746 Expression::TemplateLiteral(tmpl) => {
1747 let loc = convert_opt_loc(&tmpl.base.loc);
1748 let subexprs: Vec<Place> = tmpl
1749 .expressions
1750 .iter()
1751 .map(|e| lower_expression_to_temporary(builder, e))
1752 .collect::<Result<Vec<_>, _>>()?;
1753 let quasis: Vec<TemplateQuasi> = tmpl
1754 .quasis
1755 .iter()
1756 .map(|q| TemplateQuasi {
1757 raw: q.value.raw.clone(),
1758 cooked: q.value.cooked.clone(),
1759 })
1760 .collect();
1761 Ok(InstructionValue::TemplateLiteral {
1762 subexprs,
1763 quasis,
1764 loc,
1765 })
1766 }
1767 Expression::TaggedTemplateExpression(tagged) => {
1768 let loc = convert_opt_loc(&tagged.base.loc);
1769 if !tagged.quasi.expressions.is_empty() {
1770 builder.record_error(CompilerErrorDetail {
1771 category: ErrorCategory::Todo,
1772 reason:
1773 "(BuildHIR::lowerExpression) Handle tagged template with interpolations"
1774 .to_string(),
1775 description: None,
1776 loc: loc.clone(),
1777 suggestions: None,
1778 })?;
1779 return Ok(InstructionValue::UnsupportedNode {
1780 node_type: Some("TaggedTemplateExpression".to_string()),
1781 original_node: serialize_expression(expr),
1782 loc,
1783 });
1784 }
1785 assert!(
1786 tagged.quasi.quasis.len() == 1,
1787 "there should be only one quasi as we don't support interpolations yet"
1788 );
1789 let quasi = &tagged.quasi.quasis[0];
1790 if quasi.value.raw != quasi.value.cooked.clone().unwrap_or_default() {
1792 builder.record_error(CompilerErrorDetail {
1793 category: ErrorCategory::Todo,
1794 reason: "(BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value".to_string(),
1795 description: None,
1796 loc: loc.clone(),
1797 suggestions: None,
1798 })?;
1799 return Ok(InstructionValue::UnsupportedNode {
1800 node_type: Some("TaggedTemplateExpression".to_string()),
1801 original_node: serialize_expression(expr),
1802 loc,
1803 });
1804 }
1805 let value = TemplateQuasi {
1806 raw: quasi.value.raw.clone(),
1807 cooked: quasi.value.cooked.clone(),
1808 };
1809 let tag = lower_expression_to_temporary(builder, &tagged.tag)?;
1810 Ok(InstructionValue::TaggedTemplateExpression { tag, value, loc })
1811 }
1812 Expression::AwaitExpression(await_expr) => {
1813 let loc = convert_opt_loc(&await_expr.base.loc);
1814 let value = lower_expression_to_temporary(builder, &await_expr.argument)?;
1815 Ok(InstructionValue::Await { value, loc })
1816 }
1817 Expression::YieldExpression(yld) => {
1818 let loc = convert_opt_loc(&yld.base.loc);
1819 builder.record_error(CompilerErrorDetail {
1820 category: ErrorCategory::Todo,
1821 reason: "(BuildHIR::lowerExpression) Handle YieldExpression expressions"
1822 .to_string(),
1823 description: None,
1824 loc: loc.clone(),
1825 suggestions: None,
1826 })?;
1827 Ok(InstructionValue::UnsupportedNode {
1828 node_type: Some("YieldExpression".to_string()),
1829 original_node: serialize_expression(expr),
1830 loc,
1831 })
1832 }
1833 Expression::SpreadElement(spread) => {
1834 Ok(lower_expression(builder, &spread.argument)?)
1837 }
1838 Expression::MetaProperty(meta) => {
1839 let loc = convert_opt_loc(&meta.base.loc);
1840 if meta.meta.name == "import" && meta.property.name == "meta" {
1841 Ok(InstructionValue::MetaProperty {
1842 meta: meta.meta.name.clone(),
1843 property: meta.property.name.clone(),
1844 loc,
1845 })
1846 } else {
1847 builder.record_error(CompilerErrorDetail {
1848 category: ErrorCategory::Todo,
1849 reason: "(BuildHIR::lowerExpression) Handle MetaProperty expressions other than import.meta".to_string(),
1850 description: None,
1851 loc: loc.clone(),
1852 suggestions: None,
1853 })?;
1854 Ok(InstructionValue::UnsupportedNode {
1855 node_type: Some("MetaProperty".to_string()),
1856 original_node: serialize_expression(expr),
1857 loc,
1858 })
1859 }
1860 }
1861 Expression::ClassExpression(cls) => {
1862 let loc = convert_opt_loc(&cls.base.loc);
1863 builder.record_error(CompilerErrorDetail {
1864 category: ErrorCategory::Todo,
1865 reason: "(BuildHIR::lowerExpression) Handle ClassExpression expressions"
1866 .to_string(),
1867 description: None,
1868 loc: loc.clone(),
1869 suggestions: None,
1870 })?;
1871 Ok(InstructionValue::UnsupportedNode {
1872 node_type: Some("ClassExpression".to_string()),
1873 original_node: serialize_expression(expr),
1874 loc,
1875 })
1876 }
1877 Expression::PrivateName(pn) => {
1878 let loc = convert_opt_loc(&pn.base.loc);
1879 builder.record_error(CompilerErrorDetail {
1880 category: ErrorCategory::Todo,
1881 reason: "(BuildHIR::lowerExpression) Handle PrivateName expressions".to_string(),
1882 description: None,
1883 loc: loc.clone(),
1884 suggestions: None,
1885 })?;
1886 Ok(InstructionValue::UnsupportedNode {
1887 node_type: Some("PrivateName".to_string()),
1888 original_node: serialize_expression(expr),
1889 loc,
1890 })
1891 }
1892 Expression::Super(sup) => {
1893 let loc = convert_opt_loc(&sup.base.loc);
1894 builder.record_error(CompilerErrorDetail {
1895 category: ErrorCategory::Todo,
1896 reason: "(BuildHIR::lowerExpression) Handle Super expressions".to_string(),
1897 description: None,
1898 loc: loc.clone(),
1899 suggestions: None,
1900 })?;
1901 Ok(InstructionValue::UnsupportedNode {
1902 node_type: Some("Super".to_string()),
1903 original_node: serialize_expression(expr),
1904 loc,
1905 })
1906 }
1907 Expression::Import(imp) => {
1908 let loc = convert_opt_loc(&imp.base.loc);
1909 builder.record_error(CompilerErrorDetail {
1910 category: ErrorCategory::Todo,
1911 reason: "(BuildHIR::lowerExpression) Handle Import expressions".to_string(),
1912 description: None,
1913 loc: loc.clone(),
1914 suggestions: None,
1915 })?;
1916 Ok(InstructionValue::UnsupportedNode {
1917 node_type: Some("Import".to_string()),
1918 original_node: serialize_expression(expr),
1919 loc,
1920 })
1921 }
1922 Expression::ThisExpression(this) => {
1923 let loc = convert_opt_loc(&this.base.loc);
1924 builder.record_error(CompilerErrorDetail {
1925 category: ErrorCategory::Todo,
1926 reason: "(BuildHIR::lowerExpression) Handle ThisExpression expressions".to_string(),
1927 description: None,
1928 loc: loc.clone(),
1929 suggestions: None,
1930 })?;
1931 Ok(InstructionValue::UnsupportedNode {
1932 node_type: Some("ThisExpression".to_string()),
1933 original_node: serialize_expression(expr),
1934 loc,
1935 })
1936 }
1937 Expression::ParenthesizedExpression(paren) => {
1938 Ok(lower_expression(builder, &paren.expression)?)
1939 }
1940 Expression::JSXElement(jsx_element) => {
1941 let loc = convert_opt_loc(&jsx_element.base.loc);
1942 let opening_loc = convert_opt_loc(&jsx_element.opening_element.base.loc);
1943 let closing_loc = jsx_element
1944 .closing_element
1945 .as_ref()
1946 .and_then(|c| convert_opt_loc(&c.base.loc));
1947
1948 let tag = lower_jsx_element_name(builder, &jsx_element.opening_element.name)?;
1950
1951 let mut props: Vec<JsxAttribute> = Vec::new();
1953 for attr_item in &jsx_element.opening_element.attributes {
1954 use react_compiler_ast::jsx::JSXAttributeItem;
1955 use react_compiler_ast::jsx::JSXAttributeName;
1956 use react_compiler_ast::jsx::JSXAttributeValue;
1957 match attr_item {
1958 JSXAttributeItem::JSXSpreadAttribute(spread) => {
1959 let argument = lower_expression_to_temporary(builder, &spread.argument)?;
1960 props.push(JsxAttribute::SpreadAttribute { argument });
1961 }
1962 JSXAttributeItem::JSXAttribute(attr) => {
1963 let prop_name = match &attr.name {
1965 JSXAttributeName::JSXIdentifier(id) => {
1966 let name = &id.name;
1967 if name.contains(':') {
1968 builder.record_error(CompilerErrorDetail {
1969 category: ErrorCategory::Todo,
1970 reason: format!(
1971 "(BuildHIR::lowerExpression) Unexpected colon in attribute name `{}`",
1972 name
1973 ),
1974 description: None,
1975 loc: convert_opt_loc(&id.base.loc),
1976 suggestions: None,
1977 })?;
1978 }
1979 name.clone()
1980 }
1981 JSXAttributeName::JSXNamespacedName(ns) => {
1982 format!("{}:{}", ns.namespace.name, ns.name.name)
1983 }
1984 };
1985
1986 let value = match &attr.value {
1988 Some(JSXAttributeValue::StringLiteral(s)) => {
1989 let str_loc = convert_opt_loc(&s.base.loc);
1990 lower_value_to_temporary(
1991 builder,
1992 InstructionValue::Primitive {
1993 value: PrimitiveValue::String(s.value.clone()),
1994 loc: str_loc,
1995 },
1996 )?
1997 }
1998 Some(JSXAttributeValue::JSXExpressionContainer(container)) => {
1999 use react_compiler_ast::jsx::JSXExpressionContainerExpr;
2000 match &container.expression {
2001 JSXExpressionContainerExpr::JSXEmptyExpression(_) => {
2002 continue;
2004 }
2005 JSXExpressionContainerExpr::Expression(expr) => {
2006 lower_expression_to_temporary(builder, expr)?
2007 }
2008 }
2009 }
2010 Some(JSXAttributeValue::JSXElement(el)) => {
2011 let val = lower_expression(
2012 builder,
2013 &react_compiler_ast::expressions::Expression::JSXElement(
2014 el.clone(),
2015 ),
2016 )?;
2017 lower_value_to_temporary(builder, val)?
2018 }
2019 Some(JSXAttributeValue::JSXFragment(frag)) => {
2020 let val = lower_expression(
2021 builder,
2022 &react_compiler_ast::expressions::Expression::JSXFragment(
2023 frag.clone(),
2024 ),
2025 )?;
2026 lower_value_to_temporary(builder, val)?
2027 }
2028 None => {
2029 let attr_loc = convert_opt_loc(&attr.base.loc);
2031 lower_value_to_temporary(
2032 builder,
2033 InstructionValue::Primitive {
2034 value: PrimitiveValue::Boolean(true),
2035 loc: attr_loc,
2036 },
2037 )?
2038 }
2039 };
2040
2041 props.push(JsxAttribute::Attribute {
2042 name: prop_name,
2043 place: value,
2044 });
2045 }
2046 }
2047 }
2048
2049 let is_fbt = matches!(&tag, JsxTag::Builtin(b) if b.name == "fbt" || b.name == "fbs");
2051
2052 if is_fbt {
2055 let tag_name = match &tag {
2056 JsxTag::Builtin(b) => b.name.clone(),
2057 _ => "fbt".to_string(),
2058 };
2059 if let react_compiler_ast::jsx::JSXElementName::JSXIdentifier(jsx_id) =
2061 &jsx_element.opening_element.name
2062 {
2063 let id_loc = convert_opt_loc(&jsx_id.base.loc);
2064 let is_local_binding = builder.has_local_binding(&jsx_id.name);
2068 if is_local_binding {
2069 let reason = format!("<{}> tags should be module-level imports", tag_name);
2074 return Err(CompilerDiagnostic::new(
2075 ErrorCategory::Invariant,
2076 &reason,
2077 None,
2078 )
2079 .with_detail(CompilerDiagnosticDetail::Error {
2080 loc: id_loc.clone(),
2081 message: Some(reason.clone()),
2082 identifier_name: None,
2083 })
2084 .into());
2085 }
2086 }
2087 }
2088
2089 if is_fbt {
2091 let tag_name = match &tag {
2092 JsxTag::Builtin(b) => b.name.as_str(),
2093 _ => "fbt",
2094 };
2095 let mut enum_locs: Vec<Option<SourceLocation>> = Vec::new();
2096 let mut plural_locs: Vec<Option<SourceLocation>> = Vec::new();
2097 let mut pronoun_locs: Vec<Option<SourceLocation>> = Vec::new();
2098 collect_fbt_sub_tags(
2099 &jsx_element.children,
2100 tag_name,
2101 &mut enum_locs,
2102 &mut plural_locs,
2103 &mut pronoun_locs,
2104 );
2105
2106 for (name, locations) in [
2107 ("enum", &enum_locs),
2108 ("plural", &plural_locs),
2109 ("pronoun", &pronoun_locs),
2110 ] {
2111 if locations.len() > 1 {
2112 use react_compiler_diagnostics::CompilerDiagnosticDetail;
2113 let details: Vec<CompilerDiagnosticDetail> = locations
2114 .iter()
2115 .map(|loc| CompilerDiagnosticDetail::Error {
2116 message: Some(format!(
2117 "Multiple `<{}:{}>` tags found",
2118 tag_name, name
2119 )),
2120 loc: loc.clone(),
2121 identifier_name: None,
2122 })
2123 .collect();
2124 let mut diag = react_compiler_diagnostics::CompilerDiagnostic::new(
2125 ErrorCategory::Todo,
2126 "Support duplicate fbt tags",
2127 Some(format!(
2128 "Support `<{}>` tags with multiple `<{}:{}>` values",
2129 tag_name, tag_name, name
2130 )),
2131 );
2132 diag.details = details;
2133 builder.environment_mut().record_diagnostic(diag);
2134 }
2135 }
2136 }
2137
2138 if is_fbt {
2141 builder.fbt_depth += 1;
2142 }
2143
2144 let children: Vec<Place> = jsx_element
2146 .children
2147 .iter()
2148 .map(|child| lower_jsx_element(builder, child))
2149 .collect::<Result<Vec<_>, _>>()?
2150 .into_iter()
2151 .flatten()
2152 .collect();
2153
2154 if is_fbt {
2155 builder.fbt_depth -= 1;
2156 }
2157
2158 Ok(InstructionValue::JsxExpression {
2159 tag,
2160 props,
2161 children: if children.is_empty() {
2162 None
2163 } else {
2164 Some(children)
2165 },
2166 loc,
2167 opening_loc,
2168 closing_loc,
2169 })
2170 }
2171 Expression::JSXFragment(jsx_fragment) => {
2172 let loc = convert_opt_loc(&jsx_fragment.base.loc);
2173
2174 let children: Vec<Place> = jsx_fragment
2176 .children
2177 .iter()
2178 .map(|child| lower_jsx_element(builder, child))
2179 .collect::<Result<Vec<_>, _>>()?
2180 .into_iter()
2181 .flatten()
2182 .collect();
2183
2184 Ok(InstructionValue::JsxFragment { children, loc })
2185 }
2186 Expression::AssignmentPattern(_) => {
2187 let loc = convert_opt_loc(&match expr {
2188 Expression::AssignmentPattern(p) => p.base.loc.clone(),
2189 _ => unreachable!(),
2190 });
2191 builder.record_error(CompilerErrorDetail {
2192 reason: "(BuildHIR::lowerExpression) Handle AssignmentPattern expressions"
2193 .to_string(),
2194 category: ErrorCategory::Todo,
2195 loc: loc.clone(),
2196 description: None,
2197 suggestions: None,
2198 })?;
2199 Ok(InstructionValue::UnsupportedNode {
2200 node_type: Some("AssignmentPattern".to_string()),
2201 original_node: serialize_expression(expr),
2202 loc,
2203 })
2204 }
2205 Expression::TSAsExpression(ts) => {
2206 let loc = convert_opt_loc(&ts.base.loc);
2207 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2208 let type_annotation = &*ts.type_annotation;
2209 let type_ = lower_type_annotation(type_annotation, builder);
2210 let type_annotation_name = get_type_annotation_name(type_annotation);
2211 Ok(InstructionValue::TypeCastExpression {
2212 value,
2213 type_,
2214 type_annotation_name,
2215 type_annotation_kind: Some("as".to_string()),
2216 type_annotation: Some(ts.type_annotation.clone()),
2217 loc,
2218 })
2219 }
2220 Expression::TSSatisfiesExpression(ts) => {
2221 let loc = convert_opt_loc(&ts.base.loc);
2222 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2223 let type_annotation = &*ts.type_annotation;
2224 let type_ = lower_type_annotation(type_annotation, builder);
2225 let type_annotation_name = get_type_annotation_name(type_annotation);
2226 Ok(InstructionValue::TypeCastExpression {
2227 value,
2228 type_,
2229 type_annotation_name,
2230 type_annotation_kind: Some("satisfies".to_string()),
2231 type_annotation: Some(ts.type_annotation.clone()),
2232 loc,
2233 })
2234 }
2235 Expression::TSNonNullExpression(ts) => Ok(lower_expression(builder, &ts.expression)?),
2236 Expression::TSTypeAssertion(ts) => {
2237 let loc = convert_opt_loc(&ts.base.loc);
2238 let value = lower_expression_to_temporary(builder, &ts.expression)?;
2239 let type_annotation = &*ts.type_annotation;
2240 let type_ = lower_type_annotation(type_annotation, builder);
2241 let type_annotation_name = get_type_annotation_name(type_annotation);
2242 Ok(InstructionValue::TypeCastExpression {
2243 value,
2244 type_,
2245 type_annotation_name,
2246 type_annotation_kind: Some("as".to_string()),
2247 type_annotation: Some(ts.type_annotation.clone()),
2248 loc,
2249 })
2250 }
2251 Expression::TSInstantiationExpression(ts) => Ok(lower_expression(builder, &ts.expression)?),
2252 Expression::TypeCastExpression(tc) => {
2253 let loc = convert_opt_loc(&tc.base.loc);
2254 let value = lower_expression_to_temporary(builder, &tc.expression)?;
2255 let inner_type = tc
2257 .type_annotation
2258 .get("typeAnnotation")
2259 .unwrap_or(&*tc.type_annotation);
2260 let type_ = lower_type_annotation(inner_type, builder);
2261 let type_annotation_name = get_type_annotation_name(inner_type);
2262 Ok(InstructionValue::TypeCastExpression {
2263 value,
2264 type_,
2265 type_annotation_name,
2266 type_annotation_kind: Some("cast".to_string()),
2267 type_annotation: Some(tc.type_annotation.clone()),
2268 loc,
2269 })
2270 }
2271 Expression::BigIntLiteral(big) => {
2272 let loc = convert_opt_loc(&big.base.loc);
2273 builder.record_error(CompilerErrorDetail {
2274 category: ErrorCategory::Todo,
2275 reason: "(BuildHIR::lowerExpression) Handle BigIntLiteral expressions".to_string(),
2276 description: None,
2277 loc: loc.clone(),
2278 suggestions: None,
2279 })?;
2280 Ok(InstructionValue::UnsupportedNode {
2281 node_type: Some("BigIntLiteral".to_string()),
2282 original_node: serialize_expression(expr),
2283 loc,
2284 })
2285 }
2286 Expression::RegExpLiteral(re) => {
2287 let loc = convert_opt_loc(&re.base.loc);
2288 Ok(InstructionValue::RegExpLiteral {
2289 pattern: re.pattern.clone(),
2290 flags: re.flags.clone(),
2291 loc,
2292 })
2293 }
2294 }
2295}
2296
2297fn is_binding_in_block_direct_statements(
2305 binding: &react_compiler_ast::scope::BindingData,
2306 stmts: &[react_compiler_ast::statements::Statement],
2307) -> bool {
2308 use react_compiler_ast::statements::Statement;
2309 let decl_start = match binding.declaration_start {
2310 Some(pos) => pos,
2311 None => return false,
2312 };
2313 for stmt in stmts {
2314 match stmt {
2315 Statement::VariableDeclaration(vd) => {
2316 let start = vd.base.start.unwrap_or(0);
2317 let end = vd.base.end.unwrap_or(u32::MAX);
2318 if decl_start >= start && decl_start < end {
2319 return true;
2320 }
2321 }
2322 Statement::FunctionDeclaration(fd) => {
2323 let start = fd.base.start.unwrap_or(0);
2324 let end = fd.base.end.unwrap_or(u32::MAX);
2325 if decl_start >= start && decl_start < end {
2326 return true;
2327 }
2328 }
2329 Statement::ClassDeclaration(cd) => {
2330 let start = cd.base.start.unwrap_or(0);
2331 let end = cd.base.end.unwrap_or(u32::MAX);
2332 if decl_start >= start && decl_start < end {
2333 return true;
2334 }
2335 }
2336 _ => {}
2337 }
2338 }
2339 false
2340}
2341
2342#[allow(dead_code)]
2343fn pattern_declares_name(pattern: &react_compiler_ast::patterns::PatternLike, name: &str) -> bool {
2344 use react_compiler_ast::patterns::PatternLike;
2345 match pattern {
2346 PatternLike::Identifier(id) => id.name == name,
2347 PatternLike::ObjectPattern(op) => op.properties.iter().any(|prop| match prop {
2348 react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(p) => {
2349 pattern_declares_name(&p.value, name)
2350 }
2351 react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {
2352 pattern_declares_name(&r.argument, name)
2353 }
2354 }),
2355 PatternLike::ArrayPattern(ap) => ap.elements.iter().any(|el| {
2356 el.as_ref()
2357 .map_or(false, |e| pattern_declares_name(e, name))
2358 }),
2359 PatternLike::AssignmentPattern(ap) => pattern_declares_name(&ap.left, name),
2360 PatternLike::RestElement(r) => pattern_declares_name(&r.argument, name),
2361 PatternLike::MemberExpression(_) => false,
2362 PatternLike::TSAsExpression(_)
2363 | PatternLike::TSSatisfiesExpression(_)
2364 | PatternLike::TSNonNullExpression(_)
2365 | PatternLike::TSTypeAssertion(_)
2366 | PatternLike::TypeCastExpression(_) => false,
2367 }
2368}
2369
2370fn statement_start(stmt: &react_compiler_ast::statements::Statement) -> Option<u32> {
2375 use react_compiler_ast::statements::Statement;
2376 match stmt {
2377 Statement::BlockStatement(s) => s.base.start,
2378 Statement::ReturnStatement(s) => s.base.start,
2379 Statement::IfStatement(s) => s.base.start,
2380 Statement::ForStatement(s) => s.base.start,
2381 Statement::WhileStatement(s) => s.base.start,
2382 Statement::DoWhileStatement(s) => s.base.start,
2383 Statement::ForInStatement(s) => s.base.start,
2384 Statement::ForOfStatement(s) => s.base.start,
2385 Statement::SwitchStatement(s) => s.base.start,
2386 Statement::ThrowStatement(s) => s.base.start,
2387 Statement::TryStatement(s) => s.base.start,
2388 Statement::BreakStatement(s) => s.base.start,
2389 Statement::ContinueStatement(s) => s.base.start,
2390 Statement::LabeledStatement(s) => s.base.start,
2391 Statement::ExpressionStatement(s) => s.base.start,
2392 Statement::EmptyStatement(s) => s.base.start,
2393 Statement::DebuggerStatement(s) => s.base.start,
2394 Statement::WithStatement(s) => s.base.start,
2395 Statement::VariableDeclaration(s) => s.base.start,
2396 Statement::FunctionDeclaration(s) => s.base.start,
2397 Statement::ClassDeclaration(s) => s.base.start,
2398 Statement::ImportDeclaration(s) => s.base.start,
2399 Statement::ExportNamedDeclaration(s) => s.base.start,
2400 Statement::ExportDefaultDeclaration(s) => s.base.start,
2401 Statement::ExportAllDeclaration(s) => s.base.start,
2402 Statement::TSTypeAliasDeclaration(s) => s.base.start,
2403 Statement::TSInterfaceDeclaration(s) => s.base.start,
2404 Statement::TSEnumDeclaration(s) => s.base.start,
2405 Statement::TSModuleDeclaration(s) => s.base.start,
2406 Statement::TSDeclareFunction(s) => s.base.start,
2407 Statement::TypeAlias(s) => s.base.start,
2408 Statement::OpaqueType(s) => s.base.start,
2409 Statement::InterfaceDeclaration(s) => s.base.start,
2410 Statement::DeclareVariable(s) => s.base.start,
2411 Statement::DeclareFunction(s) => s.base.start,
2412 Statement::DeclareClass(s) => s.base.start,
2413 Statement::DeclareModule(s) => s.base.start,
2414 Statement::DeclareModuleExports(s) => s.base.start,
2415 Statement::DeclareExportDeclaration(s) => s.base.start,
2416 Statement::DeclareExportAllDeclaration(s) => s.base.start,
2417 Statement::DeclareInterface(s) => s.base.start,
2418 Statement::DeclareTypeAlias(s) => s.base.start,
2419 Statement::DeclareOpaqueType(s) => s.base.start,
2420 Statement::EnumDeclaration(s) => s.base.start,
2421 Statement::Unknown(s) => s.base().start,
2422 }
2423}
2424
2425fn statement_end(stmt: &react_compiler_ast::statements::Statement) -> Option<u32> {
2426 use react_compiler_ast::statements::Statement;
2427 match stmt {
2428 Statement::BlockStatement(s) => s.base.end,
2429 Statement::ReturnStatement(s) => s.base.end,
2430 Statement::IfStatement(s) => s.base.end,
2431 Statement::ForStatement(s) => s.base.end,
2432 Statement::WhileStatement(s) => s.base.end,
2433 Statement::DoWhileStatement(s) => s.base.end,
2434 Statement::ForInStatement(s) => s.base.end,
2435 Statement::ForOfStatement(s) => s.base.end,
2436 Statement::SwitchStatement(s) => s.base.end,
2437 Statement::ThrowStatement(s) => s.base.end,
2438 Statement::TryStatement(s) => s.base.end,
2439 Statement::BreakStatement(s) => s.base.end,
2440 Statement::ContinueStatement(s) => s.base.end,
2441 Statement::LabeledStatement(s) => s.base.end,
2442 Statement::ExpressionStatement(s) => s.base.end,
2443 Statement::EmptyStatement(s) => s.base.end,
2444 Statement::DebuggerStatement(s) => s.base.end,
2445 Statement::WithStatement(s) => s.base.end,
2446 Statement::VariableDeclaration(s) => s.base.end,
2447 Statement::FunctionDeclaration(s) => s.base.end,
2448 Statement::ClassDeclaration(s) => s.base.end,
2449 Statement::ImportDeclaration(s) => s.base.end,
2450 Statement::ExportNamedDeclaration(s) => s.base.end,
2451 Statement::ExportDefaultDeclaration(s) => s.base.end,
2452 Statement::ExportAllDeclaration(s) => s.base.end,
2453 Statement::TSTypeAliasDeclaration(s) => s.base.end,
2454 Statement::TSInterfaceDeclaration(s) => s.base.end,
2455 Statement::TSEnumDeclaration(s) => s.base.end,
2456 Statement::TSModuleDeclaration(s) => s.base.end,
2457 Statement::TSDeclareFunction(s) => s.base.end,
2458 Statement::TypeAlias(s) => s.base.end,
2459 Statement::OpaqueType(s) => s.base.end,
2460 Statement::InterfaceDeclaration(s) => s.base.end,
2461 Statement::DeclareVariable(s) => s.base.end,
2462 Statement::DeclareFunction(s) => s.base.end,
2463 Statement::DeclareClass(s) => s.base.end,
2464 Statement::DeclareModule(s) => s.base.end,
2465 Statement::DeclareModuleExports(s) => s.base.end,
2466 Statement::DeclareExportDeclaration(s) => s.base.end,
2467 Statement::DeclareExportAllDeclaration(s) => s.base.end,
2468 Statement::DeclareInterface(s) => s.base.end,
2469 Statement::DeclareTypeAlias(s) => s.base.end,
2470 Statement::DeclareOpaqueType(s) => s.base.end,
2471 Statement::EnumDeclaration(s) => s.base.end,
2472 Statement::Unknown(s) => s.base().end,
2473 }
2474}
2475
2476fn statement_loc(stmt: &react_compiler_ast::statements::Statement) -> Option<SourceLocation> {
2478 use react_compiler_ast::statements::Statement;
2479 let loc = match stmt {
2480 Statement::BlockStatement(s) => s.base.loc.clone(),
2481 Statement::ReturnStatement(s) => s.base.loc.clone(),
2482 Statement::IfStatement(s) => s.base.loc.clone(),
2483 Statement::ForStatement(s) => s.base.loc.clone(),
2484 Statement::WhileStatement(s) => s.base.loc.clone(),
2485 Statement::DoWhileStatement(s) => s.base.loc.clone(),
2486 Statement::ForInStatement(s) => s.base.loc.clone(),
2487 Statement::ForOfStatement(s) => s.base.loc.clone(),
2488 Statement::SwitchStatement(s) => s.base.loc.clone(),
2489 Statement::ThrowStatement(s) => s.base.loc.clone(),
2490 Statement::TryStatement(s) => s.base.loc.clone(),
2491 Statement::BreakStatement(s) => s.base.loc.clone(),
2492 Statement::ContinueStatement(s) => s.base.loc.clone(),
2493 Statement::LabeledStatement(s) => s.base.loc.clone(),
2494 Statement::ExpressionStatement(s) => s.base.loc.clone(),
2495 Statement::EmptyStatement(s) => s.base.loc.clone(),
2496 Statement::DebuggerStatement(s) => s.base.loc.clone(),
2497 Statement::WithStatement(s) => s.base.loc.clone(),
2498 Statement::VariableDeclaration(s) => s.base.loc.clone(),
2499 Statement::FunctionDeclaration(s) => s.base.loc.clone(),
2500 Statement::ClassDeclaration(s) => s.base.loc.clone(),
2501 Statement::ImportDeclaration(s) => s.base.loc.clone(),
2502 Statement::ExportNamedDeclaration(s) => s.base.loc.clone(),
2503 Statement::ExportDefaultDeclaration(s) => s.base.loc.clone(),
2504 Statement::ExportAllDeclaration(s) => s.base.loc.clone(),
2505 Statement::TSTypeAliasDeclaration(s) => s.base.loc.clone(),
2506 Statement::TSInterfaceDeclaration(s) => s.base.loc.clone(),
2507 Statement::TSEnumDeclaration(s) => s.base.loc.clone(),
2508 Statement::TSModuleDeclaration(s) => s.base.loc.clone(),
2509 Statement::TSDeclareFunction(s) => s.base.loc.clone(),
2510 Statement::TypeAlias(s) => s.base.loc.clone(),
2511 Statement::OpaqueType(s) => s.base.loc.clone(),
2512 Statement::InterfaceDeclaration(s) => s.base.loc.clone(),
2513 Statement::DeclareVariable(s) => s.base.loc.clone(),
2514 Statement::DeclareFunction(s) => s.base.loc.clone(),
2515 Statement::DeclareClass(s) => s.base.loc.clone(),
2516 Statement::DeclareModule(s) => s.base.loc.clone(),
2517 Statement::DeclareModuleExports(s) => s.base.loc.clone(),
2518 Statement::DeclareExportDeclaration(s) => s.base.loc.clone(),
2519 Statement::DeclareExportAllDeclaration(s) => s.base.loc.clone(),
2520 Statement::DeclareInterface(s) => s.base.loc.clone(),
2521 Statement::DeclareTypeAlias(s) => s.base.loc.clone(),
2522 Statement::DeclareOpaqueType(s) => s.base.loc.clone(),
2523 Statement::EnumDeclaration(s) => s.base.loc.clone(),
2524 Statement::Unknown(s) => s.base().loc.clone(),
2525 };
2526 convert_opt_loc(&loc)
2527}
2528
2529fn collect_binding_names_from_pattern(
2531 pattern: &react_compiler_ast::patterns::PatternLike,
2532 scope_id: react_compiler_ast::scope::ScopeId,
2533 scope_info: &ScopeInfo,
2534 out: &mut HashSet<BindingId>,
2535) {
2536 use react_compiler_ast::patterns::PatternLike;
2537 match pattern {
2538 PatternLike::Identifier(id) => {
2539 if let Some(&binding_id) = scope_info.scopes[scope_id.0 as usize]
2540 .bindings
2541 .get(&id.name)
2542 {
2543 out.insert(binding_id);
2544 }
2545 }
2546 PatternLike::ObjectPattern(obj) => {
2547 for prop in &obj.properties {
2548 match prop {
2549 react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(p) => {
2550 collect_binding_names_from_pattern(&p.value, scope_id, scope_info, out);
2551 }
2552 react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {
2553 collect_binding_names_from_pattern(&r.argument, scope_id, scope_info, out);
2554 }
2555 }
2556 }
2557 }
2558 PatternLike::ArrayPattern(arr) => {
2559 for elem in &arr.elements {
2560 if let Some(e) = elem {
2561 collect_binding_names_from_pattern(e, scope_id, scope_info, out);
2562 }
2563 }
2564 }
2565 PatternLike::AssignmentPattern(assign) => {
2566 collect_binding_names_from_pattern(&assign.left, scope_id, scope_info, out);
2567 }
2568 PatternLike::RestElement(rest) => {
2569 collect_binding_names_from_pattern(&rest.argument, scope_id, scope_info, out);
2570 }
2571 PatternLike::MemberExpression(_) => {}
2572 PatternLike::TSAsExpression(_)
2573 | PatternLike::TSSatisfiesExpression(_)
2574 | PatternLike::TSNonNullExpression(_)
2575 | PatternLike::TSTypeAssertion(_)
2576 | PatternLike::TypeCastExpression(_) => {}
2577 }
2578}
2579
2580fn lower_block_statement(
2589 builder: &mut HirBuilder,
2590 block: &react_compiler_ast::statements::BlockStatement,
2591 parent_scope: Option<react_compiler_ast::scope::ScopeId>,
2592) -> Result<(), CompilerError> {
2593 let _ = lower_block_statement_inner(builder, block, None, parent_scope);
2594 Ok(())
2595}
2596
2597fn lower_block_statement_with_scope(
2598 builder: &mut HirBuilder,
2599 block: &react_compiler_ast::statements::BlockStatement,
2600 scope_override: react_compiler_ast::scope::ScopeId,
2601) -> Result<(), CompilerError> {
2602 let _ = lower_block_statement_inner(builder, block, Some(scope_override), None);
2603 Ok(())
2604}
2605
2606fn lower_block_statement_inner(
2607 builder: &mut HirBuilder,
2608 block: &react_compiler_ast::statements::BlockStatement,
2609 scope_override: Option<react_compiler_ast::scope::ScopeId>,
2610 parent_scope: Option<react_compiler_ast::scope::ScopeId>,
2611) -> Result<(), CompilerDiagnostic> {
2612 use react_compiler_ast::scope::BindingKind as AstBindingKind;
2613 use react_compiler_ast::statements::Statement;
2614
2615 let block_scope_id = scope_override.or_else(|| {
2618 let found = builder
2619 .scope_info()
2620 .resolve_scope_for_node(block.base.node_id);
2621 if found.is_some() {
2622 return found;
2623 }
2624 let mut decl_names = Vec::new();
2627 for stmt in &block.body {
2628 if let Statement::VariableDeclaration(vd) = stmt {
2629 for d in &vd.declarations {
2630 if let react_compiler_ast::patterns::PatternLike::Identifier(id) = &d.id {
2631 decl_names.push(id.name.as_str());
2632 }
2633 }
2634 }
2635 }
2636 if decl_names.is_empty() {
2637 return None;
2638 }
2639 let search_parent = parent_scope.unwrap_or_else(|| builder.function_scope());
2640 let found =
2641 builder
2642 .scope_info()
2643 .find_block_scope_by_bindings(&decl_names, search_parent, |sid| {
2644 builder.is_synthetic_scope_claimed(sid)
2645 });
2646 if let Some(sid) = found {
2647 builder.claim_synthetic_scope(sid);
2648 }
2649 found
2650 });
2651
2652 let scope_id = match block_scope_id {
2653 Some(id) => id,
2654 None => {
2655 for body_stmt in &block.body {
2656 lower_statement(builder, body_stmt, None, parent_scope)?;
2657 }
2658 return Ok(());
2659 }
2660 };
2661
2662 let hoistable: Vec<(
2675 BindingId,
2676 String,
2677 AstBindingKind,
2678 String,
2679 Option<u32>,
2680 Option<u32>,
2681 )> = builder
2682 .scope_info()
2683 .scope_bindings_with_children(scope_id)
2684 .filter(|b| {
2685 !matches!(b.kind, AstBindingKind::Param | AstBindingKind::Module)
2686 && b.declaration_type != "FunctionExpression"
2687 && b.declaration_type != "TypeAlias"
2688 && b.declaration_type != "OpaqueType"
2689 && b.declaration_type != "InterfaceDeclaration"
2690 && b.declaration_type != "TSTypeAliasDeclaration"
2691 && b.declaration_type != "TSInterfaceDeclaration"
2692 && b.declaration_type != "TSEnumDeclaration"
2693 })
2694 .map(|b| {
2695 (
2696 b.id,
2697 b.name.clone(),
2698 b.kind.clone(),
2699 b.declaration_type.clone(),
2700 b.declaration_start,
2701 b.declaration_node_id,
2702 )
2703 })
2704 .collect();
2705
2706 if hoistable.is_empty() {
2707 for body_stmt in &block.body {
2709 lower_statement(builder, body_stmt, None, Some(scope_id))?;
2710 }
2711 return Ok(());
2712 }
2713
2714 let mut declared: HashSet<BindingId> = HashSet::new();
2716
2717 for body_stmt in &block.body {
2718 let stmt_start = statement_start(body_stmt).unwrap_or(0);
2719 let stmt_end = statement_end(body_stmt).unwrap_or(u32::MAX);
2720 let is_function_decl = matches!(body_stmt, Statement::FunctionDeclaration(_));
2721
2722 let nested_function_ranges: Vec<(u32, u32)> = if is_function_decl {
2726 vec![(stmt_start, stmt_end)]
2728 } else {
2729 let scope_info = builder.scope_info();
2730 scope_info
2731 .node_to_scope
2732 .iter()
2733 .filter(|&(&pos, &sid)| {
2734 pos > stmt_start
2735 && pos < stmt_end
2736 && matches!(scope_info.scopes[sid.0 as usize].kind, ScopeKind::Function)
2737 })
2738 .filter_map(|(&pos, _)| {
2739 scope_info
2740 .node_to_scope_end
2741 .get(&pos)
2742 .map(|&end| (pos, end))
2743 })
2744 .collect()
2745 };
2746
2747 struct HoistInfo {
2749 binding_id: BindingId,
2750 name: String,
2751 kind: AstBindingKind,
2752 declaration_type: String,
2753 first_ref_pos: u32,
2754 first_ref_nid: u32,
2755 }
2756 let mut will_hoist: Vec<HoistInfo> = Vec::new();
2757
2758 for (binding_id, name, kind, decl_type, _decl_start, decl_node_id) in &hoistable {
2759 if declared.contains(binding_id) {
2760 continue;
2761 }
2762
2763 let apply_decl_filter = !matches!(kind, AstBindingKind::Hoisted) || is_function_decl;
2777 let refs_in_stmt: Vec<(u32, u32)> = builder
2778 .scope_info()
2779 .ref_node_id_to_binding
2780 .iter()
2781 .filter_map(|(&ref_nid, &ref_bid)| {
2782 if ref_bid != *binding_id {
2783 return None;
2784 }
2785 let entry = builder.identifier_locs().get(&ref_nid)?;
2786 let ref_start = entry.start;
2787 if ref_start < stmt_start || ref_start >= stmt_end {
2788 return None;
2789 }
2790 if apply_decl_filter && *decl_node_id == Some(ref_nid) {
2791 return None;
2792 }
2793 if entry.is_jsx {
2794 return None;
2795 }
2796 Some((ref_start, ref_nid))
2797 })
2798 .collect();
2799
2800 if refs_in_stmt.is_empty() {
2801 continue;
2802 }
2803
2804 let (first_ref_pos, first_ref_nid) =
2805 *refs_in_stmt.iter().min_by_key(|(pos, _)| *pos).unwrap();
2806
2807 let is_hoisted_kind = matches!(kind, AstBindingKind::Hoisted);
2812 let refs_in_nested_fn: Vec<(u32, u32)> = refs_in_stmt
2813 .iter()
2814 .copied()
2815 .filter(|&(ref_pos, _)| {
2816 nested_function_ranges
2817 .iter()
2818 .any(|&(fn_start, fn_end)| ref_pos >= fn_start && ref_pos < fn_end)
2819 })
2820 .collect();
2821 let should_hoist = is_hoisted_kind || !refs_in_nested_fn.is_empty();
2822 if should_hoist {
2823 let binding_data = &builder.scope_info().bindings[binding_id.0 as usize];
2834 if binding_data.scope != scope_id
2835 && !is_binding_in_block_direct_statements(binding_data, &block.body)
2836 {
2837 continue;
2838 }
2839 let (hoist_ref_pos, hoist_ref_nid) = if is_hoisted_kind {
2843 (first_ref_pos, first_ref_nid)
2844 } else {
2845 *refs_in_nested_fn
2846 .iter()
2847 .min_by_key(|(pos, _)| *pos)
2848 .unwrap()
2849 };
2850 will_hoist.push(HoistInfo {
2851 binding_id: *binding_id,
2852 name: name.clone(),
2853 kind: kind.clone(),
2854 declaration_type: decl_type.clone(),
2855 first_ref_pos: hoist_ref_pos,
2856 first_ref_nid: hoist_ref_nid,
2857 });
2858 }
2859 }
2860
2861 will_hoist.sort_by_key(|h| h.first_ref_pos);
2863
2864 for info in &will_hoist {
2866 if builder
2867 .environment()
2868 .is_hoisted_identifier(info.binding_id.0)
2869 {
2870 continue;
2871 }
2872
2873 let hoist_kind = match info.kind {
2874 AstBindingKind::Const | AstBindingKind::Var => InstructionKind::HoistedConst,
2875 AstBindingKind::Let => InstructionKind::HoistedLet,
2876 AstBindingKind::Hoisted => InstructionKind::HoistedFunction,
2877 _ => {
2878 if info.declaration_type == "FunctionDeclaration" {
2879 InstructionKind::HoistedFunction
2880 } else if info.declaration_type == "VariableDeclarator" {
2881 builder.record_error(CompilerErrorDetail {
2883 category: ErrorCategory::Todo,
2884 reason: "Handle non-const declarations for hoisting".to_string(),
2885 description: Some(format!(
2886 "variable \"{}\" declared with {:?}",
2887 info.name, info.kind
2888 )),
2889 loc: None,
2890 suggestions: None,
2891 })?;
2892 continue;
2893 } else {
2894 builder.record_error(CompilerErrorDetail {
2895 category: ErrorCategory::Todo,
2896 reason: "Unsupported declaration type for hoisting".to_string(),
2897 description: Some(format!(
2898 "variable \"{}\" declared with {}",
2899 info.name, info.declaration_type
2900 )),
2901 loc: None,
2902 suggestions: None,
2903 })?;
2904 continue;
2905 }
2906 }
2907 };
2908
2909 let ref_loc = builder
2911 .identifier_locs()
2912 .get(&info.first_ref_nid)
2913 .map(|e| e.loc.clone());
2914 let identifier = builder.resolve_binding(&info.name, info.binding_id)?;
2915 let place = Place {
2916 effect: Effect::Unknown,
2917 identifier,
2918 reactive: false,
2919 loc: ref_loc.clone(),
2920 };
2921 lower_value_to_temporary(
2922 builder,
2923 InstructionValue::DeclareContext {
2924 lvalue: LValue {
2925 kind: hoist_kind,
2926 place,
2927 },
2928 loc: ref_loc,
2929 },
2930 )?;
2931 builder
2932 .environment_mut()
2933 .add_hoisted_identifier(info.binding_id.0);
2934 builder.add_context_identifier(info.binding_id);
2936 }
2937
2938 match body_stmt {
2941 Statement::FunctionDeclaration(func) => {
2942 if let Some(id) = &func.id {
2943 if let Some(&binding_id) = builder.scope_info().scopes[scope_id.0 as usize]
2944 .bindings
2945 .get(&id.name)
2946 {
2947 declared.insert(binding_id);
2948 }
2949 }
2950 }
2951 Statement::VariableDeclaration(var_decl) => {
2952 for decl in &var_decl.declarations {
2953 collect_binding_names_from_pattern(
2954 &decl.id,
2955 scope_id,
2956 builder.scope_info(),
2957 &mut declared,
2958 );
2959 }
2960 }
2961 Statement::ClassDeclaration(cls) => {
2962 if let Some(id) = &cls.id {
2963 if let Some(&binding_id) = builder.scope_info().scopes[scope_id.0 as usize]
2964 .bindings
2965 .get(&id.name)
2966 {
2967 declared.insert(binding_id);
2968 }
2969 }
2970 }
2971 _ => {
2972 }
2976 }
2977
2978 lower_statement(builder, body_stmt, None, Some(scope_id))?;
2979 }
2980 Ok(())
2981}
2982
2983fn lower_statement(
2988 builder: &mut HirBuilder,
2989 stmt: &react_compiler_ast::statements::Statement,
2990 label: Option<&str>,
2991 parent_scope: Option<react_compiler_ast::scope::ScopeId>,
2992) -> Result<(), CompilerDiagnostic> {
2993 use react_compiler_ast::statements::Statement;
2994
2995 match stmt {
2996 Statement::EmptyStatement(_) => {
2997 }
2999 Statement::DebuggerStatement(dbg) => {
3000 let loc = convert_opt_loc(&dbg.base.loc);
3001 let value = InstructionValue::Debugger { loc };
3002 lower_value_to_temporary(builder, value)?;
3003 }
3004 Statement::ExpressionStatement(expr_stmt) => {
3005 lower_expression_to_temporary(builder, &expr_stmt.expression)?;
3006 }
3007 Statement::ReturnStatement(ret) => {
3008 let loc = convert_opt_loc(&ret.base.loc);
3009 let value = if let Some(arg) = &ret.argument {
3010 lower_expression_to_temporary(builder, arg)?
3011 } else {
3012 let undefined_value = InstructionValue::Primitive {
3013 value: PrimitiveValue::Undefined,
3014 loc: None,
3015 };
3016 lower_value_to_temporary(builder, undefined_value)?
3017 };
3018 let fallthrough = builder.reserve(BlockKind::Block);
3019 builder.terminate_with_continuation(
3020 Terminal::Return {
3021 value,
3022 return_variant: ReturnVariant::Explicit,
3023 id: EvaluationOrder(0),
3024 loc,
3025 effects: None,
3026 },
3027 fallthrough,
3028 );
3029 }
3030 Statement::ThrowStatement(throw) => {
3031 let loc = convert_opt_loc(&throw.base.loc);
3032 let value = lower_expression_to_temporary(builder, &throw.argument)?;
3033
3034 if let Some(_handler) = builder.resolve_throw_handler() {
3036 builder.record_error(CompilerErrorDetail {
3037 category: ErrorCategory::Todo,
3038 reason: "(BuildHIR::lowerStatement) Support ThrowStatement inside of try/catch"
3039 .to_string(),
3040 description: None,
3041 loc: loc.clone(),
3042 suggestions: None,
3043 })?;
3044 }
3045
3046 let fallthrough = builder.reserve(BlockKind::Block);
3047 builder.terminate_with_continuation(
3048 Terminal::Throw {
3049 value,
3050 id: EvaluationOrder(0),
3051 loc,
3052 },
3053 fallthrough,
3054 );
3055 }
3056 Statement::BlockStatement(block) => {
3057 lower_block_statement(builder, block, parent_scope)?;
3058 }
3059 Statement::VariableDeclaration(var_decl) => {
3060 use react_compiler_ast::patterns::PatternLike;
3061 use react_compiler_ast::statements::VariableDeclarationKind;
3062 if matches!(var_decl.kind, VariableDeclarationKind::Var) {
3063 builder.record_error(CompilerErrorDetail {
3064 reason: "(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration"
3065 .to_string(),
3066 category: ErrorCategory::Todo,
3067 loc: convert_opt_loc(&var_decl.base.loc),
3068 description: None,
3069 suggestions: None,
3070 })?;
3071 }
3073 let kind = match var_decl.kind {
3074 VariableDeclarationKind::Let | VariableDeclarationKind::Var => InstructionKind::Let,
3075 VariableDeclarationKind::Const | VariableDeclarationKind::Using => {
3076 InstructionKind::Const
3077 }
3078 };
3079 for declarator in &var_decl.declarations {
3080 let stmt_loc = convert_opt_loc(&var_decl.base.loc);
3081 if let Some(init) = &declarator.init {
3082 let value = lower_expression_to_temporary(builder, init)?;
3083 let assign_style = match &declarator.id {
3084 PatternLike::ObjectPattern(_) | PatternLike::ArrayPattern(_) => {
3085 AssignmentStyle::Destructure
3086 }
3087 _ => AssignmentStyle::Assignment,
3088 };
3089 lower_assignment(builder, stmt_loc, kind, &declarator.id, value, assign_style)?;
3090 } else if let PatternLike::Identifier(id) = &declarator.id {
3091 let id_loc = convert_opt_loc(&id.base.loc);
3093 let mut binding = builder.resolve_identifier(
3094 &id.name,
3095 id.base.start.unwrap_or(0),
3096 id_loc.clone(),
3097 id.base.node_id,
3098 )?;
3099 if !matches!(binding, VariableBinding::Identifier { .. }) {
3100 if let Some((binding_id, binding_data)) = builder
3103 .scope_info()
3104 .find_binding_id_in_descendants(&id.name, builder.function_scope())
3105 {
3106 let binding_kind = crate::convert_binding_kind(&binding_data.kind);
3107 let identifier = builder.resolve_binding_with_loc(
3108 &id.name,
3109 binding_id,
3110 id_loc.clone(),
3111 )?;
3112 binding = VariableBinding::Identifier {
3113 identifier,
3114 binding_kind,
3115 };
3116 }
3117 }
3118 match binding {
3119 VariableBinding::Identifier { identifier, .. } => {
3120 builder.set_identifier_declaration_loc(identifier, &id_loc);
3123 let place = Place {
3124 identifier,
3125 effect: Effect::Unknown,
3126 reactive: false,
3127 loc: id_loc.clone(),
3128 };
3129 if builder.is_context_identifier(
3130 &id.name,
3131 id.base.start.unwrap_or(0),
3132 id.base.node_id,
3133 ) {
3134 if kind == InstructionKind::Const {
3135 builder.record_error(CompilerErrorDetail {
3136 reason: "Expect `const` declaration not to be reassigned"
3137 .to_string(),
3138 category: ErrorCategory::Syntax,
3139 loc: id_loc.clone(),
3140 description: None,
3141 suggestions: None,
3142 })?;
3143 }
3144 lower_value_to_temporary(
3145 builder,
3146 InstructionValue::DeclareContext {
3147 lvalue: LValue {
3148 kind: InstructionKind::Let,
3149 place,
3150 },
3151 loc: id_loc,
3152 },
3153 )?;
3154 } else {
3155 let type_annotation =
3156 extract_type_annotation_name(&id.type_annotation);
3157 lower_value_to_temporary(
3158 builder,
3159 InstructionValue::DeclareLocal {
3160 lvalue: LValue { kind, place },
3161 type_annotation,
3162 loc: id_loc,
3163 },
3164 )?;
3165 }
3166 }
3167 _ => {
3168 builder.record_error(CompilerErrorDetail {
3169 reason: "Could not find binding for declaration".to_string(),
3170 category: ErrorCategory::Invariant,
3171 loc: id_loc,
3172 description: None,
3173 suggestions: None,
3174 })?;
3175 }
3176 }
3177 } else {
3178 builder.record_error(CompilerErrorDetail {
3179 reason: "Expected variable declaration to be an identifier if no initializer was provided".to_string(),
3180 category: ErrorCategory::Syntax,
3181 loc: convert_opt_loc(&declarator.base.loc),
3182 description: None,
3183 suggestions: None,
3184 })?;
3185 }
3186 }
3187 }
3188 Statement::BreakStatement(brk) => {
3189 let loc = convert_opt_loc(&brk.base.loc);
3190 let label_name = brk.label.as_ref().map(|l| l.name.as_str());
3191 let target = builder.lookup_break(label_name)?;
3192 let fallthrough = builder.reserve(BlockKind::Block);
3193 builder.terminate_with_continuation(
3194 Terminal::Goto {
3195 block: target,
3196 variant: GotoVariant::Break,
3197 id: EvaluationOrder(0),
3198 loc,
3199 },
3200 fallthrough,
3201 );
3202 }
3203 Statement::ContinueStatement(cont) => {
3204 let loc = convert_opt_loc(&cont.base.loc);
3205 let label_name = cont.label.as_ref().map(|l| l.name.as_str());
3206 let target = builder.lookup_continue(label_name)?;
3207 let fallthrough = builder.reserve(BlockKind::Block);
3208 builder.terminate_with_continuation(
3209 Terminal::Goto {
3210 block: target,
3211 variant: GotoVariant::Continue,
3212 id: EvaluationOrder(0),
3213 loc,
3214 },
3215 fallthrough,
3216 );
3217 }
3218 Statement::IfStatement(if_stmt) => {
3219 let loc = convert_opt_loc(&if_stmt.base.loc);
3220 let continuation_block = builder.reserve(BlockKind::Block);
3222 let continuation_id = continuation_block.id;
3223
3224 let consequent_loc = statement_loc(&if_stmt.consequent);
3226 let consequent_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3227 lower_statement(builder, &if_stmt.consequent, None, parent_scope)?;
3228 Ok(Terminal::Goto {
3229 block: continuation_id,
3230 variant: GotoVariant::Break,
3231 id: EvaluationOrder(0),
3232 loc: consequent_loc,
3233 })
3234 })?;
3235
3236 let alternate_block = if let Some(alternate) = &if_stmt.alternate {
3238 let alternate_loc = statement_loc(alternate);
3239 builder.try_enter(BlockKind::Block, |builder, _block_id| {
3240 lower_statement(builder, alternate, None, parent_scope)?;
3241 Ok(Terminal::Goto {
3242 block: continuation_id,
3243 variant: GotoVariant::Break,
3244 id: EvaluationOrder(0),
3245 loc: alternate_loc,
3246 })
3247 })?
3248 } else {
3249 continuation_id
3251 };
3252
3253 let test = lower_expression_to_temporary(builder, &if_stmt.test)?;
3254 builder.terminate_with_continuation(
3255 Terminal::If {
3256 test,
3257 consequent: consequent_block,
3258 alternate: alternate_block,
3259 fallthrough: continuation_id,
3260 id: EvaluationOrder(0),
3261 loc,
3262 },
3263 continuation_block,
3264 );
3265 }
3266 Statement::ForStatement(for_stmt) => {
3267 let loc = convert_opt_loc(&for_stmt.base.loc);
3268
3269 let test_block = builder.reserve(BlockKind::Loop);
3270 let test_block_id = test_block.id;
3271 let continuation_block = builder.reserve(BlockKind::Block);
3273 let continuation_id = continuation_block.id;
3274
3275 let init_block = builder.try_enter(BlockKind::Loop, |builder, _block_id| {
3277 let init_loc = match &for_stmt.init {
3278 None => {
3279 let placeholder = InstructionValue::Primitive {
3281 value: PrimitiveValue::Undefined,
3282 loc: loc.clone(),
3283 };
3284 lower_value_to_temporary(builder, placeholder)?;
3285 loc.clone()
3286 }
3287 Some(init) => {
3288 match init.as_ref() {
3289 react_compiler_ast::statements::ForInit::VariableDeclaration(var_decl) => {
3290 let init_loc = convert_opt_loc(&var_decl.base.loc);
3291 lower_statement(builder, &Statement::VariableDeclaration(var_decl.clone()), None, parent_scope)?;
3292 init_loc
3293 }
3294 react_compiler_ast::statements::ForInit::Expression(expr) => {
3295 let init_loc = expression_loc(expr);
3296 builder.record_error(CompilerErrorDetail {
3297 category: ErrorCategory::Todo,
3298 reason: "(BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement".to_string(),
3299 description: None,
3300 loc: loc.clone(),
3301 suggestions: None,
3302 })?;
3303 lower_expression_to_temporary(builder, expr)?;
3304 init_loc
3305 }
3306 }
3307 }
3308 };
3309 Ok(Terminal::Goto {
3310 block: test_block_id,
3311 variant: GotoVariant::Break,
3312 id: EvaluationOrder(0),
3313 loc: init_loc,
3314 })
3315 })?;
3316
3317 let update_block_id = if let Some(update) = &for_stmt.update {
3319 let update_loc = expression_loc(update);
3320 Some(builder.try_enter(BlockKind::Loop, |builder, _block_id| {
3321 lower_expression_to_temporary(builder, update)?;
3322 Ok(Terminal::Goto {
3323 block: test_block_id,
3324 variant: GotoVariant::Break,
3325 id: EvaluationOrder(0),
3326 loc: update_loc,
3327 })
3328 })?)
3329 } else {
3330 None
3331 };
3332
3333 let continue_target = update_block_id.unwrap_or(test_block_id);
3335 let body_loc = statement_loc(&for_stmt.body);
3336 let body_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3337 builder.loop_scope(
3338 label.map(|s| s.to_string()),
3339 continue_target,
3340 continuation_id,
3341 |builder| {
3342 lower_statement(builder, &for_stmt.body, None, parent_scope)?;
3343 Ok(Terminal::Goto {
3344 block: continue_target,
3345 variant: GotoVariant::Continue,
3346 id: EvaluationOrder(0),
3347 loc: body_loc,
3348 })
3349 },
3350 )
3351 })?;
3352
3353 builder.terminate_with_continuation(
3355 Terminal::For {
3356 init: init_block,
3357 test: test_block_id,
3358 update: update_block_id,
3359 loop_block: body_block,
3360 fallthrough: continuation_id,
3361 id: EvaluationOrder(0),
3362 loc: loc.clone(),
3363 },
3364 test_block,
3365 );
3366
3367 if let Some(test_expr) = &for_stmt.test {
3369 let test = lower_expression_to_temporary(builder, test_expr)?;
3370 builder.terminate_with_continuation(
3371 Terminal::Branch {
3372 test,
3373 consequent: body_block,
3374 alternate: continuation_id,
3375 fallthrough: continuation_id,
3376 id: EvaluationOrder(0),
3377 loc: loc.clone(),
3378 },
3379 continuation_block,
3380 );
3381 } else {
3382 builder.record_error(CompilerErrorDetail {
3383 category: ErrorCategory::Todo,
3384 reason: "(BuildHIR::lowerStatement) Handle empty test in ForStatement"
3385 .to_string(),
3386 description: None,
3387 loc: loc.clone(),
3388 suggestions: None,
3389 })?;
3390 let true_val = InstructionValue::Primitive {
3392 value: PrimitiveValue::Boolean(true),
3393 loc: loc.clone(),
3394 };
3395 let test = lower_value_to_temporary(builder, true_val)?;
3396 builder.terminate_with_continuation(
3397 Terminal::Branch {
3398 test,
3399 consequent: body_block,
3400 alternate: continuation_id,
3401 fallthrough: continuation_id,
3402 id: EvaluationOrder(0),
3403 loc,
3404 },
3405 continuation_block,
3406 );
3407 }
3408 }
3409 Statement::WhileStatement(while_stmt) => {
3410 let loc = convert_opt_loc(&while_stmt.base.loc);
3411 let conditional_block = builder.reserve(BlockKind::Loop);
3413 let conditional_id = conditional_block.id;
3414 let continuation_block = builder.reserve(BlockKind::Block);
3416 let continuation_id = continuation_block.id;
3417
3418 let body_loc = statement_loc(&while_stmt.body);
3420 let loop_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3421 builder.loop_scope(
3422 label.map(|s| s.to_string()),
3423 conditional_id,
3424 continuation_id,
3425 |builder| {
3426 lower_statement(builder, &while_stmt.body, None, parent_scope)?;
3427 Ok(Terminal::Goto {
3428 block: conditional_id,
3429 variant: GotoVariant::Continue,
3430 id: EvaluationOrder(0),
3431 loc: body_loc,
3432 })
3433 },
3434 )
3435 })?;
3436
3437 builder.terminate_with_continuation(
3439 Terminal::While {
3440 test: conditional_id,
3441 loop_block,
3442 fallthrough: continuation_id,
3443 id: EvaluationOrder(0),
3444 loc: loc.clone(),
3445 },
3446 conditional_block,
3447 );
3448
3449 let test = lower_expression_to_temporary(builder, &while_stmt.test)?;
3451 builder.terminate_with_continuation(
3452 Terminal::Branch {
3453 test,
3454 consequent: loop_block,
3455 alternate: continuation_id,
3456 fallthrough: conditional_id,
3457 id: EvaluationOrder(0),
3458 loc,
3459 },
3460 continuation_block,
3461 );
3462 }
3463 Statement::DoWhileStatement(do_while_stmt) => {
3464 let loc = convert_opt_loc(&do_while_stmt.base.loc);
3465 let conditional_block = builder.reserve(BlockKind::Loop);
3467 let conditional_id = conditional_block.id;
3468 let continuation_block = builder.reserve(BlockKind::Block);
3470 let continuation_id = continuation_block.id;
3471
3472 let body_loc = statement_loc(&do_while_stmt.body);
3474 let loop_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3475 builder.loop_scope(
3476 label.map(|s| s.to_string()),
3477 conditional_id,
3478 continuation_id,
3479 |builder| {
3480 lower_statement(builder, &do_while_stmt.body, None, parent_scope)?;
3481 Ok(Terminal::Goto {
3482 block: conditional_id,
3483 variant: GotoVariant::Continue,
3484 id: EvaluationOrder(0),
3485 loc: body_loc,
3486 })
3487 },
3488 )
3489 })?;
3490
3491 builder.terminate_with_continuation(
3493 Terminal::DoWhile {
3494 loop_block,
3495 test: conditional_id,
3496 fallthrough: continuation_id,
3497 id: EvaluationOrder(0),
3498 loc: loc.clone(),
3499 },
3500 conditional_block,
3501 );
3502
3503 let test = lower_expression_to_temporary(builder, &do_while_stmt.test)?;
3505 builder.terminate_with_continuation(
3506 Terminal::Branch {
3507 test,
3508 consequent: loop_block,
3509 alternate: continuation_id,
3510 fallthrough: conditional_id,
3511 id: EvaluationOrder(0),
3512 loc,
3513 },
3514 continuation_block,
3515 );
3516 }
3517 Statement::ForInStatement(for_in) => {
3518 let loc = convert_opt_loc(&for_in.base.loc);
3519 let continuation_block = builder.reserve(BlockKind::Block);
3520 let continuation_id = continuation_block.id;
3521 let init_block = builder.reserve(BlockKind::Loop);
3522 let init_block_id = init_block.id;
3523
3524 let body_loc = statement_loc(&for_in.body);
3525 let loop_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3526 builder.loop_scope(
3527 label.map(|s| s.to_string()),
3528 init_block_id,
3529 continuation_id,
3530 |builder| {
3531 lower_statement(builder, &for_in.body, None, parent_scope)?;
3532 Ok(Terminal::Goto {
3533 block: init_block_id,
3534 variant: GotoVariant::Continue,
3535 id: EvaluationOrder(0),
3536 loc: body_loc,
3537 })
3538 },
3539 )
3540 })?;
3541
3542 let value = lower_expression_to_temporary(builder, &for_in.right)?;
3543 builder.terminate_with_continuation(
3544 Terminal::ForIn {
3545 init: init_block_id,
3546 loop_block,
3547 fallthrough: continuation_id,
3548 id: EvaluationOrder(0),
3549 loc: loc.clone(),
3550 },
3551 init_block,
3552 );
3553
3554 let left_loc = match for_in.left.as_ref() {
3556 react_compiler_ast::statements::ForInOfLeft::VariableDeclaration(var_decl) => {
3557 convert_opt_loc(&var_decl.base.loc).or(loc.clone())
3558 }
3559 react_compiler_ast::statements::ForInOfLeft::Pattern(pat) => {
3560 pattern_like_hir_loc(pat).or(loc.clone())
3561 }
3562 };
3563 let next_property = lower_value_to_temporary(
3564 builder,
3565 InstructionValue::NextPropertyOf {
3566 value,
3567 loc: left_loc.clone(),
3568 },
3569 )?;
3570
3571 let assign_result = match for_in.left.as_ref() {
3572 react_compiler_ast::statements::ForInOfLeft::VariableDeclaration(var_decl) => {
3573 if var_decl.declarations.len() != 1 {
3574 builder.record_error(CompilerErrorDetail {
3575 category: ErrorCategory::Invariant,
3576 reason: format!(
3577 "Expected only one declaration in ForInStatement init, got {}",
3578 var_decl.declarations.len()
3579 ),
3580 description: None,
3581 loc: left_loc.clone(),
3582 suggestions: None,
3583 })?;
3584 }
3585 if let Some(declarator) = var_decl.declarations.first() {
3586 lower_assignment(
3587 builder,
3588 left_loc.clone(),
3589 InstructionKind::Let,
3590 &declarator.id,
3591 next_property.clone(),
3592 AssignmentStyle::Assignment,
3593 )?
3594 } else {
3595 None
3596 }
3597 }
3598 react_compiler_ast::statements::ForInOfLeft::Pattern(pattern) => lower_assignment(
3599 builder,
3600 left_loc.clone(),
3601 InstructionKind::Reassign,
3602 pattern,
3603 next_property.clone(),
3604 AssignmentStyle::Assignment,
3605 )?,
3606 };
3607 let test_value = assign_result.unwrap_or(next_property);
3609 let test = lower_value_to_temporary(
3610 builder,
3611 InstructionValue::LoadLocal {
3612 place: test_value,
3613 loc: left_loc.clone(),
3614 },
3615 )?;
3616 builder.terminate_with_continuation(
3617 Terminal::Branch {
3618 test,
3619 consequent: loop_block,
3620 alternate: continuation_id,
3621 fallthrough: continuation_id,
3622 id: EvaluationOrder(0),
3623 loc: loc.clone(),
3624 },
3625 continuation_block,
3626 );
3627 }
3628 Statement::ForOfStatement(for_of) => {
3629 let loc = convert_opt_loc(&for_of.base.loc);
3630 let continuation_block = builder.reserve(BlockKind::Block);
3631 let continuation_id = continuation_block.id;
3632 let init_block = builder.reserve(BlockKind::Loop);
3633 let init_block_id = init_block.id;
3634 let test_block = builder.reserve(BlockKind::Loop);
3635 let test_block_id = test_block.id;
3636
3637 if for_of.is_await {
3638 builder.record_error(CompilerErrorDetail {
3639 category: ErrorCategory::Todo,
3640 reason: "(BuildHIR::lowerStatement) Handle for-await loops".to_string(),
3641 description: None,
3642 loc: loc.clone(),
3643 suggestions: None,
3644 })?;
3645 return Ok(());
3646 }
3647
3648 let body_loc = statement_loc(&for_of.body);
3649 let loop_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3650 builder.loop_scope(
3651 label.map(|s| s.to_string()),
3652 init_block_id,
3653 continuation_id,
3654 |builder| {
3655 lower_statement(builder, &for_of.body, None, parent_scope)?;
3656 Ok(Terminal::Goto {
3657 block: init_block_id,
3658 variant: GotoVariant::Continue,
3659 id: EvaluationOrder(0),
3660 loc: body_loc,
3661 })
3662 },
3663 )
3664 })?;
3665
3666 let value = lower_expression_to_temporary(builder, &for_of.right)?;
3667 builder.terminate_with_continuation(
3668 Terminal::ForOf {
3669 init: init_block_id,
3670 test: test_block_id,
3671 loop_block,
3672 fallthrough: continuation_id,
3673 id: EvaluationOrder(0),
3674 loc: loc.clone(),
3675 },
3676 init_block,
3677 );
3678
3679 let iterator = lower_value_to_temporary(
3681 builder,
3682 InstructionValue::GetIterator {
3683 collection: value.clone(),
3684 loc: value.loc.clone(),
3685 },
3686 )?;
3687 builder.terminate_with_continuation(
3688 Terminal::Goto {
3689 block: test_block_id,
3690 variant: GotoVariant::Break,
3691 id: EvaluationOrder(0),
3692 loc: loc.clone(),
3693 },
3694 test_block,
3695 );
3696
3697 let left_loc = match for_of.left.as_ref() {
3699 react_compiler_ast::statements::ForInOfLeft::VariableDeclaration(var_decl) => {
3700 convert_opt_loc(&var_decl.base.loc).or(loc.clone())
3701 }
3702 react_compiler_ast::statements::ForInOfLeft::Pattern(pat) => {
3703 pattern_like_hir_loc(pat).or(loc.clone())
3704 }
3705 };
3706 let advance_iterator = lower_value_to_temporary(
3707 builder,
3708 InstructionValue::IteratorNext {
3709 iterator: iterator.clone(),
3710 collection: value.clone(),
3711 loc: left_loc.clone(),
3712 },
3713 )?;
3714
3715 let assign_result = match for_of.left.as_ref() {
3716 react_compiler_ast::statements::ForInOfLeft::VariableDeclaration(var_decl) => {
3717 if var_decl.declarations.len() != 1 {
3718 builder.record_error(CompilerErrorDetail {
3719 category: ErrorCategory::Invariant,
3720 reason: format!(
3721 "Expected only one declaration in ForOfStatement init, got {}",
3722 var_decl.declarations.len()
3723 ),
3724 description: None,
3725 loc: left_loc.clone(),
3726 suggestions: None,
3727 })?;
3728 }
3729 if let Some(declarator) = var_decl.declarations.first() {
3730 lower_assignment(
3731 builder,
3732 left_loc.clone(),
3733 InstructionKind::Let,
3734 &declarator.id,
3735 advance_iterator.clone(),
3736 AssignmentStyle::Assignment,
3737 )?
3738 } else {
3739 None
3740 }
3741 }
3742 react_compiler_ast::statements::ForInOfLeft::Pattern(pattern) => lower_assignment(
3743 builder,
3744 left_loc.clone(),
3745 InstructionKind::Reassign,
3746 pattern,
3747 advance_iterator.clone(),
3748 AssignmentStyle::Assignment,
3749 )?,
3750 };
3751 let test_value = assign_result.unwrap_or(advance_iterator);
3753 let test = lower_value_to_temporary(
3754 builder,
3755 InstructionValue::LoadLocal {
3756 place: test_value,
3757 loc: left_loc.clone(),
3758 },
3759 )?;
3760 builder.terminate_with_continuation(
3761 Terminal::Branch {
3762 test,
3763 consequent: loop_block,
3764 alternate: continuation_id,
3765 fallthrough: continuation_id,
3766 id: EvaluationOrder(0),
3767 loc: loc.clone(),
3768 },
3769 continuation_block,
3770 );
3771 }
3772 Statement::SwitchStatement(switch_stmt) => {
3773 let loc = convert_opt_loc(&switch_stmt.base.loc);
3774 let continuation_block = builder.reserve(BlockKind::Block);
3775 let continuation_id = continuation_block.id;
3776
3777 let mut fallthrough = continuation_id;
3780 let mut cases: Vec<Case> = Vec::new();
3781 let mut has_default = false;
3782
3783 for ii in (0..switch_stmt.cases.len()).rev() {
3784 let case = &switch_stmt.cases[ii];
3785 let case_loc = convert_opt_loc(&case.base.loc);
3786
3787 if case.test.is_none() {
3788 if has_default {
3789 builder.record_error(CompilerErrorDetail {
3790 category: ErrorCategory::Syntax,
3791 reason: "Expected at most one `default` branch in a switch statement"
3792 .to_string(),
3793 description: None,
3794 loc: case_loc.clone(),
3795 suggestions: None,
3796 })?;
3797 break;
3798 }
3799 has_default = true;
3800 }
3801
3802 let fallthrough_target = fallthrough;
3803 let block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
3804 builder.switch_scope(label.map(|s| s.to_string()), continuation_id, |builder| {
3805 for consequent in &case.consequent {
3806 lower_statement(builder, consequent, None, parent_scope)?;
3807 }
3808 Ok(Terminal::Goto {
3809 block: fallthrough_target,
3810 variant: GotoVariant::Break,
3811 id: EvaluationOrder(0),
3812 loc: case_loc.clone(),
3813 })
3814 })
3815 })?;
3816
3817 let test = if let Some(test_expr) = &case.test {
3818 Some(lower_reorderable_expression(builder, test_expr)?)
3819 } else {
3820 None
3821 };
3822
3823 cases.push(Case { test, block });
3824 fallthrough = block;
3825 }
3826
3827 cases.reverse();
3829
3830 if !has_default {
3832 cases.push(Case {
3833 test: None,
3834 block: continuation_id,
3835 });
3836 }
3837
3838 let test = lower_expression_to_temporary(builder, &switch_stmt.discriminant)?;
3839 builder.terminate_with_continuation(
3840 Terminal::Switch {
3841 test,
3842 cases,
3843 fallthrough: continuation_id,
3844 id: EvaluationOrder(0),
3845 loc,
3846 },
3847 continuation_block,
3848 );
3849 }
3850 Statement::TryStatement(try_stmt) => {
3851 let loc = convert_opt_loc(&try_stmt.base.loc);
3852 let continuation_block = builder.reserve(BlockKind::Block);
3853 let continuation_id = continuation_block.id;
3854
3855 let handler_clause = match &try_stmt.handler {
3856 Some(h) => h,
3857 None => {
3858 builder.record_error(CompilerErrorDetail {
3859 category: ErrorCategory::Todo,
3860 reason:
3861 "(BuildHIR::lowerStatement) Handle TryStatement without a catch clause"
3862 .to_string(),
3863 description: None,
3864 loc: loc.clone(),
3865 suggestions: None,
3866 })?;
3867 return Ok(());
3868 }
3869 };
3870
3871 if try_stmt.finalizer.is_some() {
3872 builder.record_error(CompilerErrorDetail {
3873 category: ErrorCategory::Todo,
3874 reason: "(BuildHIR::lowerStatement) Handle TryStatement with a finalizer ('finally') clause".to_string(),
3875 description: None,
3876 loc: loc.clone(),
3877 suggestions: None,
3878 })?;
3879 }
3880
3881 let handler_binding_info: Option<(Place, react_compiler_ast::patterns::PatternLike)> =
3883 if let Some(param) = &handler_clause.param {
3884 let is_destructuring = matches!(
3888 param,
3889 react_compiler_ast::patterns::PatternLike::ObjectPattern(_)
3890 | react_compiler_ast::patterns::PatternLike::ArrayPattern(_)
3891 );
3892 if is_destructuring {
3893 fn collect_identifier_locs(
3895 pat: &react_compiler_ast::patterns::PatternLike,
3896 locs: &mut Vec<Option<SourceLocation>>,
3897 ) {
3898 match pat {
3899 react_compiler_ast::patterns::PatternLike::Identifier(id) => {
3900 locs.push(convert_opt_loc(&id.base.loc));
3901 }
3902 react_compiler_ast::patterns::PatternLike::ObjectPattern(obj) => {
3903 for prop in &obj.properties {
3904 match prop {
3905 react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(p) => {
3906 collect_identifier_locs(&p.value, locs);
3907 }
3908 react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {
3909 collect_identifier_locs(&r.argument, locs);
3910 }
3911 }
3912 }
3913 }
3914 react_compiler_ast::patterns::PatternLike::ArrayPattern(arr) => {
3915 for elem in &arr.elements {
3916 if let Some(e) = elem {
3917 collect_identifier_locs(e, locs);
3918 }
3919 }
3920 }
3921 _ => {}
3922 }
3923 }
3924 let mut id_locs = Vec::new();
3925 collect_identifier_locs(param, &mut id_locs);
3926 for id_loc in id_locs {
3927 builder.record_error(CompilerErrorDetail {
3928 reason: "(BuildHIR::lowerAssignment) Could not find binding for declaration.".to_string(),
3929 category: ErrorCategory::Invariant,
3930 loc: id_loc,
3931 description: None,
3932 suggestions: None,
3933 })?;
3934 }
3935 None
3936 } else {
3937 let param_loc = convert_opt_loc(&pattern_like_loc(param));
3938 let id = builder.make_temporary(param_loc.clone());
3939 promote_temporary(builder, id);
3940 let place = Place {
3941 identifier: id,
3942 effect: Effect::Unknown,
3943 reactive: false,
3944 loc: param_loc.clone(),
3945 };
3946 lower_value_to_temporary(
3948 builder,
3949 InstructionValue::DeclareLocal {
3950 lvalue: LValue {
3951 kind: InstructionKind::Catch,
3952 place: place.clone(),
3953 },
3954 type_annotation: None,
3955 loc: param_loc,
3956 },
3957 )?;
3958 Some((place, param.clone()))
3959 }
3960 } else {
3961 None
3962 };
3963
3964 let handler_binding_for_block = handler_binding_info.clone();
3966 let handler_loc = convert_opt_loc(&handler_clause.base.loc);
3967 let handler_param_loc = handler_clause
3969 .param
3970 .as_ref()
3971 .and_then(|p| convert_opt_loc(&pattern_like_loc(p)));
3972 let handler_block = builder.try_enter(BlockKind::Catch, |builder, _block_id| {
3973 if let Some((ref place, ref pattern)) = handler_binding_for_block {
3974 lower_assignment(
3975 builder,
3976 handler_param_loc.clone().or_else(|| handler_loc.clone()),
3977 InstructionKind::Catch,
3978 pattern,
3979 place.clone(),
3980 AssignmentStyle::Assignment,
3981 )?;
3982 }
3983 let catch_scope = builder
3991 .scope_info()
3992 .resolve_scope_for_node(handler_clause.base.node_id)
3993 .or_else(|| {
3994 builder
3995 .scope_info()
3996 .resolve_scope_for_node(handler_clause.body.base.node_id)
3997 });
3998 if let Some(scope_id) = catch_scope {
3999 lower_block_statement_with_scope(builder, &handler_clause.body, scope_id)?;
4000 } else {
4001 lower_block_statement(builder, &handler_clause.body, parent_scope)?;
4005 }
4006 Ok(Terminal::Goto {
4007 block: continuation_id,
4008 variant: GotoVariant::Break,
4009 id: EvaluationOrder(0),
4010 loc: handler_loc.clone(),
4011 })
4012 })?;
4013
4014 let try_body_loc = convert_opt_loc(&try_stmt.block.base.loc);
4021 let try_block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
4022 builder.try_enter_try_catch(handler_block, |builder| {
4023 lower_block_statement(builder, &try_stmt.block, parent_scope)?;
4024 Ok(())
4025 })?;
4026 Ok(Terminal::Goto {
4027 block: continuation_id,
4028 variant: GotoVariant::Try,
4029 id: EvaluationOrder(0),
4030 loc: try_body_loc.clone(),
4031 })
4032 })?;
4033
4034 builder.terminate_with_continuation(
4035 Terminal::Try {
4036 block: try_block,
4037 handler_binding: handler_binding_info.map(|(place, _)| place),
4038 handler: handler_block,
4039 fallthrough: continuation_id,
4040 id: EvaluationOrder(0),
4041 loc,
4042 },
4043 continuation_block,
4044 );
4045 }
4046 Statement::LabeledStatement(labeled_stmt) => {
4047 let label_name = &labeled_stmt.label.name;
4048 let loc = convert_opt_loc(&labeled_stmt.base.loc);
4049
4050 match labeled_stmt.body.as_ref() {
4052 Statement::ForStatement(_)
4053 | Statement::WhileStatement(_)
4054 | Statement::DoWhileStatement(_)
4055 | Statement::ForInStatement(_)
4056 | Statement::ForOfStatement(_) => {
4057 lower_statement(builder, &labeled_stmt.body, Some(label_name), parent_scope)?;
4059 }
4060 _ => {
4061 let continuation_block = builder.reserve(BlockKind::Block);
4063 let continuation_id = continuation_block.id;
4064 let body_loc = statement_loc(&labeled_stmt.body);
4065
4066 let block = builder.try_enter(BlockKind::Block, |builder, _block_id| {
4067 builder.label_scope(label_name.clone(), continuation_id, |builder| {
4068 lower_statement(builder, &labeled_stmt.body, None, parent_scope)?;
4069 Ok(())
4070 })?;
4071 Ok(Terminal::Goto {
4072 block: continuation_id,
4073 variant: GotoVariant::Break,
4074 id: EvaluationOrder(0),
4075 loc: body_loc,
4076 })
4077 })?;
4078
4079 builder.terminate_with_continuation(
4080 Terminal::Label {
4081 block,
4082 fallthrough: continuation_id,
4083 id: EvaluationOrder(0),
4084 loc,
4085 },
4086 continuation_block,
4087 );
4088 }
4089 }
4090 }
4091 Statement::WithStatement(with_stmt) => {
4092 let loc = convert_opt_loc(&with_stmt.base.loc);
4093 builder.record_error(CompilerErrorDetail {
4094 category: ErrorCategory::UnsupportedSyntax,
4095 reason: "JavaScript 'with' syntax is not supported".to_string(),
4096 description: Some("'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives".to_string()),
4097 loc: loc.clone(),
4098 suggestions: None,
4099 })?;
4100 lower_value_to_temporary(
4101 builder,
4102 InstructionValue::UnsupportedNode {
4103 node_type: Some("WithStatement".to_string()),
4104 original_node: serialize_statement(stmt),
4105 loc,
4106 },
4107 )?;
4108 }
4109 Statement::FunctionDeclaration(func_decl) => {
4110 lower_function_declaration(builder, func_decl)?;
4111 }
4112 Statement::ClassDeclaration(cls) => {
4113 let loc = convert_opt_loc(&cls.base.loc);
4114 builder.record_error(CompilerErrorDetail {
4115 category: ErrorCategory::UnsupportedSyntax,
4116 reason: "Inline `class` declarations are not supported".to_string(),
4117 description: Some(
4118 "Move class declarations outside of components/hooks".to_string(),
4119 ),
4120 loc: loc.clone(),
4121 suggestions: None,
4122 })?;
4123 lower_value_to_temporary(
4124 builder,
4125 InstructionValue::UnsupportedNode {
4126 node_type: Some("ClassDeclaration".to_string()),
4127 original_node: serialize_statement(stmt),
4128 loc,
4129 },
4130 )?;
4131 }
4132 Statement::ImportDeclaration(_)
4133 | Statement::ExportNamedDeclaration(_)
4134 | Statement::ExportDefaultDeclaration(_)
4135 | Statement::ExportAllDeclaration(_) => {
4136 let (loc, node_type_name) = match stmt {
4137 Statement::ImportDeclaration(s) => {
4138 (convert_opt_loc(&s.base.loc), "ImportDeclaration")
4139 }
4140 Statement::ExportNamedDeclaration(s) => {
4141 (convert_opt_loc(&s.base.loc), "ExportNamedDeclaration")
4142 }
4143 Statement::ExportDefaultDeclaration(s) => {
4144 (convert_opt_loc(&s.base.loc), "ExportDefaultDeclaration")
4145 }
4146 Statement::ExportAllDeclaration(s) => {
4147 (convert_opt_loc(&s.base.loc), "ExportAllDeclaration")
4148 }
4149 _ => unreachable!(),
4150 };
4151 builder.record_error(CompilerErrorDetail {
4152 category: ErrorCategory::Syntax,
4153 reason: "JavaScript `import` and `export` statements may only appear at the top level of a module".to_string(),
4154 description: None,
4155 loc: loc.clone(),
4156 suggestions: None,
4157 })?;
4158 lower_value_to_temporary(
4159 builder,
4160 InstructionValue::UnsupportedNode {
4161 node_type: Some(node_type_name.to_string()),
4162 original_node: serialize_statement(stmt),
4163 loc,
4164 },
4165 )?;
4166 }
4167 Statement::TSEnumDeclaration(e) => {
4169 let loc = convert_opt_loc(&e.base.loc);
4170 let original_node = serde_json::to_value(
4171 &react_compiler_ast::statements::Statement::TSEnumDeclaration(e.clone()),
4172 )
4173 .ok();
4174 lower_value_to_temporary(
4175 builder,
4176 InstructionValue::UnsupportedNode {
4177 node_type: Some("TSEnumDeclaration".to_string()),
4178 original_node,
4179 loc,
4180 },
4181 )?;
4182 }
4183 Statement::EnumDeclaration(e) => {
4184 let loc = convert_opt_loc(&e.base.loc);
4185 let original_node = serde_json::to_value(
4186 &react_compiler_ast::statements::Statement::EnumDeclaration(e.clone()),
4187 )
4188 .ok();
4189 lower_value_to_temporary(
4190 builder,
4191 InstructionValue::UnsupportedNode {
4192 node_type: Some("EnumDeclaration".to_string()),
4193 original_node,
4194 loc,
4195 },
4196 )?;
4197 }
4198 Statement::TSTypeAliasDeclaration(_)
4200 | Statement::TSInterfaceDeclaration(_)
4201 | Statement::TSModuleDeclaration(_)
4202 | Statement::TSDeclareFunction(_)
4203 | Statement::TypeAlias(_)
4204 | Statement::OpaqueType(_)
4205 | Statement::InterfaceDeclaration(_)
4206 | Statement::DeclareVariable(_)
4207 | Statement::DeclareFunction(_)
4208 | Statement::DeclareClass(_)
4209 | Statement::DeclareModule(_)
4210 | Statement::DeclareModuleExports(_)
4211 | Statement::DeclareExportDeclaration(_)
4212 | Statement::DeclareExportAllDeclaration(_)
4213 | Statement::DeclareInterface(_)
4214 | Statement::DeclareTypeAlias(_)
4215 | Statement::DeclareOpaqueType(_) => {}
4216 Statement::Unknown(unknown) => {
4221 let loc = convert_opt_loc(&unknown.base().loc);
4222 let node_type = unknown.node_type().to_string();
4223 builder.record_error(CompilerErrorDetail {
4224 category: ErrorCategory::UnsupportedSyntax,
4225 reason: format!("Unsupported statement kind '{node_type}'"),
4226 description: None,
4227 loc: loc.clone(),
4228 suggestions: None,
4229 })?;
4230 lower_value_to_temporary(
4231 builder,
4232 InstructionValue::UnsupportedNode {
4233 node_type: Some(node_type),
4234 original_node: Some(unknown.raw().clone()),
4235 loc,
4236 },
4237 )?;
4238 }
4239 }
4240 Ok(())
4241}
4242
4243enum FunctionBody<'a> {
4248 Block(&'a react_compiler_ast::statements::BlockStatement),
4249 Expression(&'a react_compiler_ast::expressions::Expression),
4250}
4251
4252pub fn lower(
4258 func: &FunctionNode<'_>,
4259 _id: Option<&str>,
4260 scope_info: &ScopeInfo,
4261 env: &mut Environment,
4262) -> Result<HirFunction, CompilerError> {
4263 let (params, body, generator, is_async, loc, start, end, ast_id) = match func {
4268 FunctionNode::FunctionDeclaration(decl) => (
4269 &decl.params[..],
4270 FunctionBody::Block(&decl.body),
4271 decl.generator,
4272 decl.is_async,
4273 convert_opt_loc(&decl.base.loc),
4274 decl.base.start.unwrap_or(0),
4275 decl.base.end.unwrap_or(0),
4276 decl.id.as_ref().map(|id| id.name.as_str()),
4277 ),
4278 FunctionNode::FunctionExpression(expr) => (
4279 &expr.params[..],
4280 FunctionBody::Block(&expr.body),
4281 expr.generator,
4282 expr.is_async,
4283 convert_opt_loc(&expr.base.loc),
4284 expr.base.start.unwrap_or(0),
4285 expr.base.end.unwrap_or(0),
4286 expr.id.as_ref().map(|id| id.name.as_str()),
4287 ),
4288 FunctionNode::ArrowFunctionExpression(arrow) => {
4289 let body = match arrow.body.as_ref() {
4290 react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
4291 FunctionBody::Block(block)
4292 }
4293 react_compiler_ast::expressions::ArrowFunctionBody::Expression(expr) => {
4294 FunctionBody::Expression(expr)
4295 }
4296 };
4297 (
4298 &arrow.params[..],
4299 body,
4300 arrow.generator,
4301 arrow.is_async,
4302 convert_opt_loc(&arrow.base.loc),
4303 arrow.base.start.unwrap_or(0),
4304 arrow.base.end.unwrap_or(0),
4305 None, )
4307 }
4308 };
4309
4310 let scope_id = scope_info
4311 .resolve_scope_for_node(func.node_id())
4312 .unwrap_or(scope_info.program_scope);
4313
4314 validate_ts_this_parameters_in_function_range(scope_info, start, end)?;
4315
4316 let identifier_locs = build_identifier_loc_index(func, scope_info);
4318
4319 let context_identifiers = find_context_identifiers(func, scope_info, env, &identifier_locs)?;
4321
4322 let context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> =
4324 IndexMap::new();
4325
4326 let (hir_func, _used_names, _child_bindings) = lower_inner(
4327 params,
4328 body,
4329 ast_id,
4330 generator,
4331 is_async,
4332 loc,
4333 scope_info,
4334 env,
4335 None, None, context_map,
4338 scope_id,
4339 scope_id, &context_identifiers,
4341 true, &identifier_locs,
4343 )?;
4344
4345 Ok(hir_func)
4346}
4347
4348enum IdentifierForAssignment {
4354 Place(Place),
4356 Global { name: String },
4358}
4359
4360fn lower_identifier_for_assignment(
4363 builder: &mut HirBuilder,
4364 loc: Option<SourceLocation>,
4365 ident_loc: Option<SourceLocation>,
4366 kind: InstructionKind,
4367 name: &str,
4368 start: u32,
4369 node_id: Option<u32>,
4370) -> Result<Option<IdentifierForAssignment>, CompilerError> {
4371 let mut binding = builder.resolve_identifier(name, start, ident_loc.clone(), node_id)?;
4372 if !matches!(binding, VariableBinding::Identifier { .. }) && kind != InstructionKind::Reassign {
4373 if let Some((binding_id, binding_data)) = builder
4374 .scope_info()
4375 .find_binding_id_in_descendants(name, builder.function_scope())
4376 {
4377 let bk = crate::convert_binding_kind(&binding_data.kind);
4378 let identifier =
4379 builder.resolve_binding_with_loc(name, binding_id, ident_loc.clone())?;
4380 binding = VariableBinding::Identifier {
4381 identifier,
4382 binding_kind: bk,
4383 };
4384 }
4385 }
4386 match binding {
4387 VariableBinding::Identifier {
4388 identifier,
4389 binding_kind,
4390 ..
4391 } => {
4392 if kind != InstructionKind::Reassign {
4395 builder.set_identifier_declaration_loc(identifier, &ident_loc);
4396 }
4397 if binding_kind == BindingKind::Const && kind == InstructionKind::Reassign {
4398 builder.record_error(CompilerErrorDetail {
4399 reason: "Cannot reassign a `const` variable".to_string(),
4400 category: ErrorCategory::Syntax,
4401 loc: loc.clone(),
4402 description: Some(format!("`{}` is declared as const", name)),
4403 suggestions: None,
4404 })?;
4405 return Ok(None);
4406 }
4407 Ok(Some(IdentifierForAssignment::Place(Place {
4408 identifier,
4409 effect: Effect::Unknown,
4410 reactive: false,
4411 loc,
4412 })))
4413 }
4414 VariableBinding::Global { name: gname } => {
4415 if kind == InstructionKind::Reassign {
4416 Ok(Some(IdentifierForAssignment::Global { name: gname }))
4417 } else {
4418 builder.record_error(CompilerErrorDetail {
4419 reason: "Could not find binding for declaration".to_string(),
4420 category: ErrorCategory::Invariant,
4421 loc,
4422 description: None,
4423 suggestions: None,
4424 })?;
4425 Ok(None)
4426 }
4427 }
4428 _ => {
4429 if kind == InstructionKind::Reassign {
4431 Ok(Some(IdentifierForAssignment::Global {
4432 name: name.to_string(),
4433 }))
4434 } else {
4435 builder.record_error(CompilerErrorDetail {
4436 reason: "Could not find binding for declaration".to_string(),
4437 category: ErrorCategory::Invariant,
4438 loc,
4439 description: None,
4440 suggestions: None,
4441 })?;
4442 Ok(None)
4443 }
4444 }
4445 }
4446}
4447
4448fn lower_assignment(
4449 builder: &mut HirBuilder,
4450 loc: Option<SourceLocation>,
4451 kind: InstructionKind,
4452 target: &react_compiler_ast::patterns::PatternLike,
4453 value: Place,
4454 assignment_style: AssignmentStyle,
4455) -> Result<Option<Place>, CompilerError> {
4456 use react_compiler_ast::patterns::PatternLike;
4457
4458 match target {
4459 PatternLike::Identifier(id) => {
4460 let id_loc = convert_opt_loc(&id.base.loc);
4461 let result = lower_identifier_for_assignment(
4462 builder,
4463 loc.clone(),
4464 id_loc,
4465 kind,
4466 &id.name,
4467 id.base.start.unwrap_or(0),
4468 id.base.node_id,
4469 )?;
4470 match result {
4471 None => {
4472 return Ok(None);
4474 }
4475 Some(IdentifierForAssignment::Global { name }) => {
4476 let temp = lower_value_to_temporary(
4477 builder,
4478 InstructionValue::StoreGlobal { name, value, loc },
4479 )?;
4480 return Ok(Some(temp));
4481 }
4482 Some(IdentifierForAssignment::Place(place)) => {
4483 let start = id.base.start.unwrap_or(0);
4484 if builder.is_context_identifier(&id.name, start, id.base.node_id) {
4485 let is_hoisted = builder
4487 .scope_info()
4488 .resolve_reference_for_node(id.base.node_id)
4489 .map(|b| builder.environment().is_hoisted_identifier(b.id.0))
4490 .unwrap_or(false);
4491 if kind == InstructionKind::Const && !is_hoisted {
4492 builder.record_error(CompilerErrorDetail {
4493 reason: "Expected `const` declaration not to be reassigned"
4494 .to_string(),
4495 category: ErrorCategory::Syntax,
4496 loc: loc.clone(),
4497 suggestions: None,
4498 description: None,
4499 })?;
4500 }
4501 if kind != InstructionKind::Const
4502 && kind != InstructionKind::Reassign
4503 && kind != InstructionKind::Let
4504 && kind != InstructionKind::Function
4505 {
4506 builder.record_error(CompilerErrorDetail {
4507 reason: "Unexpected context variable kind".to_string(),
4508 category: ErrorCategory::Syntax,
4509 loc: loc.clone(),
4510 suggestions: None,
4511 description: None,
4512 })?;
4513 let temp = lower_value_to_temporary(
4514 builder,
4515 InstructionValue::UnsupportedNode {
4516 node_type: Some("Identifier".to_string()),
4517 original_node: serialize_pattern(target),
4518 loc,
4519 },
4520 )?;
4521 return Ok(Some(temp));
4522 }
4523 let temp = lower_value_to_temporary(
4524 builder,
4525 InstructionValue::StoreContext {
4526 lvalue: LValue { place, kind },
4527 value,
4528 loc,
4529 },
4530 )?;
4531 return Ok(Some(temp));
4532 } else {
4533 let type_annotation = extract_type_annotation_name(&id.type_annotation);
4534 let temp = lower_value_to_temporary(
4535 builder,
4536 InstructionValue::StoreLocal {
4537 lvalue: LValue { place, kind },
4538 value,
4539 type_annotation,
4540 loc,
4541 },
4542 )?;
4543 return Ok(Some(temp));
4544 }
4545 }
4546 }
4547 }
4548
4549 PatternLike::MemberExpression(member) => {
4550 if kind != InstructionKind::Reassign {
4552 builder.record_error(CompilerErrorDetail {
4553 category: ErrorCategory::Invariant,
4554 reason: "MemberExpression may only appear in an assignment expression"
4555 .to_string(),
4556 description: None,
4557 loc: loc.clone(),
4558 suggestions: None,
4559 })?;
4560 return Ok(None);
4561 }
4562 let object = lower_expression_to_temporary(builder, &member.object)?;
4563 let temp = if !member.computed
4564 || matches!(
4565 &*member.property,
4566 react_compiler_ast::expressions::Expression::NumericLiteral(_)
4567 ) {
4568 match &*member.property {
4569 react_compiler_ast::expressions::Expression::Identifier(prop_id) => {
4570 lower_value_to_temporary(
4571 builder,
4572 InstructionValue::PropertyStore {
4573 object,
4574 property: PropertyLiteral::String(prop_id.name.clone()),
4575 value,
4576 loc,
4577 },
4578 )?
4579 }
4580 react_compiler_ast::expressions::Expression::NumericLiteral(num) => {
4581 lower_value_to_temporary(
4582 builder,
4583 InstructionValue::PropertyStore {
4584 object,
4585 property: PropertyLiteral::Number(FloatValue::new(
4586 num.precise_value(),
4587 )),
4588 value,
4589 loc,
4590 },
4591 )?
4592 }
4593 _ => {
4594 builder.record_error(CompilerErrorDetail {
4595 reason: format!("(BuildHIR::lowerAssignment) Handle {} properties in MemberExpression", expression_type_name(&member.property)),
4596 category: ErrorCategory::Todo,
4597 loc: expression_loc(&member.property),
4598 description: None,
4599 suggestions: None,
4600 })?;
4601 lower_value_to_temporary(
4602 builder,
4603 InstructionValue::UnsupportedNode {
4604 node_type: Some("MemberExpression".to_string()),
4605 original_node: serialize_pattern(target),
4606 loc,
4607 },
4608 )?
4609 }
4610 }
4611 } else {
4612 if matches!(
4613 &*member.property,
4614 react_compiler_ast::expressions::Expression::PrivateName(_)
4615 ) {
4616 builder.record_error(CompilerErrorDetail {
4617 reason: "(BuildHIR::lowerAssignment) Expected private name to appear as a non-computed property".to_string(),
4618 category: ErrorCategory::Todo,
4619 loc: expression_loc(&member.property),
4620 description: None,
4621 suggestions: None,
4622 })?;
4623 lower_value_to_temporary(
4624 builder,
4625 InstructionValue::UnsupportedNode {
4626 node_type: Some("MemberExpression".to_string()),
4627 original_node: serialize_pattern(target),
4628 loc,
4629 },
4630 )?
4631 } else {
4632 let property_place = lower_expression_to_temporary(builder, &member.property)?;
4633 lower_value_to_temporary(
4634 builder,
4635 InstructionValue::ComputedStore {
4636 object,
4637 property: property_place,
4638 value,
4639 loc,
4640 },
4641 )?
4642 }
4643 };
4644 Ok(Some(temp))
4645 }
4646
4647 PatternLike::ArrayPattern(pattern) => {
4648 let mut items: Vec<ArrayPatternElement> = Vec::new();
4649 let mut followups: Vec<(Place, &PatternLike)> = Vec::new();
4650
4651 let force_temporaries = if kind == InstructionKind::Reassign {
4654 let mut found = false;
4655 for elem in &pattern.elements {
4656 match elem {
4657 Some(PatternLike::Identifier(id)) => {
4658 let start = id.base.start.unwrap_or(0);
4659 if builder.is_context_identifier(&id.name, start, id.base.node_id) {
4660 found = true;
4661 break;
4662 }
4663 let ident_loc = convert_opt_loc(&id.base.loc);
4664 match builder.resolve_identifier(
4665 &id.name,
4666 start,
4667 ident_loc,
4668 id.base.node_id,
4669 )? {
4670 VariableBinding::Identifier { .. } => {}
4671 _ => {
4672 found = true;
4673 break;
4674 }
4675 }
4676 }
4677 _ => {
4678 found = true;
4682 break;
4683 }
4684 }
4685 }
4686 found
4687 } else {
4688 false
4689 };
4690
4691 for element in &pattern.elements {
4692 match element {
4693 None => {
4694 items.push(ArrayPatternElement::Hole);
4695 }
4696 Some(PatternLike::RestElement(rest)) => {
4697 match &*rest.argument {
4698 PatternLike::Identifier(id) => {
4699 let start = id.base.start.unwrap_or(0);
4700 let is_context =
4701 builder.is_context_identifier(&id.name, start, id.base.node_id);
4702 let can_use_direct = !force_temporaries
4703 && (matches!(assignment_style, AssignmentStyle::Assignment)
4704 || !is_context);
4705 if can_use_direct {
4706 match lower_identifier_for_assignment(
4707 builder,
4708 convert_opt_loc(&rest.base.loc),
4709 convert_opt_loc(&id.base.loc),
4710 kind,
4711 &id.name,
4712 start,
4713 id.base.node_id,
4714 )? {
4715 Some(IdentifierForAssignment::Place(place)) => {
4716 items.push(ArrayPatternElement::Spread(
4717 SpreadPattern { place },
4718 ));
4719 }
4720 Some(IdentifierForAssignment::Global { .. }) => {
4721 let temp = build_temporary_place(
4722 builder,
4723 convert_opt_loc(&rest.base.loc),
4724 );
4725 promote_temporary(builder, temp.identifier);
4726 items.push(ArrayPatternElement::Spread(
4727 SpreadPattern {
4728 place: temp.clone(),
4729 },
4730 ));
4731 followups.push((temp, &rest.argument));
4732 }
4733 None => {
4734 }
4736 }
4737 } else {
4738 let temp = build_temporary_place(
4739 builder,
4740 convert_opt_loc(&rest.base.loc),
4741 );
4742 promote_temporary(builder, temp.identifier);
4743 items.push(ArrayPatternElement::Spread(SpreadPattern {
4744 place: temp.clone(),
4745 }));
4746 followups.push((temp, &rest.argument));
4747 }
4748 }
4749 _ => {
4750 let temp =
4751 build_temporary_place(builder, convert_opt_loc(&rest.base.loc));
4752 promote_temporary(builder, temp.identifier);
4753 items.push(ArrayPatternElement::Spread(SpreadPattern {
4754 place: temp.clone(),
4755 }));
4756 followups.push((temp, &rest.argument));
4757 }
4758 }
4759 }
4760 Some(PatternLike::Identifier(id)) => {
4761 let start = id.base.start.unwrap_or(0);
4762 let is_context =
4763 builder.is_context_identifier(&id.name, start, id.base.node_id);
4764 let can_use_direct = !force_temporaries
4765 && (matches!(assignment_style, AssignmentStyle::Assignment)
4766 || !is_context);
4767 if can_use_direct {
4768 match lower_identifier_for_assignment(
4769 builder,
4770 convert_opt_loc(&id.base.loc),
4771 convert_opt_loc(&id.base.loc),
4772 kind,
4773 &id.name,
4774 start,
4775 id.base.node_id,
4776 )? {
4777 Some(IdentifierForAssignment::Place(place)) => {
4778 items.push(ArrayPatternElement::Place(place));
4779 }
4780 Some(IdentifierForAssignment::Global { .. }) => {
4781 let temp = build_temporary_place(
4782 builder,
4783 convert_opt_loc(&id.base.loc),
4784 );
4785 promote_temporary(builder, temp.identifier);
4786 items.push(ArrayPatternElement::Place(temp.clone()));
4787 followups.push((temp, element.as_ref().unwrap()));
4788 }
4789 None => {
4790 items.push(ArrayPatternElement::Hole);
4791 }
4792 }
4793 } else {
4794 let temp =
4796 build_temporary_place(builder, convert_opt_loc(&id.base.loc));
4797 promote_temporary(builder, temp.identifier);
4798 items.push(ArrayPatternElement::Place(temp.clone()));
4799 followups.push((temp, element.as_ref().unwrap()));
4800 }
4801 }
4802 Some(other) => {
4803 let elem_loc = pattern_like_hir_loc(other);
4805 let temp = build_temporary_place(builder, elem_loc);
4806 promote_temporary(builder, temp.identifier);
4807 items.push(ArrayPatternElement::Place(temp.clone()));
4808 followups.push((temp, other));
4809 }
4810 }
4811 }
4812
4813 let temporary = lower_value_to_temporary(
4814 builder,
4815 InstructionValue::Destructure {
4816 lvalue: LValuePattern {
4817 pattern: Pattern::Array(ArrayPattern {
4818 items,
4819 loc: convert_opt_loc(&pattern.base.loc),
4820 }),
4821 kind,
4822 },
4823 value: value.clone(),
4824 loc: loc.clone(),
4825 },
4826 )?;
4827
4828 for (place, path) in followups {
4829 let followup_loc = pattern_like_hir_loc(path).or(loc.clone());
4830 lower_assignment(builder, followup_loc, kind, path, place, assignment_style)?;
4831 }
4832 Ok(Some(temporary))
4833 }
4834
4835 PatternLike::ObjectPattern(pattern) => {
4836 let mut properties: Vec<ObjectPropertyOrSpread> = Vec::new();
4837 let mut followups: Vec<(Place, &PatternLike)> = Vec::new();
4838
4839 let force_temporaries = if kind == InstructionKind::Reassign {
4841 use react_compiler_ast::patterns::ObjectPatternProperty;
4842 let mut found = false;
4843 for prop in &pattern.properties {
4844 match prop {
4845 ObjectPatternProperty::RestElement(_) => {
4846 found = true;
4847 break;
4848 }
4849 ObjectPatternProperty::ObjectProperty(obj_prop) => match &*obj_prop.value {
4850 PatternLike::Identifier(id) => {
4851 let start = id.base.start.unwrap_or(0);
4852 let ident_loc = convert_opt_loc(&id.base.loc);
4853 match builder.resolve_identifier(
4854 &id.name,
4855 start,
4856 ident_loc,
4857 id.base.node_id,
4858 )? {
4859 VariableBinding::Identifier { .. } => {}
4860 _ => {
4861 found = true;
4862 break;
4863 }
4864 }
4865 }
4866 _ => {
4867 found = true;
4868 break;
4869 }
4870 },
4871 }
4872 }
4873 found
4874 } else {
4875 false
4876 };
4877
4878 for prop in &pattern.properties {
4879 match prop {
4880 react_compiler_ast::patterns::ObjectPatternProperty::RestElement(rest) => {
4881 match &*rest.argument {
4882 PatternLike::Identifier(id) => {
4883 let start = id.base.start.unwrap_or(0);
4884 let is_context =
4885 builder.is_context_identifier(&id.name, start, id.base.node_id);
4886 let can_use_direct = !force_temporaries
4887 && (matches!(assignment_style, AssignmentStyle::Assignment)
4888 || !is_context);
4889 if can_use_direct {
4890 match lower_identifier_for_assignment(
4891 builder,
4892 convert_opt_loc(&rest.base.loc),
4893 convert_opt_loc(&id.base.loc),
4894 kind,
4895 &id.name,
4896 start,
4897 id.base.node_id,
4898 )? {
4899 Some(IdentifierForAssignment::Place(place)) => {
4900 properties.push(ObjectPropertyOrSpread::Spread(
4901 SpreadPattern { place },
4902 ));
4903 }
4904 Some(IdentifierForAssignment::Global { .. }) => {
4905 builder.record_error(CompilerErrorDetail {
4906 reason: "Expected reassignment of globals to enable forceTemporaries".to_string(),
4907 category: ErrorCategory::Todo,
4908 loc: convert_opt_loc(&rest.base.loc),
4909 description: None,
4910 suggestions: None,
4911 })?;
4912 }
4913 None => {}
4914 }
4915 } else {
4916 let temp = build_temporary_place(
4917 builder,
4918 convert_opt_loc(&rest.base.loc),
4919 );
4920 promote_temporary(builder, temp.identifier);
4921 properties.push(ObjectPropertyOrSpread::Spread(
4922 SpreadPattern {
4923 place: temp.clone(),
4924 },
4925 ));
4926 followups.push((temp, &rest.argument));
4927 }
4928 }
4929 _ => {
4930 builder.record_error(CompilerErrorDetail {
4931 reason: format!("(BuildHIR::lowerAssignment) Handle {} rest element in ObjectPattern",
4932 match &*rest.argument {
4933 PatternLike::ObjectPattern(_) => "ObjectPattern",
4934 PatternLike::ArrayPattern(_) => "ArrayPattern",
4935 PatternLike::AssignmentPattern(_) => "AssignmentPattern",
4936 PatternLike::MemberExpression(_) => "MemberExpression",
4937 _ => "unknown",
4938 }),
4939 category: ErrorCategory::Todo,
4940 loc: convert_opt_loc(&rest.base.loc),
4941 description: None,
4942 suggestions: None,
4943 })?;
4944 }
4945 }
4946 }
4947 react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(
4948 obj_prop,
4949 ) => {
4950 if obj_prop.computed {
4951 builder.record_error(CompilerErrorDetail {
4952 reason: "(BuildHIR::lowerAssignment) Handle computed properties in ObjectPattern".to_string(),
4953 category: ErrorCategory::Todo,
4954 loc: convert_opt_loc(&obj_prop.base.loc),
4955 description: None,
4956 suggestions: None,
4957 })?;
4958 continue;
4959 }
4960
4961 let key = match lower_object_property_key(builder, &obj_prop.key, false)? {
4962 Some(k) => k,
4963 None => continue,
4964 };
4965
4966 match &*obj_prop.value {
4967 PatternLike::Identifier(id) => {
4968 let start = id.base.start.unwrap_or(0);
4969 let is_context =
4970 builder.is_context_identifier(&id.name, start, id.base.node_id);
4971 let can_use_direct = !force_temporaries
4972 && (matches!(assignment_style, AssignmentStyle::Assignment)
4973 || !is_context);
4974 if can_use_direct {
4975 match lower_identifier_for_assignment(
4976 builder,
4977 convert_opt_loc(&id.base.loc),
4978 convert_opt_loc(&id.base.loc),
4979 kind,
4980 &id.name,
4981 start,
4982 id.base.node_id,
4983 )? {
4984 Some(IdentifierForAssignment::Place(place)) => {
4985 properties.push(ObjectPropertyOrSpread::Property(
4986 ObjectProperty {
4987 key,
4988 property_type: ObjectPropertyType::Property,
4989 place,
4990 },
4991 ));
4992 }
4993 Some(IdentifierForAssignment::Global { .. }) => {
4994 builder.record_error(CompilerErrorDetail {
4995 reason: "Expected reassignment of globals to enable forceTemporaries".to_string(),
4996 category: ErrorCategory::Todo,
4997 loc: convert_opt_loc(&id.base.loc),
4998 description: None,
4999 suggestions: None,
5000 })?;
5001 }
5002 None => {
5003 continue;
5004 }
5005 }
5006 } else {
5007 let temp = build_temporary_place(
5009 builder,
5010 convert_opt_loc(&id.base.loc),
5011 );
5012 promote_temporary(builder, temp.identifier);
5013 properties.push(ObjectPropertyOrSpread::Property(
5014 ObjectProperty {
5015 key,
5016 property_type: ObjectPropertyType::Property,
5017 place: temp.clone(),
5018 },
5019 ));
5020 followups.push((temp, &*obj_prop.value));
5021 }
5022 }
5023 other => {
5024 let elem_loc = pattern_like_hir_loc(other);
5026 let temp = build_temporary_place(builder, elem_loc);
5027 promote_temporary(builder, temp.identifier);
5028 properties.push(ObjectPropertyOrSpread::Property(ObjectProperty {
5029 key,
5030 property_type: ObjectPropertyType::Property,
5031 place: temp.clone(),
5032 }));
5033 followups.push((temp, other));
5034 }
5035 }
5036 }
5037 }
5038 }
5039
5040 let temporary = lower_value_to_temporary(
5041 builder,
5042 InstructionValue::Destructure {
5043 lvalue: LValuePattern {
5044 pattern: Pattern::Object(ObjectPattern {
5045 properties,
5046 loc: convert_opt_loc(&pattern.base.loc),
5047 }),
5048 kind,
5049 },
5050 value: value.clone(),
5051 loc: loc.clone(),
5052 },
5053 )?;
5054
5055 for (place, path) in followups {
5056 let followup_loc = pattern_like_hir_loc(path).or(loc.clone());
5057 lower_assignment(builder, followup_loc, kind, path, place, assignment_style)?;
5058 }
5059 Ok(Some(temporary))
5060 }
5061
5062 PatternLike::AssignmentPattern(pattern) => {
5063 let pat_loc = convert_opt_loc(&pattern.base.loc);
5065
5066 let temp = build_temporary_place(builder, pat_loc.clone());
5067
5068 let test_block = builder.reserve(BlockKind::Value);
5069 let continuation_block = builder.reserve(builder.current_block_kind());
5070
5071 let consequent = builder.try_enter(BlockKind::Value, |builder, _| {
5073 let default_value = lower_reorderable_expression(builder, &pattern.right)?;
5074 lower_value_to_temporary(
5075 builder,
5076 InstructionValue::StoreLocal {
5077 lvalue: LValue {
5078 place: temp.clone(),
5079 kind: InstructionKind::Const,
5080 },
5081 value: default_value,
5082 type_annotation: None,
5083 loc: pat_loc.clone(),
5084 },
5085 )?;
5086 Ok(Terminal::Goto {
5087 block: continuation_block.id,
5088 variant: GotoVariant::Break,
5089 id: EvaluationOrder(0),
5090 loc: pat_loc.clone(),
5091 })
5092 });
5093
5094 let alternate = builder.try_enter(BlockKind::Value, |builder, _| {
5096 lower_value_to_temporary(
5097 builder,
5098 InstructionValue::StoreLocal {
5099 lvalue: LValue {
5100 place: temp.clone(),
5101 kind: InstructionKind::Const,
5102 },
5103 value: value.clone(),
5104 type_annotation: None,
5105 loc: pat_loc.clone(),
5106 },
5107 )?;
5108 Ok(Terminal::Goto {
5109 block: continuation_block.id,
5110 variant: GotoVariant::Break,
5111 id: EvaluationOrder(0),
5112 loc: pat_loc.clone(),
5113 })
5114 });
5115
5116 builder.terminate_with_continuation(
5118 Terminal::Ternary {
5119 test: test_block.id,
5120 fallthrough: continuation_block.id,
5121 id: EvaluationOrder(0),
5122 loc: pat_loc.clone(),
5123 },
5124 test_block,
5125 );
5126
5127 let undef = lower_value_to_temporary(
5129 builder,
5130 InstructionValue::Primitive {
5131 value: PrimitiveValue::Undefined,
5132 loc: pat_loc.clone(),
5133 },
5134 )?;
5135 let test = lower_value_to_temporary(
5136 builder,
5137 InstructionValue::BinaryExpression {
5138 left: value,
5139 operator: BinaryOperator::StrictEqual,
5140 right: undef,
5141 loc: pat_loc.clone(),
5142 },
5143 )?;
5144 builder.terminate_with_continuation(
5145 Terminal::Branch {
5146 test,
5147 consequent: consequent?,
5148 alternate: alternate?,
5149 fallthrough: continuation_block.id,
5150 id: EvaluationOrder(0),
5151 loc: pat_loc.clone(),
5152 },
5153 continuation_block,
5154 );
5155
5156 Ok(lower_assignment(
5158 builder,
5159 pat_loc,
5160 kind,
5161 &pattern.left,
5162 temp,
5163 assignment_style,
5164 )?)
5165 }
5166
5167 PatternLike::RestElement(rest) => {
5168 Ok(lower_assignment(
5170 builder,
5171 loc,
5172 kind,
5173 &rest.argument,
5174 value,
5175 assignment_style,
5176 )?)
5177 }
5178
5179 PatternLike::TSAsExpression(_)
5186 | PatternLike::TSSatisfiesExpression(_)
5187 | PatternLike::TSNonNullExpression(_)
5188 | PatternLike::TSTypeAssertion(_)
5189 | PatternLike::TypeCastExpression(_) => Ok(None),
5190 }
5191}
5192
5193fn pattern_like_hir_loc(pat: &react_compiler_ast::patterns::PatternLike) -> Option<SourceLocation> {
5195 convert_opt_loc(&pattern_like_loc(pat))
5196}
5197
5198fn lower_optional_member_expression(
5199 builder: &mut HirBuilder,
5200 expr: &react_compiler_ast::expressions::OptionalMemberExpression,
5201) -> Result<InstructionValue, CompilerError> {
5202 let place = lower_optional_member_expression_impl(builder, expr, None)?.1;
5203 Ok(InstructionValue::LoadLocal {
5204 loc: place.loc.clone(),
5205 place,
5206 })
5207}
5208
5209fn lower_optional_member_expression_impl(
5213 builder: &mut HirBuilder,
5214 expr: &react_compiler_ast::expressions::OptionalMemberExpression,
5215 parent_alternate: Option<BlockId>,
5216) -> Result<(Place, Place), CompilerError> {
5217 use react_compiler_ast::expressions::Expression;
5218 let optional = expr.optional;
5219 let loc = convert_opt_loc(&expr.base.loc);
5220 let place = build_temporary_place(builder, loc.clone());
5221 let continuation_block = builder.reserve(builder.current_block_kind());
5222 let continuation_id = continuation_block.id;
5223 let consequent = builder.reserve(BlockKind::Value);
5224
5225 let alternate = if let Some(parent_alt) = parent_alternate {
5228 Ok(parent_alt)
5229 } else {
5230 builder.try_enter(BlockKind::Value, |builder, _block_id| {
5231 let temp = lower_value_to_temporary(
5232 builder,
5233 InstructionValue::Primitive {
5234 value: PrimitiveValue::Undefined,
5235 loc: loc.clone(),
5236 },
5237 )?;
5238 lower_value_to_temporary(
5239 builder,
5240 InstructionValue::StoreLocal {
5241 lvalue: LValue {
5242 kind: InstructionKind::Const,
5243 place: place.clone(),
5244 },
5245 value: temp,
5246 type_annotation: None,
5247 loc: loc.clone(),
5248 },
5249 )?;
5250 Ok(Terminal::Goto {
5251 block: continuation_id,
5252 variant: GotoVariant::Break,
5253 id: EvaluationOrder(0),
5254 loc: loc.clone(),
5255 })
5256 })
5257 }?;
5258
5259 let mut object: Option<Place> = None;
5260 let test_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
5261 match expr.object.as_ref() {
5262 Expression::OptionalMemberExpression(opt_member) => {
5263 let (_obj, value) =
5264 lower_optional_member_expression_impl(builder, opt_member, Some(alternate))?;
5265 object = Some(value);
5266 }
5267 Expression::OptionalCallExpression(opt_call) => {
5268 let value =
5269 lower_optional_call_expression_impl(builder, opt_call, Some(alternate))?;
5270 let value_place = lower_value_to_temporary(builder, value)?;
5271 object = Some(value_place);
5272 }
5273 other => {
5274 object = Some(lower_expression_to_temporary(builder, other)?);
5275 }
5276 }
5277 let test_place = object.as_ref().unwrap().clone();
5278 Ok(Terminal::Branch {
5279 test: test_place,
5280 consequent: consequent.id,
5281 alternate,
5282 fallthrough: continuation_id,
5283 id: EvaluationOrder(0),
5284 loc: loc.clone(),
5285 })
5286 });
5287
5288 let obj = object.unwrap();
5289
5290 builder.try_enter_reserved(consequent, |builder| {
5292 let lowered = lower_member_expression_with_object(builder, expr, obj.clone())?;
5293 let temp = lower_value_to_temporary(builder, lowered.value)?;
5294 lower_value_to_temporary(
5295 builder,
5296 InstructionValue::StoreLocal {
5297 lvalue: LValue {
5298 kind: InstructionKind::Const,
5299 place: place.clone(),
5300 },
5301 value: temp,
5302 type_annotation: None,
5303 loc: loc.clone(),
5304 },
5305 )?;
5306 Ok(Terminal::Goto {
5307 block: continuation_id,
5308 variant: GotoVariant::Break,
5309 id: EvaluationOrder(0),
5310 loc: loc.clone(),
5311 })
5312 })?;
5313
5314 builder.terminate_with_continuation(
5315 Terminal::Optional {
5316 optional,
5317 test: test_block?,
5318 fallthrough: continuation_id,
5319 id: EvaluationOrder(0),
5320 loc: loc.clone(),
5321 },
5322 continuation_block,
5323 );
5324
5325 Ok((obj, place))
5326}
5327
5328fn lower_optional_call_expression(
5329 builder: &mut HirBuilder,
5330 expr: &react_compiler_ast::expressions::OptionalCallExpression,
5331) -> Result<InstructionValue, CompilerError> {
5332 Ok(lower_optional_call_expression_impl(builder, expr, None)?)
5333}
5334
5335fn lower_optional_call_expression_impl(
5336 builder: &mut HirBuilder,
5337 expr: &react_compiler_ast::expressions::OptionalCallExpression,
5338 parent_alternate: Option<BlockId>,
5339) -> Result<InstructionValue, CompilerError> {
5340 use react_compiler_ast::expressions::Expression;
5341 let optional = expr.optional;
5342 let loc = convert_opt_loc(&expr.base.loc);
5343 let place = build_temporary_place(builder, loc.clone());
5344 let continuation_block = builder.reserve(builder.current_block_kind());
5345 let continuation_id = continuation_block.id;
5346 let consequent = builder.reserve(BlockKind::Value);
5347
5348 let alternate = if let Some(parent_alt) = parent_alternate {
5350 Ok(parent_alt)
5351 } else {
5352 builder.try_enter(BlockKind::Value, |builder, _block_id| {
5353 let temp = lower_value_to_temporary(
5354 builder,
5355 InstructionValue::Primitive {
5356 value: PrimitiveValue::Undefined,
5357 loc: loc.clone(),
5358 },
5359 )?;
5360 lower_value_to_temporary(
5361 builder,
5362 InstructionValue::StoreLocal {
5363 lvalue: LValue {
5364 kind: InstructionKind::Const,
5365 place: place.clone(),
5366 },
5367 value: temp,
5368 type_annotation: None,
5369 loc: loc.clone(),
5370 },
5371 )?;
5372 Ok(Terminal::Goto {
5373 block: continuation_id,
5374 variant: GotoVariant::Break,
5375 id: EvaluationOrder(0),
5376 loc: loc.clone(),
5377 })
5378 })
5379 }?;
5380
5381 enum CalleeInfo {
5383 CallExpression { callee: Place },
5384 MethodCall { receiver: Place, property: Place },
5385 }
5386
5387 let mut callee_info: Option<CalleeInfo> = None;
5388
5389 let test_block = builder.try_enter(BlockKind::Value, |builder, _block_id| {
5390 match expr.callee.as_ref() {
5391 Expression::OptionalCallExpression(opt_call) => {
5392 let value =
5393 lower_optional_call_expression_impl(builder, opt_call, Some(alternate))?;
5394 let value_place = lower_value_to_temporary(builder, value)?;
5395 callee_info = Some(CalleeInfo::CallExpression {
5396 callee: value_place,
5397 });
5398 }
5399 Expression::OptionalMemberExpression(opt_member) => {
5400 let (obj, value) =
5401 lower_optional_member_expression_impl(builder, opt_member, Some(alternate))?;
5402 callee_info = Some(CalleeInfo::MethodCall {
5403 receiver: obj,
5404 property: value,
5405 });
5406 }
5407 Expression::MemberExpression(member) => {
5408 let lowered = lower_member_expression(builder, member)?;
5409 let property_place = lower_value_to_temporary(builder, lowered.value)?;
5410 callee_info = Some(CalleeInfo::MethodCall {
5411 receiver: lowered.object,
5412 property: property_place,
5413 });
5414 }
5415 other => {
5416 let callee_place = lower_expression_to_temporary(builder, other)?;
5417 callee_info = Some(CalleeInfo::CallExpression {
5418 callee: callee_place,
5419 });
5420 }
5421 }
5422
5423 let test_place = match callee_info.as_ref().unwrap() {
5424 CalleeInfo::CallExpression { callee } => callee.clone(),
5425 CalleeInfo::MethodCall { property, .. } => property.clone(),
5426 };
5427
5428 Ok(Terminal::Branch {
5429 test: test_place,
5430 consequent: consequent.id,
5431 alternate,
5432 fallthrough: continuation_id,
5433 id: EvaluationOrder(0),
5434 loc: loc.clone(),
5435 })
5436 });
5437
5438 builder.try_enter_reserved(consequent, |builder| {
5440 let args = lower_arguments(builder, &expr.arguments)?;
5441 let temp = build_temporary_place(builder, loc.clone());
5442
5443 match callee_info.as_ref().unwrap() {
5444 CalleeInfo::CallExpression { callee } => {
5445 builder.push(Instruction {
5446 id: EvaluationOrder(0),
5447 lvalue: temp.clone(),
5448 value: InstructionValue::CallExpression {
5449 callee: callee.clone(),
5450 args,
5451 loc: loc.clone(),
5452 },
5453 loc: loc.clone(),
5454 effects: None,
5455 });
5456 }
5457 CalleeInfo::MethodCall { receiver, property } => {
5458 builder.push(Instruction {
5459 id: EvaluationOrder(0),
5460 lvalue: temp.clone(),
5461 value: InstructionValue::MethodCall {
5462 receiver: receiver.clone(),
5463 property: property.clone(),
5464 args,
5465 loc: loc.clone(),
5466 },
5467 loc: loc.clone(),
5468 effects: None,
5469 });
5470 }
5471 }
5472
5473 lower_value_to_temporary(
5474 builder,
5475 InstructionValue::StoreLocal {
5476 lvalue: LValue {
5477 kind: InstructionKind::Const,
5478 place: place.clone(),
5479 },
5480 value: temp,
5481 type_annotation: None,
5482 loc: loc.clone(),
5483 },
5484 )?;
5485 Ok(Terminal::Goto {
5486 block: continuation_id,
5487 variant: GotoVariant::Break,
5488 id: EvaluationOrder(0),
5489 loc: loc.clone(),
5490 })
5491 })?;
5492
5493 builder.terminate_with_continuation(
5494 Terminal::Optional {
5495 optional,
5496 test: test_block?,
5497 fallthrough: continuation_id,
5498 id: EvaluationOrder(0),
5499 loc: loc.clone(),
5500 },
5501 continuation_block,
5502 );
5503
5504 Ok(InstructionValue::LoadLocal {
5505 place: place.clone(),
5506 loc: place.loc,
5507 })
5508}
5509
5510fn lower_function_to_value(
5511 builder: &mut HirBuilder,
5512 expr: &react_compiler_ast::expressions::Expression,
5513 expr_type: FunctionExpressionType,
5514) -> Result<InstructionValue, CompilerDiagnostic> {
5515 use react_compiler_ast::expressions::Expression;
5516 let loc = match expr {
5517 Expression::ArrowFunctionExpression(arrow) => convert_opt_loc(&arrow.base.loc),
5518 Expression::FunctionExpression(func) => convert_opt_loc(&func.base.loc),
5519 _ => None,
5520 };
5521 let name = match expr {
5522 Expression::FunctionExpression(func) => func.id.as_ref().map(|id| id.name.clone()),
5523 _ => None,
5524 };
5525 let lowered_func = lower_function(builder, expr)?;
5526 Ok(InstructionValue::FunctionExpression {
5527 name,
5528 name_hint: None,
5529 lowered_func,
5530 expr_type,
5531 loc,
5532 })
5533}
5534
5535fn lower_function(
5536 builder: &mut HirBuilder,
5537 expr: &react_compiler_ast::expressions::Expression,
5538) -> Result<LoweredFunction, CompilerDiagnostic> {
5539 use react_compiler_ast::expressions::Expression;
5540
5541 let (params, body, id, generator, is_async, func_start, func_end, func_loc, func_node_id) =
5543 match expr {
5544 Expression::ArrowFunctionExpression(arrow) => {
5545 let body = match arrow.body.as_ref() {
5546 react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
5547 FunctionBody::Block(block)
5548 }
5549 react_compiler_ast::expressions::ArrowFunctionBody::Expression(expr) => {
5550 FunctionBody::Expression(expr)
5551 }
5552 };
5553 (
5554 &arrow.params[..],
5555 body,
5556 None::<&str>,
5557 arrow.generator,
5558 arrow.is_async,
5559 arrow.base.start.unwrap_or(0),
5560 arrow.base.end.unwrap_or(0),
5561 convert_opt_loc(&arrow.base.loc),
5562 arrow.base.node_id,
5563 )
5564 }
5565 Expression::FunctionExpression(func) => (
5566 &func.params[..],
5567 FunctionBody::Block(&func.body),
5568 func.id.as_ref().map(|id| id.name.as_str()),
5569 func.generator,
5570 func.is_async,
5571 func.base.start.unwrap_or(0),
5572 func.base.end.unwrap_or(0),
5573 convert_opt_loc(&func.base.loc),
5574 func.base.node_id,
5575 ),
5576 _ => {
5577 return Err(CompilerDiagnostic::new(
5578 ErrorCategory::Invariant,
5579 "lower_function called with non-function expression",
5580 None,
5581 ));
5582 }
5583 };
5584
5585 let function_scope =
5588 if let Some(scope) = builder.scope_info().resolve_scope_for_node(func_node_id) {
5589 scope
5590 } else if func_start < func_end {
5591 builder.scope_info().program_scope
5592 } else {
5593 let parent = builder.function_scope();
5594 let scope_info = builder.scope_info();
5595 let mapped: std::collections::HashSet<react_compiler_ast::scope::ScopeId> =
5596 scope_info.node_id_to_scope.values().copied().collect();
5597 let param_names: Vec<String> = params
5598 .iter()
5599 .filter_map(|p| {
5600 if let react_compiler_ast::patterns::PatternLike::Identifier(id) = p {
5601 Some(id.name.clone())
5602 } else {
5603 None
5604 }
5605 })
5606 .collect();
5607 let mut descendants = std::collections::HashSet::new();
5608 descendants.insert(parent);
5609 let mut changed = true;
5610 while changed {
5611 changed = false;
5612 for (i, scope) in scope_info.scopes.iter().enumerate() {
5613 let sid = react_compiler_ast::scope::ScopeId(i as u32);
5614 if let Some(p) = scope.parent {
5615 if descendants.contains(&p) && !descendants.contains(&sid) {
5616 descendants.insert(sid);
5617 changed = true;
5618 }
5619 }
5620 }
5621 }
5622 let mut found = scope_info.program_scope;
5623 for (i, scope) in scope_info.scopes.iter().enumerate() {
5624 let sid = react_compiler_ast::scope::ScopeId(i as u32);
5625 if let Some(p) = scope.parent {
5626 if descendants.contains(&p)
5627 && matches!(scope.kind, ScopeKind::Function)
5628 && !mapped.contains(&sid)
5629 && !builder.is_synthetic_scope_claimed(sid)
5630 {
5631 if !param_names.is_empty() {
5632 let all_match = param_names
5633 .iter()
5634 .all(|name| scope.bindings.contains_key(name));
5635 if !all_match {
5636 continue;
5637 }
5638 }
5639 found = sid;
5640 break;
5641 }
5642 }
5643 }
5644 builder.claim_synthetic_scope(found);
5645 found
5646 };
5647
5648 let component_scope = builder.component_scope();
5649 let scope_info = builder.scope_info();
5650
5651 let parent_bindings = builder.bindings().clone();
5652 let parent_used_names = builder.used_names().clone();
5653 let context_ids = builder.context_identifiers().clone();
5654 let ident_locs = builder.identifier_locs();
5655
5656 let ref_override = if func_start >= func_end {
5659 Some(collect_identifier_node_ids_from_body(&body))
5660 } else {
5661 None
5662 };
5663
5664 let captured_context = gather_captured_context(
5666 scope_info,
5667 function_scope,
5668 component_scope,
5669 func_start,
5670 func_end,
5671 ident_locs,
5672 ref_override.as_ref(),
5673 );
5674 let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5675 let parent_context = builder.context().clone();
5676 let mut merged = parent_context;
5677 for (k, v) in captured_context {
5678 merged.insert(k, v);
5679 }
5680 merged
5681 };
5682
5683 let (scope_info, env) = builder.scope_info_and_env_mut();
5685 let (hir_func, child_used_names, child_bindings) = lower_inner(
5686 params,
5687 body,
5688 id,
5689 generator,
5690 is_async,
5691 func_loc,
5692 scope_info,
5693 env,
5694 Some(parent_bindings),
5695 Some(parent_used_names),
5696 merged_context,
5697 function_scope,
5698 component_scope,
5699 &context_ids,
5700 false, ident_locs,
5702 )?;
5703
5704 builder.merge_used_names(child_used_names);
5705 builder.merge_bindings(child_bindings);
5706
5707 let func_id = builder.environment_mut().add_function(hir_func);
5708 Ok(LoweredFunction { func: func_id })
5709}
5710
5711fn lower_function_declaration(
5713 builder: &mut HirBuilder,
5714 func_decl: &react_compiler_ast::statements::FunctionDeclaration,
5715) -> Result<(), CompilerError> {
5716 let loc = convert_opt_loc(&func_decl.base.loc);
5717 let func_start = func_decl.base.start.unwrap_or(0);
5718 let func_end = func_decl.base.end.unwrap_or(0);
5719
5720 let func_name = func_decl.id.as_ref().map(|id| id.name.clone());
5721
5722 let function_scope = builder
5724 .scope_info()
5725 .resolve_scope_for_node(func_decl.base.node_id)
5726 .unwrap_or(builder.scope_info().program_scope);
5727
5728 let component_scope = builder.component_scope();
5729 let scope_info = builder.scope_info();
5730
5731 let parent_bindings = builder.bindings().clone();
5732 let parent_used_names = builder.used_names().clone();
5733 let context_ids = builder.context_identifiers().clone();
5734 let ident_locs = builder.identifier_locs();
5735
5736 let captured_context = gather_captured_context(
5738 scope_info,
5739 function_scope,
5740 component_scope,
5741 func_start,
5742 func_end,
5743 ident_locs,
5744 None,
5745 );
5746 let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5747 let parent_context = builder.context().clone();
5748 let mut merged = parent_context;
5749 for (k, v) in captured_context {
5750 merged.insert(k, v);
5751 }
5752 merged
5753 };
5754
5755 let (scope_info, env) = builder.scope_info_and_env_mut();
5756 let (hir_func, child_used_names, child_bindings) = lower_inner(
5757 &func_decl.params,
5758 FunctionBody::Block(&func_decl.body),
5759 func_decl.id.as_ref().map(|id| id.name.as_str()),
5760 func_decl.generator,
5761 func_decl.is_async,
5762 loc.clone(),
5763 scope_info,
5764 env,
5765 Some(parent_bindings),
5766 Some(parent_used_names),
5767 merged_context,
5768 function_scope,
5769 component_scope,
5770 &context_ids,
5771 false, ident_locs,
5773 )?;
5774
5775 builder.merge_used_names(child_used_names);
5776 builder.merge_bindings(child_bindings);
5777
5778 let func_id = builder.environment_mut().add_function(hir_func);
5779 let lowered_func = LoweredFunction { func: func_id };
5780
5781 let fn_value = InstructionValue::FunctionExpression {
5783 name: func_name.clone(),
5784 name_hint: None,
5785 lowered_func,
5786 expr_type: FunctionExpressionType::FunctionDeclaration,
5787 loc: loc.clone(),
5788 };
5789 let fn_place = lower_value_to_temporary(builder, fn_value)?;
5790
5791 if let Some(ref name) = func_name {
5801 if let Some(id_node) = &func_decl.id {
5802 let start = id_node.base.start.unwrap_or(0);
5803 let ident_loc = convert_opt_loc(&id_node.base.loc);
5804 let scope_binding = builder.get_function_declaration_binding(function_scope, name);
5805 let mut is_context = false;
5806 let binding = match scope_binding {
5807 Some(binding_id) => {
5808 is_context = builder.is_context_binding(binding_id);
5809 let binding_kind = crate::convert_binding_kind(
5810 &builder.scope_info().bindings[binding_id.0 as usize].kind,
5811 );
5812 let identifier =
5813 builder.resolve_binding_with_loc(name, binding_id, ident_loc.clone())?;
5814 VariableBinding::Identifier {
5815 identifier,
5816 binding_kind,
5817 }
5818 }
5819 None => {
5820 let mut binding = builder.resolve_identifier(
5821 name,
5822 start,
5823 ident_loc.clone(),
5824 id_node.base.node_id,
5825 )?;
5826 if matches!(&binding, VariableBinding::Global { .. }) {
5827 let fallback = {
5832 let si = builder.scope_info();
5833 let scope_id = si
5834 .resolve_scope_for_node(func_decl.base.node_id)
5835 .unwrap_or(si.program_scope);
5836 si.get_binding(scope_id, name).map(|bid| {
5837 let b = &si.bindings[bid.0 as usize];
5838 (b.declaration_start.unwrap_or(0), b.declaration_node_id)
5839 })
5840 };
5841 if let Some((ds, ds_node_id)) = fallback {
5842 binding = builder.resolve_identifier(
5843 name,
5844 ds,
5845 ident_loc.clone(),
5846 ds_node_id,
5847 )?;
5848 }
5849 }
5850 if matches!(&binding, VariableBinding::Identifier { .. }) {
5851 is_context =
5852 builder.is_context_identifier(name, start, id_node.base.node_id);
5853 }
5854 binding
5855 }
5856 };
5857 match binding {
5858 VariableBinding::Identifier { identifier, .. } => {
5859 let place = Place {
5866 identifier,
5867 reactive: false,
5868 effect: Effect::Unknown,
5869 loc: loc.clone(),
5870 };
5871 if is_context {
5872 lower_value_to_temporary(
5873 builder,
5874 InstructionValue::StoreContext {
5875 lvalue: LValue {
5876 kind: InstructionKind::Function,
5877 place,
5878 },
5879 value: fn_place,
5880 loc,
5881 },
5882 )?;
5883 } else {
5884 lower_value_to_temporary(
5885 builder,
5886 InstructionValue::StoreLocal {
5887 lvalue: LValue {
5888 kind: InstructionKind::Function,
5889 place,
5890 },
5891 value: fn_place,
5892 type_annotation: None,
5893 loc,
5894 },
5895 )?;
5896 }
5897 }
5898 _ => {
5899 builder.record_error(CompilerErrorDetail {
5900 category: ErrorCategory::Invariant,
5901 reason: format!(
5902 "Could not find binding for function declaration `{}`",
5903 name
5904 ),
5905 description: None,
5906 loc,
5907 suggestions: None,
5908 })?;
5909 }
5910 }
5911 }
5912 }
5913 Ok(())
5914}
5915
5916fn lower_function_for_object_method(
5918 builder: &mut HirBuilder,
5919 method: &react_compiler_ast::expressions::ObjectMethod,
5920) -> Result<LoweredFunction, CompilerError> {
5921 let func_start = method.base.start.unwrap_or(0);
5922 let func_end = method.base.end.unwrap_or(0);
5923 let func_loc = convert_opt_loc(&method.base.loc);
5924
5925 let function_scope = builder
5926 .scope_info()
5927 .resolve_scope_for_node(method.base.node_id)
5928 .unwrap_or(builder.scope_info().program_scope);
5929
5930 let component_scope = builder.component_scope();
5931 let scope_info = builder.scope_info();
5932
5933 let parent_bindings = builder.bindings().clone();
5934 let parent_used_names = builder.used_names().clone();
5935 let context_ids = builder.context_identifiers().clone();
5936 let ident_locs = builder.identifier_locs();
5937
5938 let captured_context = gather_captured_context(
5939 scope_info,
5940 function_scope,
5941 component_scope,
5942 func_start,
5943 func_end,
5944 ident_locs,
5945 None,
5946 );
5947 let merged_context: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> = {
5948 let parent_context = builder.context().clone();
5949 let mut merged = parent_context;
5950 for (k, v) in captured_context {
5951 merged.insert(k, v);
5952 }
5953 merged
5954 };
5955
5956 let (scope_info, env) = builder.scope_info_and_env_mut();
5957 let (hir_func, child_used_names, child_bindings) = lower_inner(
5958 &method.params,
5959 FunctionBody::Block(&method.body),
5960 None,
5961 method.generator,
5962 method.is_async,
5963 func_loc,
5964 scope_info,
5965 env,
5966 Some(parent_bindings),
5967 Some(parent_used_names),
5968 merged_context,
5969 function_scope,
5970 component_scope,
5971 &context_ids,
5972 false, ident_locs,
5974 )?;
5975
5976 builder.merge_used_names(child_used_names);
5977 builder.merge_bindings(child_bindings);
5978
5979 let func_id = builder.environment_mut().add_function(hir_func);
5980 Ok(LoweredFunction { func: func_id })
5981}
5982
5983fn lower_inner(
5986 params: &[react_compiler_ast::patterns::PatternLike],
5987 body: FunctionBody<'_>,
5988 id: Option<&str>,
5989 generator: bool,
5990 is_async: bool,
5991 loc: Option<SourceLocation>,
5992 scope_info: &ScopeInfo,
5993 env: &mut Environment,
5994 parent_bindings: Option<IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>>,
5995 parent_used_names: Option<IndexMap<String, react_compiler_ast::scope::BindingId>>,
5996 context_map: IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>>,
5997 function_scope: react_compiler_ast::scope::ScopeId,
5998 component_scope: react_compiler_ast::scope::ScopeId,
5999 context_identifiers: &HashSet<react_compiler_ast::scope::BindingId>,
6000 is_top_level: bool,
6001 identifier_locs: &IdentifierLocIndex,
6002) -> Result<
6003 (
6004 HirFunction,
6005 IndexMap<String, react_compiler_ast::scope::BindingId>,
6006 IndexMap<react_compiler_ast::scope::BindingId, IdentifierId>,
6007 ),
6008 CompilerError,
6009> {
6010 validate_ts_this_parameter(scope_info, function_scope)?;
6011
6012 let mut builder = HirBuilder::new(
6013 env,
6014 scope_info,
6015 function_scope,
6016 component_scope,
6017 context_identifiers.clone(),
6018 parent_bindings,
6019 Some(context_map.clone()),
6020 None,
6021 parent_used_names,
6022 identifier_locs,
6023 );
6024
6025 let mut context: Vec<Place> = Vec::new();
6027 for (&binding_id, ctx_loc) in &context_map {
6028 let binding = &scope_info.bindings[binding_id.0 as usize];
6029 let identifier = builder.resolve_binding(&binding.name, binding_id)?;
6030 context.push(Place {
6031 identifier,
6032 effect: Effect::Unknown,
6033 reactive: false,
6034 loc: ctx_loc.clone(),
6035 });
6036 }
6037
6038 let mut hir_params: Vec<ParamPattern> = Vec::new();
6040 for param in params {
6041 match param {
6042 react_compiler_ast::patterns::PatternLike::Identifier(ident) => {
6043 if is_always_reserved_word(&ident.name) {
6044 return Err(CompilerError::from(reserved_identifier_diagnostic(
6045 &ident.name,
6046 )));
6047 }
6048 let start = ident.base.start.unwrap_or(0);
6049 let param_loc = convert_opt_loc(&ident.base.loc);
6050 let mut binding = builder.resolve_identifier(
6051 &ident.name,
6052 start,
6053 param_loc.clone(),
6054 ident.base.node_id,
6055 )?;
6056 if !matches!(binding, VariableBinding::Identifier { .. }) {
6057 if let Some((binding_id, binding_data)) = builder
6061 .scope_info()
6062 .find_binding_id_in_descendants(&ident.name, builder.function_scope())
6063 {
6064 let binding_kind = crate::convert_binding_kind(&binding_data.kind);
6065 let identifier = builder.resolve_binding_with_loc(
6066 &ident.name,
6067 binding_id,
6068 param_loc.clone(),
6069 )?;
6070 binding = VariableBinding::Identifier {
6071 identifier,
6072 binding_kind,
6073 };
6074 }
6075 }
6076 match binding {
6077 VariableBinding::Identifier { identifier, .. } => {
6078 builder.set_identifier_declaration_loc(identifier, ¶m_loc);
6079 let place = Place {
6080 identifier,
6081 effect: Effect::Unknown,
6082 reactive: false,
6083 loc: param_loc,
6084 };
6085 hir_params.push(ParamPattern::Place(place));
6086 }
6087 _ => {
6088 builder.record_diagnostic(
6089 CompilerDiagnostic::new(
6090 ErrorCategory::Invariant,
6091 "Could not find binding",
6092 Some(format!(
6093 "[BuildHIR] Could not find binding for param `{}`",
6094 ident.name
6095 )),
6096 )
6097 .with_detail(
6098 CompilerDiagnosticDetail::Error {
6099 loc: convert_opt_loc(&ident.base.loc),
6100 message: Some("Could not find binding".to_string()),
6101 identifier_name: None,
6102 },
6103 ),
6104 );
6105 }
6106 }
6107 }
6108 react_compiler_ast::patterns::PatternLike::RestElement(rest) => {
6109 let rest_loc = convert_opt_loc(&rest.base.loc);
6110 let place = build_temporary_place(&mut builder, rest_loc.clone());
6112 hir_params.push(ParamPattern::Spread(SpreadPattern {
6113 place: place.clone(),
6114 }));
6115 lower_assignment(
6117 &mut builder,
6118 rest_loc,
6119 InstructionKind::Let,
6120 &rest.argument,
6121 place,
6122 AssignmentStyle::Assignment,
6123 )?;
6124 }
6125 react_compiler_ast::patterns::PatternLike::ObjectPattern(_)
6126 | react_compiler_ast::patterns::PatternLike::ArrayPattern(_)
6127 | react_compiler_ast::patterns::PatternLike::AssignmentPattern(_) => {
6128 let param_loc = convert_opt_loc(&pattern_like_loc(param));
6129 let place = build_temporary_place(&mut builder, param_loc.clone());
6130 promote_temporary(&mut builder, place.identifier);
6131 hir_params.push(ParamPattern::Place(place.clone()));
6132 lower_assignment(
6133 &mut builder,
6134 param_loc,
6135 InstructionKind::Let,
6136 param,
6137 place,
6138 AssignmentStyle::Assignment,
6139 )?;
6140 }
6141 react_compiler_ast::patterns::PatternLike::MemberExpression(member) => {
6142 builder.record_diagnostic(
6143 CompilerDiagnostic::new(
6144 ErrorCategory::Todo,
6145 "Handle MemberExpression parameters",
6146 Some("[BuildHIR] Add support for MemberExpression parameters".to_string()),
6147 )
6148 .with_detail(CompilerDiagnosticDetail::Error {
6149 loc: convert_opt_loc(&member.base.loc),
6150 message: Some("Unsupported parameter type".to_string()),
6151 identifier_name: None,
6152 }),
6153 );
6154 }
6155 react_compiler_ast::patterns::PatternLike::TSAsExpression(_)
6156 | react_compiler_ast::patterns::PatternLike::TSSatisfiesExpression(_)
6157 | react_compiler_ast::patterns::PatternLike::TSNonNullExpression(_)
6158 | react_compiler_ast::patterns::PatternLike::TSTypeAssertion(_)
6159 | react_compiler_ast::patterns::PatternLike::TypeCastExpression(_) => {}
6160 }
6161 }
6162
6163 let mut directives: Vec<String> = Vec::new();
6165 match body {
6166 FunctionBody::Expression(expr) => {
6167 let fallthrough = builder.reserve(BlockKind::Block);
6168 let value = lower_expression_to_temporary(&mut builder, expr)?;
6169 builder.terminate_with_continuation(
6170 Terminal::Return {
6171 value,
6172 return_variant: ReturnVariant::Implicit,
6173 id: EvaluationOrder(0),
6174 loc: None,
6175 effects: None,
6176 },
6177 fallthrough,
6178 );
6179 }
6180 FunctionBody::Block(block) => {
6181 directives = block
6182 .directives
6183 .iter()
6184 .map(|d| d.value.value.clone())
6185 .collect();
6186 lower_block_statement_with_scope(&mut builder, block, function_scope)?;
6190 }
6191 }
6192
6193 let undefined_value = InstructionValue::Primitive {
6195 value: PrimitiveValue::Undefined,
6196 loc: None,
6197 };
6198 let return_value = lower_value_to_temporary(&mut builder, undefined_value)?;
6199 builder.terminate(
6200 Terminal::Return {
6201 value: return_value,
6202 return_variant: ReturnVariant::Void,
6203 id: EvaluationOrder(0),
6204 loc: None,
6205 effects: None,
6206 },
6207 None,
6208 );
6209
6210 let (hir_body, instructions, used_names, child_bindings) = builder.build()?;
6212
6213 let returns = crate::hir_builder::create_temporary_place(env, loc.clone());
6215
6216 Ok((
6217 HirFunction {
6218 loc,
6219 id: id.map(|s| s.to_string()),
6220 name_hint: None,
6221 fn_type: if is_top_level {
6222 env.fn_type
6223 } else {
6224 ReactFunctionType::Other
6225 },
6226 params: hir_params,
6227 return_type_annotation: None,
6228 returns,
6229 context,
6230 body: hir_body,
6231 instructions,
6232 generator,
6233 is_async,
6234 directives,
6235 aliasing_effects: None,
6236 },
6237 used_names,
6238 child_bindings,
6239 ))
6240}
6241
6242fn lower_jsx_element_name(
6243 builder: &mut HirBuilder,
6244 name: &react_compiler_ast::jsx::JSXElementName,
6245) -> Result<JsxTag, CompilerError> {
6246 use react_compiler_ast::jsx::JSXElementName;
6247 match name {
6248 JSXElementName::JSXIdentifier(id) => {
6249 let tag = &id.name;
6250 let loc = convert_opt_loc(&id.base.loc);
6251 let start = id.base.start.unwrap_or(0);
6252 if tag.starts_with(|c: char| c.is_ascii_uppercase()) {
6253 let place = lower_identifier(builder, tag, start, loc.clone(), id.base.node_id)?;
6255 let load_value = if builder.is_context_identifier(tag, start, id.base.node_id) {
6256 InstructionValue::LoadContext { place, loc }
6257 } else {
6258 InstructionValue::LoadLocal { place, loc }
6259 };
6260 let temp = lower_value_to_temporary(builder, load_value)?;
6261 Ok(JsxTag::Place(temp))
6262 } else {
6263 Ok(JsxTag::Builtin(BuiltinTag {
6265 name: tag.clone(),
6266 loc,
6267 }))
6268 }
6269 }
6270 JSXElementName::JSXMemberExpression(member) => {
6271 let place = lower_jsx_member_expression(builder, member)?;
6272 Ok(JsxTag::Place(place))
6273 }
6274 JSXElementName::JSXNamespacedName(ns) => {
6275 let namespace = &ns.namespace.name;
6276 let name = &ns.name.name;
6277 let tag = format!("{}:{}", namespace, name);
6278 let loc = convert_opt_loc(&ns.base.loc);
6279 if namespace.contains(':') || name.contains(':') {
6280 builder.record_error(CompilerErrorDetail {
6281 category: ErrorCategory::Syntax,
6282 reason: "Expected JSXNamespacedName to have no colons in the namespace or name"
6283 .to_string(),
6284 description: Some(format!("Got `{}` : `{}`", namespace, name)),
6285 loc: loc.clone(),
6286 suggestions: None,
6287 })?;
6288 }
6289 let place = lower_value_to_temporary(
6290 builder,
6291 InstructionValue::Primitive {
6292 value: PrimitiveValue::String(tag),
6293 loc: loc.clone(),
6294 },
6295 )?;
6296 Ok(JsxTag::Place(place))
6297 }
6298 }
6299}
6300
6301fn lower_jsx_member_expression(
6302 builder: &mut HirBuilder,
6303 expr: &react_compiler_ast::jsx::JSXMemberExpression,
6304) -> Result<Place, CompilerError> {
6305 use react_compiler_ast::jsx::JSXMemberExprObject;
6306 let expr_loc = convert_opt_loc(&expr.base.loc);
6308 let object = match &*expr.object {
6309 JSXMemberExprObject::JSXIdentifier(id) => {
6310 let id_loc = convert_opt_loc(&id.base.loc);
6311 let start = id.base.start.unwrap_or(0);
6312 let place = lower_identifier(builder, &id.name, start, id_loc, id.base.node_id)?;
6314 let load_value = if builder.is_context_identifier(&id.name, start, id.base.node_id) {
6315 InstructionValue::LoadContext {
6316 place,
6317 loc: expr_loc.clone(),
6318 }
6319 } else {
6320 InstructionValue::LoadLocal {
6321 place,
6322 loc: expr_loc.clone(),
6323 }
6324 };
6325 lower_value_to_temporary(builder, load_value)?
6326 }
6327 JSXMemberExprObject::JSXMemberExpression(inner) => {
6328 lower_jsx_member_expression(builder, inner)?
6329 }
6330 };
6331 let prop_name = &expr.property.name;
6332 let value = InstructionValue::PropertyLoad {
6333 object,
6334 property: PropertyLiteral::String(prop_name.clone()),
6335 loc: expr_loc,
6336 };
6337 Ok(lower_value_to_temporary(builder, value)?)
6338}
6339
6340fn lower_jsx_element(
6341 builder: &mut HirBuilder,
6342 child: &react_compiler_ast::jsx::JSXChild,
6343) -> Result<Option<Place>, CompilerError> {
6344 use react_compiler_ast::jsx::JSXChild;
6345 use react_compiler_ast::jsx::JSXExpressionContainerExpr;
6346 match child {
6347 JSXChild::JSXText(text) => {
6348 let value = if builder.fbt_depth > 0 {
6352 Some(text.value.clone())
6353 } else {
6354 trim_jsx_text(&text.value)
6355 };
6356 match value {
6357 None => Ok(None),
6358 Some(value) => {
6359 let loc = convert_opt_loc(&text.base.loc);
6360 let place = lower_value_to_temporary(
6361 builder,
6362 InstructionValue::JSXText { value, loc },
6363 )?;
6364 Ok(Some(place))
6365 }
6366 }
6367 }
6368 JSXChild::JSXElement(element) => {
6369 let value = lower_expression(
6370 builder,
6371 &react_compiler_ast::expressions::Expression::JSXElement(element.clone()),
6372 )?;
6373 Ok(Some(lower_value_to_temporary(builder, value)?))
6374 }
6375 JSXChild::JSXFragment(fragment) => {
6376 let value = lower_expression(
6377 builder,
6378 &react_compiler_ast::expressions::Expression::JSXFragment(fragment.clone()),
6379 )?;
6380 Ok(Some(lower_value_to_temporary(builder, value)?))
6381 }
6382 JSXChild::JSXExpressionContainer(container) => match &container.expression {
6383 JSXExpressionContainerExpr::JSXEmptyExpression(_) => Ok(None),
6384 JSXExpressionContainerExpr::Expression(expr) => {
6385 Ok(Some(lower_expression_to_temporary(builder, expr)?))
6386 }
6387 },
6388 JSXChild::JSXSpreadChild(spread) => Ok(Some(lower_expression_to_temporary(
6389 builder,
6390 &spread.expression,
6391 )?)),
6392 }
6393}
6394
6395fn split_line_endings(s: &str) -> Vec<&str> {
6397 let mut lines = Vec::new();
6398 let mut start = 0;
6399 let bytes = s.as_bytes();
6400 let mut i = 0;
6401 while i < bytes.len() {
6402 if bytes[i] == b'\r' {
6403 lines.push(&s[start..i]);
6404 if i + 1 < bytes.len() && bytes[i + 1] == b'\n' {
6405 i += 2;
6406 } else {
6407 i += 1;
6408 }
6409 start = i;
6410 } else if bytes[i] == b'\n' {
6411 lines.push(&s[start..i]);
6412 i += 1;
6413 start = i;
6414 } else {
6415 i += 1;
6416 }
6417 }
6418 lines.push(&s[start..]);
6419 lines
6420}
6421
6422fn trim_jsx_text(original: &str) -> Option<String> {
6425 let lines: Vec<&str> = split_line_endings(original);
6427
6428 let mut last_non_empty_line = 0;
6432 for (i, line) in lines.iter().enumerate() {
6433 if line.contains(|c: char| c != ' ' && c != '\t') {
6434 last_non_empty_line = i;
6435 }
6436 }
6437
6438 let mut str = String::new();
6439
6440 for (i, line) in lines.iter().enumerate() {
6441 let is_first_line = i == 0;
6442 let is_last_line = i == lines.len() - 1;
6443 let is_last_non_empty_line = i == last_non_empty_line;
6444
6445 let mut trimmed_line = line.replace('\t', " ");
6447
6448 if !is_first_line {
6450 trimmed_line = trimmed_line.trim_start_matches(' ').to_string();
6451 }
6452
6453 if !is_last_line {
6455 trimmed_line = trimmed_line.trim_end_matches(' ').to_string();
6456 }
6457
6458 if !trimmed_line.is_empty() {
6459 if !is_last_non_empty_line {
6460 trimmed_line.push(' ');
6461 }
6462 str.push_str(&trimmed_line);
6463 }
6464 }
6465
6466 if str.is_empty() { None } else { Some(str) }
6467}
6468
6469fn lower_object_method(
6470 builder: &mut HirBuilder,
6471 method: &react_compiler_ast::expressions::ObjectMethod,
6472) -> Result<Option<ObjectProperty>, CompilerError> {
6473 use react_compiler_ast::expressions::ObjectMethodKind;
6474 if !matches!(method.kind, ObjectMethodKind::Method) {
6475 let kind_str = match method.kind {
6476 ObjectMethodKind::Get => "get",
6477 ObjectMethodKind::Set => "set",
6478 ObjectMethodKind::Method => "method",
6479 };
6480 builder.record_error(CompilerErrorDetail {
6481 reason: format!(
6482 "(BuildHIR::lowerExpression) Handle {} functions in ObjectExpression",
6483 kind_str
6484 ),
6485 category: ErrorCategory::Todo,
6486 loc: convert_opt_loc(&method.base.loc),
6487 description: None,
6488 suggestions: None,
6489 })?;
6490 return Ok(None);
6491 }
6492 let key = lower_object_property_key(builder, &method.key, method.computed)?.unwrap_or(
6493 ObjectPropertyKey::String {
6494 name: String::new(),
6495 },
6496 );
6497
6498 let lowered_func = lower_function_for_object_method(builder, method)?;
6499
6500 let loc = convert_opt_loc(&method.base.loc);
6501 let method_value = InstructionValue::ObjectMethod {
6502 loc: loc.clone(),
6503 lowered_func,
6504 };
6505 let method_place = lower_value_to_temporary(builder, method_value)?;
6506
6507 Ok(Some(ObjectProperty {
6508 key,
6509 property_type: ObjectPropertyType::Method,
6510 place: method_place,
6511 }))
6512}
6513
6514fn lower_object_property_key(
6515 builder: &mut HirBuilder,
6516 key: &react_compiler_ast::expressions::Expression,
6517 computed: bool,
6518) -> Result<Option<ObjectPropertyKey>, CompilerError> {
6519 use react_compiler_ast::expressions::Expression;
6520 match key {
6521 Expression::StringLiteral(lit) => Ok(Some(ObjectPropertyKey::String {
6522 name: lit.value.clone(),
6523 })),
6524 Expression::Identifier(ident) if !computed => Ok(Some(ObjectPropertyKey::Identifier {
6525 name: ident.name.clone(),
6526 })),
6527 Expression::NumericLiteral(lit) if !computed => Ok(Some(ObjectPropertyKey::Identifier {
6528 name: lit.value.to_string(),
6529 })),
6530 _ if computed => {
6531 let place = lower_expression_to_temporary(builder, key)?;
6532 Ok(Some(ObjectPropertyKey::Computed { name: place }))
6533 }
6534 _ => {
6535 let loc = match key {
6536 Expression::Identifier(i) => convert_opt_loc(&i.base.loc),
6537 _ => None,
6538 };
6539 builder.record_error(CompilerErrorDetail {
6540 category: ErrorCategory::Todo,
6541 reason: "Unsupported key type in ObjectExpression".to_string(),
6542 description: None,
6543 loc,
6544 suggestions: None,
6545 })?;
6546 Ok(None)
6547 }
6548 }
6549}
6550
6551fn lower_reorderable_expression(
6552 builder: &mut HirBuilder,
6553 expr: &react_compiler_ast::expressions::Expression,
6554) -> Result<Place, CompilerError> {
6555 if !is_reorderable_expression(builder, expr, true) {
6556 builder.record_error(CompilerErrorDetail {
6557 category: ErrorCategory::Todo,
6558 reason: format!(
6559 "(BuildHIR::node.lowerReorderableExpression) Expression type `{}` cannot be safely reordered",
6560 expression_type_name(expr)
6561 ),
6562 description: None,
6563 loc: expression_loc(expr),
6564 suggestions: None,
6565 })?;
6566 }
6567 Ok(lower_expression_to_temporary(builder, expr)?)
6568}
6569
6570fn is_reorderable_expression(
6571 builder: &HirBuilder,
6572 expr: &react_compiler_ast::expressions::Expression,
6573 allow_local_identifiers: bool,
6574) -> bool {
6575 use react_compiler_ast::expressions::Expression;
6576 match expr {
6577 Expression::Identifier(ident) => {
6578 let binding = builder
6579 .scope_info()
6580 .resolve_reference_for_node(ident.base.node_id);
6581 match binding {
6582 None => {
6583 true
6585 }
6586 Some(b) => {
6587 if b.scope == builder.scope_info().program_scope {
6588 true
6590 } else {
6591 allow_local_identifiers
6592 }
6593 }
6594 }
6595 }
6596 Expression::RegExpLiteral(_)
6597 | Expression::StringLiteral(_)
6598 | Expression::NumericLiteral(_)
6599 | Expression::NullLiteral(_)
6600 | Expression::BooleanLiteral(_)
6601 | Expression::BigIntLiteral(_) => true,
6602 Expression::UnaryExpression(unary) => {
6603 use react_compiler_ast::operators::UnaryOperator;
6604 matches!(
6605 unary.operator,
6606 UnaryOperator::Not | UnaryOperator::Plus | UnaryOperator::Neg
6607 ) && is_reorderable_expression(builder, &unary.argument, allow_local_identifiers)
6608 }
6609 Expression::LogicalExpression(logical) => {
6610 is_reorderable_expression(builder, &logical.left, allow_local_identifiers)
6611 && is_reorderable_expression(builder, &logical.right, allow_local_identifiers)
6612 }
6613 Expression::ConditionalExpression(cond) => {
6614 is_reorderable_expression(builder, &cond.test, allow_local_identifiers)
6615 && is_reorderable_expression(builder, &cond.consequent, allow_local_identifiers)
6616 && is_reorderable_expression(builder, &cond.alternate, allow_local_identifiers)
6617 }
6618 Expression::ArrayExpression(arr) => {
6619 arr.elements.iter().all(|element| {
6620 match element {
6621 Some(e) => is_reorderable_expression(builder, e, allow_local_identifiers),
6622 None => false, }
6624 })
6625 }
6626 Expression::ObjectExpression(obj) => obj.properties.iter().all(|prop| match prop {
6627 react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(p) => {
6628 !p.computed && is_reorderable_expression(builder, &p.value, allow_local_identifiers)
6629 }
6630 _ => false,
6631 }),
6632 Expression::MemberExpression(member) => {
6633 let mut inner = member.object.as_ref();
6635 while let Expression::MemberExpression(m) = inner {
6636 inner = m.object.as_ref();
6637 }
6638 if let Expression::Identifier(ident) = inner {
6639 match builder
6640 .scope_info()
6641 .resolve_reference_for_node(ident.base.node_id)
6642 {
6643 None => true, Some(binding) => {
6645 binding.scope == builder.scope_info().program_scope
6647 }
6648 }
6649 } else {
6650 false
6651 }
6652 }
6653 Expression::ArrowFunctionExpression(arrow) => {
6654 use react_compiler_ast::expressions::ArrowFunctionBody;
6655 match arrow.body.as_ref() {
6656 ArrowFunctionBody::BlockStatement(block) => block.body.is_empty(),
6657 ArrowFunctionBody::Expression(body_expr) => {
6658 is_reorderable_expression(builder, body_expr, false)
6659 }
6660 }
6661 }
6662 Expression::CallExpression(call) => {
6663 is_reorderable_expression(builder, &call.callee, allow_local_identifiers)
6664 && call
6665 .arguments
6666 .iter()
6667 .all(|arg| is_reorderable_expression(builder, arg, allow_local_identifiers))
6668 }
6669 Expression::NewExpression(new_expr) => {
6670 is_reorderable_expression(builder, &new_expr.callee, allow_local_identifiers)
6671 && new_expr
6672 .arguments
6673 .iter()
6674 .all(|arg| is_reorderable_expression(builder, arg, allow_local_identifiers))
6675 }
6676 Expression::TSAsExpression(ts) => {
6678 is_reorderable_expression(builder, &ts.expression, allow_local_identifiers)
6679 }
6680 Expression::TSSatisfiesExpression(ts) => {
6681 is_reorderable_expression(builder, &ts.expression, allow_local_identifiers)
6682 }
6683 Expression::TSNonNullExpression(ts) => {
6684 is_reorderable_expression(builder, &ts.expression, allow_local_identifiers)
6685 }
6686 Expression::TSInstantiationExpression(ts) => {
6687 is_reorderable_expression(builder, &ts.expression, allow_local_identifiers)
6688 }
6689 Expression::TypeCastExpression(tc) => {
6690 is_reorderable_expression(builder, &tc.expression, allow_local_identifiers)
6691 }
6692 Expression::TSTypeAssertion(ts) => {
6693 is_reorderable_expression(builder, &ts.expression, allow_local_identifiers)
6694 }
6695 Expression::ParenthesizedExpression(p) => {
6696 is_reorderable_expression(builder, &p.expression, allow_local_identifiers)
6697 }
6698 _ => false,
6699 }
6700}
6701
6702fn get_type_annotation_name(val: &serde_json::Value) -> Option<String> {
6705 val.get("type")
6706 .and_then(|v| v.as_str())
6707 .map(|s| s.to_string())
6708}
6709
6710fn lower_type_annotation(val: &serde_json::Value, builder: &mut HirBuilder) -> Type {
6713 let type_name = match val.get("type").and_then(|v| v.as_str()) {
6714 Some(name) => name,
6715 None => return builder.make_type(),
6716 };
6717 match type_name {
6718 "GenericTypeAnnotation" => {
6719 if let Some(id) = val.get("id") {
6721 if id.get("type").and_then(|v| v.as_str()) == Some("Identifier") {
6722 if id.get("name").and_then(|v| v.as_str()) == Some("Array") {
6723 return Type::Object {
6724 shape_id: Some("BuiltInArray".to_string()),
6725 };
6726 }
6727 }
6728 }
6729 builder.make_type()
6730 }
6731 "TSTypeReference" => {
6732 if let Some(type_name_val) = val.get("typeName") {
6733 if type_name_val.get("type").and_then(|v| v.as_str()) == Some("Identifier") {
6734 if type_name_val.get("name").and_then(|v| v.as_str()) == Some("Array") {
6735 return Type::Object {
6736 shape_id: Some("BuiltInArray".to_string()),
6737 };
6738 }
6739 }
6740 }
6741 builder.make_type()
6742 }
6743 "ArrayTypeAnnotation" | "TSArrayType" => Type::Object {
6744 shape_id: Some("BuiltInArray".to_string()),
6745 },
6746 "BooleanLiteralTypeAnnotation"
6747 | "BooleanTypeAnnotation"
6748 | "NullLiteralTypeAnnotation"
6749 | "NumberLiteralTypeAnnotation"
6750 | "NumberTypeAnnotation"
6751 | "StringLiteralTypeAnnotation"
6752 | "StringTypeAnnotation"
6753 | "TSBooleanKeyword"
6754 | "TSNullKeyword"
6755 | "TSNumberKeyword"
6756 | "TSStringKeyword"
6757 | "TSSymbolKeyword"
6758 | "TSUndefinedKeyword"
6759 | "TSVoidKeyword"
6760 | "VoidTypeAnnotation" => Type::Primitive,
6761 _ => builder.make_type(),
6762 }
6763}
6764
6765fn gather_captured_context(
6771 scope_info: &ScopeInfo,
6772 function_scope: react_compiler_ast::scope::ScopeId,
6773 component_scope: react_compiler_ast::scope::ScopeId,
6774 func_start: u32,
6775 func_end: u32,
6776 identifier_locs: &IdentifierLocIndex,
6777 ref_node_ids_override: Option<&IndexSet<u32>>,
6778) -> IndexMap<react_compiler_ast::scope::BindingId, Option<SourceLocation>> {
6779 let parent_scope = scope_info.scopes[function_scope.0 as usize].parent;
6780 let pure_scopes = match parent_scope {
6781 Some(parent) => capture_scopes(scope_info, parent, component_scope),
6782 None => IndexSet::new(),
6783 };
6784
6785 let mut captured: std::collections::HashMap<
6790 react_compiler_ast::scope::BindingId,
6791 (u32, Option<SourceLocation>), > = std::collections::HashMap::new();
6793
6794 for (&ref_nid, &binding_id) in &scope_info.ref_node_id_to_binding {
6795 if let Some(allowed) = ref_node_ids_override {
6796 if !allowed.contains(&ref_nid) {
6797 continue;
6798 }
6799 } else {
6800 let ref_start = identifier_locs.get(&ref_nid).map(|e| e.start).unwrap_or(0);
6802 if ref_start < func_start || ref_start >= func_end {
6803 continue;
6804 }
6805 }
6806 let binding = &scope_info.bindings[binding_id.0 as usize];
6807 if binding.declaration_node_id == Some(ref_nid) {
6809 continue;
6810 }
6811 if let Some(entry) = identifier_locs.get(&ref_nid) {
6818 if entry.is_declaration_name || entry.in_type_annotation {
6819 continue;
6820 }
6821 }
6822 if binding.declaration_type == "TypeAlias"
6824 || binding.declaration_type == "OpaqueType"
6825 || binding.declaration_type == "InterfaceDeclaration"
6826 || binding.declaration_type == "TSTypeAliasDeclaration"
6827 || binding.declaration_type == "TSInterfaceDeclaration"
6828 || binding.declaration_type == "TSEnumDeclaration"
6829 {
6830 continue;
6831 }
6832 if pure_scopes.contains(&binding.scope) {
6833 let ref_start = identifier_locs.get(&ref_nid).map(|e| e.start).unwrap_or(0);
6834 if binding.declaration_start == Some(ref_start) {
6843 continue;
6844 }
6845 let loc = identifier_locs.get(&ref_nid).map(|entry| {
6846 if let Some(oe_loc) = &entry.opening_element_loc {
6847 oe_loc.clone()
6848 } else {
6849 entry.loc.clone()
6850 }
6851 });
6852 captured
6853 .entry(binding.id)
6854 .and_modify(|(min_pos, existing_loc)| {
6855 if ref_start < *min_pos {
6856 *min_pos = ref_start;
6857 *existing_loc = loc.clone();
6858 }
6859 })
6860 .or_insert((ref_start, loc));
6861 }
6862 }
6863
6864 let mut sorted: Vec<_> = captured.into_iter().collect();
6867 sorted.sort_by_key(|(_, (pos, _))| *pos);
6868
6869 sorted
6870 .into_iter()
6871 .map(|(bid, (_, loc))| (bid, loc))
6872 .collect()
6873}
6874
6875fn capture_scopes(
6876 scope_info: &ScopeInfo,
6877 from: react_compiler_ast::scope::ScopeId,
6878 to: react_compiler_ast::scope::ScopeId,
6879) -> IndexSet<react_compiler_ast::scope::ScopeId> {
6880 let mut result = IndexSet::new();
6881 let mut current = Some(from);
6882 while let Some(scope_id) = current {
6883 result.insert(scope_id);
6884 if scope_id == to {
6885 break;
6886 }
6887 current = scope_info.scopes[scope_id.0 as usize].parent;
6888 }
6889 result
6890}
6891
6892#[derive(Clone, Copy)]
6894pub enum AssignmentStyle {
6895 Assignment,
6897 Destructure,
6899}
6900
6901fn collect_fbt_sub_tags(
6904 children: &[react_compiler_ast::jsx::JSXChild],
6905 tag_name: &str,
6906 enum_locs: &mut Vec<Option<SourceLocation>>,
6907 plural_locs: &mut Vec<Option<SourceLocation>>,
6908 pronoun_locs: &mut Vec<Option<SourceLocation>>,
6909) {
6910 use react_compiler_ast::jsx::JSXChild;
6911 for child in children {
6912 match child {
6913 JSXChild::JSXElement(el) => {
6914 collect_fbt_sub_tags_from_element(
6915 el,
6916 tag_name,
6917 enum_locs,
6918 plural_locs,
6919 pronoun_locs,
6920 );
6921 }
6922 JSXChild::JSXFragment(frag) => {
6923 collect_fbt_sub_tags(
6924 &frag.children,
6925 tag_name,
6926 enum_locs,
6927 plural_locs,
6928 pronoun_locs,
6929 );
6930 }
6931 JSXChild::JSXExpressionContainer(container) => {
6932 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(expr) =
6933 &container.expression
6934 {
6935 collect_fbt_sub_tags_from_expr(
6936 expr,
6937 tag_name,
6938 enum_locs,
6939 plural_locs,
6940 pronoun_locs,
6941 );
6942 }
6943 }
6944 _ => {}
6945 }
6946 }
6947}
6948
6949fn collect_fbt_sub_tags_from_element(
6950 el: &react_compiler_ast::jsx::JSXElement,
6951 tag_name: &str,
6952 enum_locs: &mut Vec<Option<SourceLocation>>,
6953 plural_locs: &mut Vec<Option<SourceLocation>>,
6954 pronoun_locs: &mut Vec<Option<SourceLocation>>,
6955) {
6956 use react_compiler_ast::jsx::JSXElementName;
6957 if let JSXElementName::JSXNamespacedName(ns) = &el.opening_element.name {
6958 if ns.namespace.name == tag_name {
6959 let loc = convert_opt_loc(&ns.base.loc);
6960 match ns.name.name.as_str() {
6961 "enum" => enum_locs.push(loc),
6962 "plural" => plural_locs.push(loc),
6963 "pronoun" => pronoun_locs.push(loc),
6964 _ => {}
6965 }
6966 }
6967 }
6968 collect_fbt_sub_tags(&el.children, tag_name, enum_locs, plural_locs, pronoun_locs);
6969 for attr in &el.opening_element.attributes {
6971 if let react_compiler_ast::jsx::JSXAttributeItem::JSXAttribute(a) = attr {
6972 if let Some(val) = &a.value {
6973 if let react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(
6974 container,
6975 ) = val
6976 {
6977 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(expr) =
6978 &container.expression
6979 {
6980 collect_fbt_sub_tags_from_expr(
6981 expr,
6982 tag_name,
6983 enum_locs,
6984 plural_locs,
6985 pronoun_locs,
6986 );
6987 }
6988 } else if let react_compiler_ast::jsx::JSXAttributeValue::JSXElement(nested) = val {
6989 collect_fbt_sub_tags_from_element(
6990 nested,
6991 tag_name,
6992 enum_locs,
6993 plural_locs,
6994 pronoun_locs,
6995 );
6996 }
6997 }
6998 }
6999 }
7000}
7001
7002fn collect_fbt_sub_tags_from_expr(
7003 expr: &react_compiler_ast::expressions::Expression,
7004 tag_name: &str,
7005 enum_locs: &mut Vec<Option<SourceLocation>>,
7006 plural_locs: &mut Vec<Option<SourceLocation>>,
7007 pronoun_locs: &mut Vec<Option<SourceLocation>>,
7008) {
7009 use react_compiler_ast::expressions::Expression;
7010 match expr {
7011 Expression::JSXElement(el) => {
7012 collect_fbt_sub_tags_from_element(el, tag_name, enum_locs, plural_locs, pronoun_locs);
7013 }
7014 Expression::JSXFragment(frag) => {
7015 collect_fbt_sub_tags(
7016 &frag.children,
7017 tag_name,
7018 enum_locs,
7019 plural_locs,
7020 pronoun_locs,
7021 );
7022 }
7023 Expression::ConditionalExpression(cond) => {
7024 collect_fbt_sub_tags_from_expr(
7025 &cond.consequent,
7026 tag_name,
7027 enum_locs,
7028 plural_locs,
7029 pronoun_locs,
7030 );
7031 collect_fbt_sub_tags_from_expr(
7032 &cond.alternate,
7033 tag_name,
7034 enum_locs,
7035 plural_locs,
7036 pronoun_locs,
7037 );
7038 }
7039 Expression::LogicalExpression(log) => {
7040 collect_fbt_sub_tags_from_expr(
7041 &log.left,
7042 tag_name,
7043 enum_locs,
7044 plural_locs,
7045 pronoun_locs,
7046 );
7047 collect_fbt_sub_tags_from_expr(
7048 &log.right,
7049 tag_name,
7050 enum_locs,
7051 plural_locs,
7052 pronoun_locs,
7053 );
7054 }
7055 Expression::ParenthesizedExpression(paren) => {
7056 collect_fbt_sub_tags_from_expr(
7057 &paren.expression,
7058 tag_name,
7059 enum_locs,
7060 plural_locs,
7061 pronoun_locs,
7062 );
7063 }
7064 Expression::ArrowFunctionExpression(arrow) => match arrow.body.as_ref() {
7065 react_compiler_ast::expressions::ArrowFunctionBody::Expression(body_expr) => {
7066 collect_fbt_sub_tags_from_expr(
7067 body_expr,
7068 tag_name,
7069 enum_locs,
7070 plural_locs,
7071 pronoun_locs,
7072 );
7073 }
7074 react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
7075 collect_fbt_sub_tags_from_stmts(
7076 &block.body,
7077 tag_name,
7078 enum_locs,
7079 plural_locs,
7080 pronoun_locs,
7081 );
7082 }
7083 },
7084 Expression::CallExpression(call) => {
7085 for arg in &call.arguments {
7086 collect_fbt_sub_tags_from_expr(arg, tag_name, enum_locs, plural_locs, pronoun_locs);
7087 }
7088 }
7089 _ => {}
7090 }
7091}
7092
7093fn collect_fbt_sub_tags_from_stmts(
7094 stmts: &[react_compiler_ast::statements::Statement],
7095 tag_name: &str,
7096 enum_locs: &mut Vec<Option<SourceLocation>>,
7097 plural_locs: &mut Vec<Option<SourceLocation>>,
7098 pronoun_locs: &mut Vec<Option<SourceLocation>>,
7099) {
7100 for stmt in stmts {
7101 if let react_compiler_ast::statements::Statement::ReturnStatement(ret) = stmt {
7102 if let Some(arg) = &ret.argument {
7103 collect_fbt_sub_tags_from_expr(arg, tag_name, enum_locs, plural_locs, pronoun_locs);
7104 }
7105 } else if let react_compiler_ast::statements::Statement::ExpressionStatement(expr_stmt) =
7106 stmt
7107 {
7108 collect_fbt_sub_tags_from_expr(
7109 &expr_stmt.expression,
7110 tag_name,
7111 enum_locs,
7112 plural_locs,
7113 pronoun_locs,
7114 );
7115 }
7116 }
7117}
7118
7119fn collect_identifier_node_ids_from_body(body: &FunctionBody) -> IndexSet<u32> {
7120 let mut positions = IndexSet::new();
7121 match body {
7122 FunctionBody::Block(block) => {
7123 for stmt in &block.body {
7124 collect_identifier_node_ids_from_stmt(stmt, &mut positions);
7125 }
7126 }
7127 FunctionBody::Expression(expr) => {
7128 collect_identifier_node_ids_from_expr(expr, &mut positions);
7129 }
7130 }
7131 positions
7132}
7133
7134fn collect_identifier_node_ids_from_stmt(
7135 stmt: &react_compiler_ast::statements::Statement,
7136 positions: &mut IndexSet<u32>,
7137) {
7138 use react_compiler_ast::statements::Statement;
7139 match stmt {
7140 Statement::ExpressionStatement(s) => {
7141 collect_identifier_node_ids_from_expr(&s.expression, positions)
7142 }
7143 Statement::ReturnStatement(s) => {
7144 if let Some(arg) = &s.argument {
7145 collect_identifier_node_ids_from_expr(arg, positions);
7146 }
7147 }
7148 Statement::ThrowStatement(s) => {
7149 collect_identifier_node_ids_from_expr(&s.argument, positions)
7150 }
7151 Statement::BlockStatement(s) => {
7152 for stmt in &s.body {
7153 collect_identifier_node_ids_from_stmt(stmt, positions);
7154 }
7155 }
7156 Statement::IfStatement(s) => {
7157 collect_identifier_node_ids_from_expr(&s.test, positions);
7158 collect_identifier_node_ids_from_stmt(&s.consequent, positions);
7159 if let Some(alt) = &s.alternate {
7160 collect_identifier_node_ids_from_stmt(alt, positions);
7161 }
7162 }
7163 Statement::VariableDeclaration(s) => {
7164 for decl in &s.declarations {
7165 if let Some(init) = &decl.init {
7166 collect_identifier_node_ids_from_expr(init, positions);
7167 }
7168 }
7169 }
7170 _ => {}
7171 }
7172}
7173
7174fn collect_identifier_node_ids_from_expr(
7175 expr: &react_compiler_ast::expressions::Expression,
7176 positions: &mut IndexSet<u32>,
7177) {
7178 use react_compiler_ast::expressions::Expression;
7179 match expr {
7180 Expression::Identifier(id) => {
7181 if let Some(nid) = id.base.node_id {
7182 positions.insert(nid);
7183 }
7184 }
7185 Expression::CallExpression(call) => {
7186 collect_identifier_node_ids_from_expr(&call.callee, positions);
7187 for arg in &call.arguments {
7188 collect_identifier_node_ids_from_expr(arg, positions);
7189 }
7190 }
7191 Expression::BinaryExpression(e) => {
7192 collect_identifier_node_ids_from_expr(&e.left, positions);
7193 collect_identifier_node_ids_from_expr(&e.right, positions);
7194 }
7195 Expression::ConditionalExpression(e) => {
7196 collect_identifier_node_ids_from_expr(&e.test, positions);
7197 collect_identifier_node_ids_from_expr(&e.consequent, positions);
7198 collect_identifier_node_ids_from_expr(&e.alternate, positions);
7199 }
7200 Expression::LogicalExpression(e) => {
7201 collect_identifier_node_ids_from_expr(&e.left, positions);
7202 collect_identifier_node_ids_from_expr(&e.right, positions);
7203 }
7204 Expression::MemberExpression(e) => {
7205 collect_identifier_node_ids_from_expr(&e.object, positions);
7206 }
7207 Expression::OptionalMemberExpression(e) => {
7208 collect_identifier_node_ids_from_expr(&e.object, positions);
7209 }
7210 Expression::OptionalCallExpression(e) => {
7211 collect_identifier_node_ids_from_expr(&e.callee, positions);
7212 for arg in &e.arguments {
7213 collect_identifier_node_ids_from_expr(arg, positions);
7214 }
7215 }
7216 Expression::UpdateExpression(e) => {
7217 collect_identifier_node_ids_from_expr(&e.argument, positions);
7218 }
7219 Expression::FunctionExpression(func) => {
7220 for stmt in &func.body.body {
7221 collect_identifier_node_ids_from_stmt(stmt, positions);
7222 }
7223 }
7224 Expression::UnaryExpression(e) => {
7225 collect_identifier_node_ids_from_expr(&e.argument, positions);
7226 }
7227 Expression::ParenthesizedExpression(e) => {
7228 collect_identifier_node_ids_from_expr(&e.expression, positions);
7229 }
7230 Expression::TypeCastExpression(e) => {
7231 collect_identifier_node_ids_from_expr(&e.expression, positions);
7232 }
7233 Expression::ArrowFunctionExpression(arrow) => match arrow.body.as_ref() {
7234 react_compiler_ast::expressions::ArrowFunctionBody::BlockStatement(block) => {
7235 for stmt in &block.body {
7236 collect_identifier_node_ids_from_stmt(stmt, positions);
7237 }
7238 }
7239 react_compiler_ast::expressions::ArrowFunctionBody::Expression(e) => {
7240 collect_identifier_node_ids_from_expr(e, positions);
7241 }
7242 },
7243 Expression::JSXElement(el) => {
7244 if let react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) =
7245 &el.opening_element.name
7246 {
7247 if let Some(nid) = id.base.node_id {
7248 positions.insert(nid);
7249 }
7250 }
7251 for attr in &el.opening_element.attributes {
7252 match attr {
7253 react_compiler_ast::jsx::JSXAttributeItem::JSXAttribute(a) => {
7254 if let Some(val) = &a.value {
7255 match val {
7256 react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {
7257 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) = &c.expression {
7258 collect_identifier_node_ids_from_expr(e, positions);
7259 }
7260 }
7261 _ => {}
7262 }
7263 }
7264 }
7265 react_compiler_ast::jsx::JSXAttributeItem::JSXSpreadAttribute(a) => {
7266 collect_identifier_node_ids_from_expr(&a.argument, positions);
7267 }
7268 }
7269 }
7270 for child in &el.children {
7271 match child {
7272 react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => {
7273 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) =
7274 &c.expression
7275 {
7276 collect_identifier_node_ids_from_expr(e, positions);
7277 }
7278 }
7279 react_compiler_ast::jsx::JSXChild::JSXElement(child_el) => {
7280 collect_identifier_node_ids_from_expr(
7281 &Expression::JSXElement(child_el.clone()),
7282 positions,
7283 );
7284 }
7285 react_compiler_ast::jsx::JSXChild::JSXSpreadChild(s) => {
7286 collect_identifier_node_ids_from_expr(&s.expression, positions);
7287 }
7288 _ => {}
7289 }
7290 }
7291 }
7292 Expression::JSXFragment(frag) => {
7293 for child in &frag.children {
7294 match child {
7295 react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => {
7296 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) =
7297 &c.expression
7298 {
7299 collect_identifier_node_ids_from_expr(e, positions);
7300 }
7301 }
7302 react_compiler_ast::jsx::JSXChild::JSXElement(child_el) => {
7303 collect_identifier_node_ids_from_expr(
7304 &Expression::JSXElement(child_el.clone()),
7305 positions,
7306 );
7307 }
7308 _ => {}
7309 }
7310 }
7311 }
7312 Expression::ArrayExpression(arr) => {
7313 for elem in &arr.elements {
7314 if let Some(e) = elem {
7315 collect_identifier_node_ids_from_expr(e, positions);
7316 }
7317 }
7318 }
7319 Expression::ObjectExpression(obj) => {
7320 for prop in &obj.properties {
7321 match prop {
7322 react_compiler_ast::expressions::ObjectExpressionProperty::ObjectProperty(
7323 p,
7324 ) => {
7325 collect_identifier_node_ids_from_expr(&p.value, positions);
7326 }
7327 react_compiler_ast::expressions::ObjectExpressionProperty::SpreadElement(s) => {
7328 collect_identifier_node_ids_from_expr(&s.argument, positions);
7329 }
7330 _ => {}
7331 }
7332 }
7333 }
7334 Expression::NewExpression(e) => {
7335 collect_identifier_node_ids_from_expr(&e.callee, positions);
7336 for arg in &e.arguments {
7337 collect_identifier_node_ids_from_expr(arg, positions);
7338 }
7339 }
7340 Expression::AssignmentExpression(e) => {
7341 collect_identifier_node_ids_from_expr(&e.right, positions);
7342 }
7343 Expression::TemplateLiteral(e) => {
7344 for expr in &e.expressions {
7345 collect_identifier_node_ids_from_expr(expr, positions);
7346 }
7347 }
7348 Expression::SpreadElement(e) => {
7349 collect_identifier_node_ids_from_expr(&e.argument, positions);
7350 }
7351 Expression::SequenceExpression(e) => {
7352 for expr in &e.expressions {
7353 collect_identifier_node_ids_from_expr(expr, positions);
7354 }
7355 }
7356 _ => {}
7357 }
7358}