compiler/crates/react_compiler_hir/src/lib.rs RUST 1,634 lines View on github.com → Search inside
1pub mod default_module_type_provider;2pub mod dominator;3pub mod environment;4pub mod environment_config;5pub mod globals;6pub mod object_shape;7pub mod print;8pub mod reactive;9pub mod type_config;10pub mod visitors;1112use indexmap::{IndexMap, IndexSet};13pub use react_compiler_diagnostics::CompilerDiagnostic;14pub use react_compiler_diagnostics::ErrorCategory;15pub use react_compiler_diagnostics::GENERATED_SOURCE;16pub use react_compiler_diagnostics::Position;17pub use react_compiler_diagnostics::SourceLocation;18pub use reactive::*;19use rustc_hash::FxBuildHasher;2021// =============================================================================22// ID newtypes23// =============================================================================2425#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]26pub struct BlockId(pub u32);2728#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]29pub struct IdentifierId(pub u32);3031/// Index into the flat instruction table on HirFunction.32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]33pub struct InstructionId(pub u32);3435/// Evaluation order assigned to instructions and terminals during numbering.36/// This was previously called InstructionId in the TypeScript compiler.37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]38pub struct EvaluationOrder(pub u32);3940#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]41pub struct DeclarationId(pub u32);4243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]44pub struct ScopeId(pub u32);4546#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]47pub struct TypeId(pub u32);4849#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]50pub struct FunctionId(pub u32);5152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]53pub struct MutableRangeId(pub u32);5455// =============================================================================56// FloatValue wrapper57// =============================================================================5859/// Wrapper around f64 that stores raw bytes for deterministic equality and hashing.60/// This allows use in FxHashMap keys and ensures NaN == NaN (bitwise comparison).61#[derive(Debug, Clone, Copy)]62pub struct FloatValue(u64);6364impl FloatValue {65    pub fn new(value: f64) -> Self {66        FloatValue(value.to_bits())67    }6869    pub fn value(self) -> f64 {70        f64::from_bits(self.0)71    }72}7374impl From<f64> for FloatValue {75    fn from(value: f64) -> Self {76        FloatValue::new(value)77    }78}7980impl From<FloatValue> for f64 {81    fn from(value: FloatValue) -> Self {82        value.value()83    }84}8586impl PartialEq for FloatValue {87    fn eq(&self, other: &Self) -> bool {88        self.0 == other.089    }90}9192impl Eq for FloatValue {}9394impl std::hash::Hash for FloatValue {95    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {96        self.0.hash(state);97    }98}99100impl std::fmt::Display for FloatValue {101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {102        write!(f, "{}", format_js_number(self.value()))103    }104}105106/// Format an f64 the way JavaScript's `Number.prototype.toString()` does.107///108/// Key differences from Rust's default `Display`:109/// - Uses scientific notation for |x| >= 1e21 (e.g. `1e+21`, `2.18739127891275e+22`)110/// - Uses scientific notation for 0 < |x| < 1e-6 (e.g. `1e-7`, `1.5e-8`)111/// - Uses minimal significant digits that round-trip to the same f64112/// - Formats -0 as "0"113pub fn format_js_number(n: f64) -> String {114    if n.is_nan() {115        return "NaN".to_string();116    }117    if n.is_infinite() {118        return if n > 0.0 {119            "Infinity".to_string()120        } else {121            "-Infinity".to_string()122        };123    }124    if n == 0.0 {125        return "0".to_string();126    }127128    let abs = n.abs();129    let sign = if n < 0.0 { "-" } else { "" };130131    if abs >= 1e21 || (abs > 0.0 && abs < 1e-6) {132        // Use scientific notation matching JS format: coefficient + "e+" or "e-" + exponent133        // Rust's {:e} uses "e" (lowercase) like JS, but formats as e.g. "1.5e21" not "1.5e+21"134        let formatted = format!("{:e}", abs);135        // Split into coefficient and exponent parts136        let (coeff, exp_str) = formatted.split_once('e').unwrap();137        let exp: i32 = exp_str.parse().unwrap();138        // JS uses e+N for positive exponents, e-N for negative139        if exp >= 0 {140            format!("{}{}e+{}", sign, coeff, exp)141        } else {142            format!("{}{}e-{}", sign, coeff, exp.unsigned_abs())143        }144    } else if abs.fract() == 0.0 && abs < (i64::MAX as f64) {145        // Integer that fits in i64 — format without decimal point146        format!("{}{}", sign, abs as i64)147    } else {148        // Regular float: Rust's default Display gives us the right digits149        format!("{}", n)150    }151}152153// =============================================================================154// Core HIR types155// =============================================================================156157/// A function lowered to HIR form158#[derive(Debug, Clone)]159pub struct HirFunction {160    pub loc: Option<SourceLocation>,161    pub id: Option<String>,162    pub name_hint: Option<String>,163    pub fn_type: ReactFunctionType,164    pub params: Vec<ParamPattern>,165    pub return_type_annotation: Option<String>,166    pub returns: Place,167    pub context: Vec<Place>,168    pub body: HIR,169    pub instructions: Vec<Instruction>,170    pub generator: bool,171    pub is_async: bool,172    pub directives: Vec<String>,173    pub aliasing_effects: Option<Vec<AliasingEffect>>,174}175176#[derive(Debug, Clone, Copy, PartialEq, Eq)]177pub enum ReactFunctionType {178    Component,179    Hook,180    Other,181}182183#[derive(Debug, Clone)]184pub enum ParamPattern {185    Place(Place),186    Spread(SpreadPattern),187}188189/// The HIR control-flow graph190#[derive(Debug, Clone)]191pub struct HIR {192    pub entry: BlockId,193    pub blocks: IndexMap<BlockId, BasicBlock, FxBuildHasher>,194}195196/// Block kinds197#[derive(Debug, Clone, Copy, PartialEq, Eq)]198pub enum BlockKind {199    Block,200    Value,201    Loop,202    Sequence,203    Catch,204}205206impl std::fmt::Display for BlockKind {207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {208        match self {209            BlockKind::Block => write!(f, "block"),210            BlockKind::Value => write!(f, "value"),211            BlockKind::Loop => write!(f, "loop"),212            BlockKind::Sequence => write!(f, "sequence"),213            BlockKind::Catch => write!(f, "catch"),214        }215    }216}217218/// A basic block in the CFG219#[derive(Debug, Clone)]220pub struct BasicBlock {221    pub kind: BlockKind,222    pub id: BlockId,223    pub instructions: Vec<InstructionId>,224    pub terminal: Terminal,225    pub preds: IndexSet<BlockId, FxBuildHasher>,226    pub phis: Vec<Phi>,227}228229/// Phi node for SSA230#[derive(Debug, Clone)]231pub struct Phi {232    pub place: Place,233    pub operands: IndexMap<BlockId, Place, FxBuildHasher>,234}235236// =============================================================================237// Terminal enum238// =============================================================================239240#[derive(Debug, Clone)]241pub enum Terminal {242    Unsupported {243        id: EvaluationOrder,244        loc: Option<SourceLocation>,245    },246    Unreachable {247        id: EvaluationOrder,248        loc: Option<SourceLocation>,249    },250    Throw {251        value: Place,252        id: EvaluationOrder,253        loc: Option<SourceLocation>,254    },255    Return {256        value: Place,257        return_variant: ReturnVariant,258        id: EvaluationOrder,259        loc: Option<SourceLocation>,260        effects: Option<Vec<AliasingEffect>>,261    },262    Goto {263        block: BlockId,264        variant: GotoVariant,265        id: EvaluationOrder,266        loc: Option<SourceLocation>,267    },268    If {269        test: Place,270        consequent: BlockId,271        alternate: BlockId,272        fallthrough: BlockId,273        id: EvaluationOrder,274        loc: Option<SourceLocation>,275    },276    Branch {277        test: Place,278        consequent: BlockId,279        alternate: BlockId,280        fallthrough: BlockId,281        id: EvaluationOrder,282        loc: Option<SourceLocation>,283    },284    Switch {285        test: Place,286        cases: Vec<Case>,287        fallthrough: BlockId,288        id: EvaluationOrder,289        loc: Option<SourceLocation>,290    },291    DoWhile {292        loop_block: BlockId,293        test: BlockId,294        fallthrough: BlockId,295        id: EvaluationOrder,296        loc: Option<SourceLocation>,297    },298    While {299        test: BlockId,300        loop_block: BlockId,301        fallthrough: BlockId,302        id: EvaluationOrder,303        loc: Option<SourceLocation>,304    },305    For {306        init: BlockId,307        test: BlockId,308        update: Option<BlockId>,309        loop_block: BlockId,310        fallthrough: BlockId,311        id: EvaluationOrder,312        loc: Option<SourceLocation>,313    },314    ForOf {315        init: BlockId,316        test: BlockId,317        loop_block: BlockId,318        fallthrough: BlockId,319        id: EvaluationOrder,320        loc: Option<SourceLocation>,321    },322    ForIn {323        init: BlockId,324        loop_block: BlockId,325        fallthrough: BlockId,326        id: EvaluationOrder,327        loc: Option<SourceLocation>,328    },329    Logical {330        operator: LogicalOperator,331        test: BlockId,332        fallthrough: BlockId,333        id: EvaluationOrder,334        loc: Option<SourceLocation>,335    },336    Ternary {337        test: BlockId,338        fallthrough: BlockId,339        id: EvaluationOrder,340        loc: Option<SourceLocation>,341    },342    Optional {343        optional: bool,344        test: BlockId,345        fallthrough: BlockId,346        id: EvaluationOrder,347        loc: Option<SourceLocation>,348    },349    Label {350        block: BlockId,351        fallthrough: BlockId,352        id: EvaluationOrder,353        loc: Option<SourceLocation>,354    },355    Sequence {356        block: BlockId,357        fallthrough: BlockId,358        id: EvaluationOrder,359        loc: Option<SourceLocation>,360    },361    MaybeThrow {362        continuation: BlockId,363        handler: Option<BlockId>,364        id: EvaluationOrder,365        loc: Option<SourceLocation>,366        effects: Option<Vec<AliasingEffect>>,367    },368    Try {369        block: BlockId,370        handler_binding: Option<Place>,371        handler: BlockId,372        fallthrough: BlockId,373        id: EvaluationOrder,374        loc: Option<SourceLocation>,375    },376    Scope {377        fallthrough: BlockId,378        block: BlockId,379        scope: ScopeId,380        id: EvaluationOrder,381        loc: Option<SourceLocation>,382    },383    PrunedScope {384        fallthrough: BlockId,385        block: BlockId,386        scope: ScopeId,387        id: EvaluationOrder,388        loc: Option<SourceLocation>,389    },390}391392impl Terminal {393    /// Get the evaluation order of this terminal394    pub fn evaluation_order(&self) -> EvaluationOrder {395        match self {396            Terminal::Unsupported { id, .. }397            | Terminal::Unreachable { id, .. }398            | Terminal::Throw { id, .. }399            | Terminal::Return { id, .. }400            | Terminal::Goto { id, .. }401            | Terminal::If { id, .. }402            | Terminal::Branch { id, .. }403            | Terminal::Switch { id, .. }404            | Terminal::DoWhile { id, .. }405            | Terminal::While { id, .. }406            | Terminal::For { id, .. }407            | Terminal::ForOf { id, .. }408            | Terminal::ForIn { id, .. }409            | Terminal::Logical { id, .. }410            | Terminal::Ternary { id, .. }411            | Terminal::Optional { id, .. }412            | Terminal::Label { id, .. }413            | Terminal::Sequence { id, .. }414            | Terminal::MaybeThrow { id, .. }415            | Terminal::Try { id, .. }416            | Terminal::Scope { id, .. }417            | Terminal::PrunedScope { id, .. } => *id,418        }419    }420421    /// Get the source location of this terminal422    pub fn loc(&self) -> Option<&SourceLocation> {423        match self {424            Terminal::Unsupported { loc, .. }425            | Terminal::Unreachable { loc, .. }426            | Terminal::Throw { loc, .. }427            | Terminal::Return { loc, .. }428            | Terminal::Goto { loc, .. }429            | Terminal::If { loc, .. }430            | Terminal::Branch { loc, .. }431            | Terminal::Switch { loc, .. }432            | Terminal::DoWhile { loc, .. }433            | Terminal::While { loc, .. }434            | Terminal::For { loc, .. }435            | Terminal::ForOf { loc, .. }436            | Terminal::ForIn { loc, .. }437            | Terminal::Logical { loc, .. }438            | Terminal::Ternary { loc, .. }439            | Terminal::Optional { loc, .. }440            | Terminal::Label { loc, .. }441            | Terminal::Sequence { loc, .. }442            | Terminal::MaybeThrow { loc, .. }443            | Terminal::Try { loc, .. }444            | Terminal::Scope { loc, .. }445            | Terminal::PrunedScope { loc, .. } => loc.as_ref(),446        }447    }448449    /// Set the evaluation order of this terminal450    pub fn set_evaluation_order(&mut self, new_id: EvaluationOrder) {451        match self {452            Terminal::Unsupported { id, .. }453            | Terminal::Unreachable { id, .. }454            | Terminal::Throw { id, .. }455            | Terminal::Return { id, .. }456            | Terminal::Goto { id, .. }457            | Terminal::If { id, .. }458            | Terminal::Branch { id, .. }459            | Terminal::Switch { id, .. }460            | Terminal::DoWhile { id, .. }461            | Terminal::While { id, .. }462            | Terminal::For { id, .. }463            | Terminal::ForOf { id, .. }464            | Terminal::ForIn { id, .. }465            | Terminal::Logical { id, .. }466            | Terminal::Ternary { id, .. }467            | Terminal::Optional { id, .. }468            | Terminal::Label { id, .. }469            | Terminal::Sequence { id, .. }470            | Terminal::MaybeThrow { id, .. }471            | Terminal::Try { id, .. }472            | Terminal::Scope { id, .. }473            | Terminal::PrunedScope { id, .. } => *id = new_id,474        }475    }476}477478#[derive(Debug, Clone, Copy, PartialEq, Eq)]479pub enum ReturnVariant {480    Void,481    Implicit,482    Explicit,483}484485#[derive(Debug, Clone, Copy, PartialEq, Eq)]486pub enum GotoVariant {487    Break,488    Continue,489    Try,490}491492#[derive(Debug, Clone)]493pub struct Case {494    pub test: Option<Place>,495    pub block: BlockId,496}497498#[derive(Debug, Clone, Copy, PartialEq, Eq)]499pub enum LogicalOperator {500    And,501    Or,502    NullishCoalescing,503}504505impl std::fmt::Display for LogicalOperator {506    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {507        match self {508            LogicalOperator::And => write!(f, "&&"),509            LogicalOperator::Or => write!(f, "||"),510            LogicalOperator::NullishCoalescing => write!(f, "??"),511        }512    }513}514515// =============================================================================516// Instruction types517// =============================================================================518519#[derive(Debug, Clone)]520pub struct Instruction {521    pub id: EvaluationOrder,522    pub lvalue: Place,523    pub value: InstructionValue,524    pub loc: Option<SourceLocation>,525    pub effects: Option<Vec<AliasingEffect>>,526}527528#[derive(Debug, Clone, Copy, PartialEq, Eq)]529pub enum InstructionKind {530    Const,531    Let,532    Reassign,533    Catch,534    HoistedConst,535    HoistedLet,536    HoistedFunction,537    Function,538}539540#[derive(Debug, Clone)]541pub struct LValue {542    pub place: Place,543    pub kind: InstructionKind,544}545546#[derive(Debug, Clone)]547pub struct LValuePattern {548    pub pattern: Pattern,549    pub kind: InstructionKind,550}551552#[derive(Debug, Clone)]553pub enum Pattern {554    Array(ArrayPattern),555    Object(ObjectPattern),556}557558// =============================================================================559// InstructionValue enum560// =============================================================================561562#[derive(Debug, Clone)]563pub enum InstructionValue {564    LoadLocal {565        place: Place,566        loc: Option<SourceLocation>,567    },568    LoadContext {569        place: Place,570        loc: Option<SourceLocation>,571    },572    DeclareLocal {573        lvalue: LValue,574        type_annotation: Option<String>,575        loc: Option<SourceLocation>,576    },577    DeclareContext {578        lvalue: LValue,579        loc: Option<SourceLocation>,580    },581    StoreLocal {582        lvalue: LValue,583        value: Place,584        type_annotation: Option<String>,585        loc: Option<SourceLocation>,586    },587    StoreContext {588        lvalue: LValue,589        value: Place,590        loc: Option<SourceLocation>,591    },592    Destructure {593        lvalue: LValuePattern,594        value: Place,595        loc: Option<SourceLocation>,596    },597    Primitive {598        value: PrimitiveValue,599        loc: Option<SourceLocation>,600    },601    JSXText {602        value: String,603        loc: Option<SourceLocation>,604    },605    BinaryExpression {606        operator: BinaryOperator,607        left: Place,608        right: Place,609        loc: Option<SourceLocation>,610    },611    NewExpression {612        callee: Place,613        args: Vec<PlaceOrSpread>,614        loc: Option<SourceLocation>,615    },616    CallExpression {617        callee: Place,618        args: Vec<PlaceOrSpread>,619        loc: Option<SourceLocation>,620    },621    MethodCall {622        receiver: Place,623        property: Place,624        args: Vec<PlaceOrSpread>,625        loc: Option<SourceLocation>,626    },627    UnaryExpression {628        operator: UnaryOperator,629        value: Place,630        loc: Option<SourceLocation>,631    },632    TypeCastExpression {633        value: Place,634        type_: Type,635        type_annotation_name: Option<String>,636        type_annotation_kind: Option<String>,637        /// The original AST type annotation node, preserved for codegen.638        /// For Flow: the inner type from TypeAnnotation.typeAnnotation639        /// For TS: the TSType node from TSAsExpression/TSSatisfiesExpression640        type_annotation: Option<Box<serde_json::Value>>,641        loc: Option<SourceLocation>,642    },643    JsxExpression {644        tag: JsxTag,645        props: Vec<JsxAttribute>,646        children: Option<Vec<Place>>,647        loc: Option<SourceLocation>,648        opening_loc: Option<SourceLocation>,649        closing_loc: Option<SourceLocation>,650    },651    ObjectExpression {652        properties: Vec<ObjectPropertyOrSpread>,653        loc: Option<SourceLocation>,654    },655    ObjectMethod {656        loc: Option<SourceLocation>,657        lowered_func: LoweredFunction,658    },659    ArrayExpression {660        elements: Vec<ArrayElement>,661        loc: Option<SourceLocation>,662    },663    JsxFragment {664        children: Vec<Place>,665        loc: Option<SourceLocation>,666    },667    RegExpLiteral {668        pattern: String,669        flags: String,670        loc: Option<SourceLocation>,671    },672    MetaProperty {673        meta: String,674        property: String,675        loc: Option<SourceLocation>,676    },677    PropertyStore {678        object: Place,679        property: PropertyLiteral,680        value: Place,681        loc: Option<SourceLocation>,682    },683    PropertyLoad {684        object: Place,685        property: PropertyLiteral,686        loc: Option<SourceLocation>,687    },688    PropertyDelete {689        object: Place,690        property: PropertyLiteral,691        loc: Option<SourceLocation>,692    },693    ComputedStore {694        object: Place,695        property: Place,696        value: Place,697        loc: Option<SourceLocation>,698    },699    ComputedLoad {700        object: Place,701        property: Place,702        loc: Option<SourceLocation>,703    },704    ComputedDelete {705        object: Place,706        property: Place,707        loc: Option<SourceLocation>,708    },709    LoadGlobal {710        binding: NonLocalBinding,711        loc: Option<SourceLocation>,712    },713    StoreGlobal {714        name: String,715        value: Place,716        loc: Option<SourceLocation>,717    },718    FunctionExpression {719        name: Option<String>,720        name_hint: Option<String>,721        lowered_func: LoweredFunction,722        expr_type: FunctionExpressionType,723        loc: Option<SourceLocation>,724    },725    TaggedTemplateExpression {726        tag: Place,727        value: TemplateQuasi,728        loc: Option<SourceLocation>,729    },730    TemplateLiteral {731        subexprs: Vec<Place>,732        quasis: Vec<TemplateQuasi>,733        loc: Option<SourceLocation>,734    },735    Await {736        value: Place,737        loc: Option<SourceLocation>,738    },739    GetIterator {740        collection: Place,741        loc: Option<SourceLocation>,742    },743    IteratorNext {744        iterator: Place,745        collection: Place,746        loc: Option<SourceLocation>,747    },748    NextPropertyOf {749        value: Place,750        loc: Option<SourceLocation>,751    },752    PrefixUpdate {753        lvalue: Place,754        operation: UpdateOperator,755        value: Place,756        loc: Option<SourceLocation>,757    },758    PostfixUpdate {759        lvalue: Place,760        operation: UpdateOperator,761        value: Place,762        loc: Option<SourceLocation>,763    },764    Debugger {765        loc: Option<SourceLocation>,766    },767    StartMemoize {768        manual_memo_id: u32,769        deps: Option<Vec<ManualMemoDependency>>,770        deps_loc: Option<Option<SourceLocation>>,771        has_invalid_deps: bool,772        loc: Option<SourceLocation>,773    },774    FinishMemoize {775        manual_memo_id: u32,776        decl: Place,777        pruned: bool,778        loc: Option<SourceLocation>,779    },780    UnsupportedNode {781        node_type: Option<String>,782        /// The original AST node serialized as JSON, so codegen can emit it verbatim.783        original_node: Option<serde_json::Value>,784        loc: Option<SourceLocation>,785    },786}787788impl InstructionValue {789    pub fn loc(&self) -> Option<&SourceLocation> {790        match self {791            InstructionValue::LoadLocal { loc, .. }792            | InstructionValue::LoadContext { loc, .. }793            | InstructionValue::DeclareLocal { loc, .. }794            | InstructionValue::DeclareContext { loc, .. }795            | InstructionValue::StoreLocal { loc, .. }796            | InstructionValue::StoreContext { loc, .. }797            | InstructionValue::Destructure { loc, .. }798            | InstructionValue::Primitive { loc, .. }799            | InstructionValue::JSXText { loc, .. }800            | InstructionValue::BinaryExpression { loc, .. }801            | InstructionValue::NewExpression { loc, .. }802            | InstructionValue::CallExpression { loc, .. }803            | InstructionValue::MethodCall { loc, .. }804            | InstructionValue::UnaryExpression { loc, .. }805            | InstructionValue::TypeCastExpression { loc, .. }806            | InstructionValue::JsxExpression { loc, .. }807            | InstructionValue::ObjectExpression { loc, .. }808            | InstructionValue::ObjectMethod { loc, .. }809            | InstructionValue::ArrayExpression { loc, .. }810            | InstructionValue::JsxFragment { loc, .. }811            | InstructionValue::RegExpLiteral { loc, .. }812            | InstructionValue::MetaProperty { loc, .. }813            | InstructionValue::PropertyStore { loc, .. }814            | InstructionValue::PropertyLoad { loc, .. }815            | InstructionValue::PropertyDelete { loc, .. }816            | InstructionValue::ComputedStore { loc, .. }817            | InstructionValue::ComputedLoad { loc, .. }818            | InstructionValue::ComputedDelete { loc, .. }819            | InstructionValue::LoadGlobal { loc, .. }820            | InstructionValue::StoreGlobal { loc, .. }821            | InstructionValue::FunctionExpression { loc, .. }822            | InstructionValue::TaggedTemplateExpression { loc, .. }823            | InstructionValue::TemplateLiteral { loc, .. }824            | InstructionValue::Await { loc, .. }825            | InstructionValue::GetIterator { loc, .. }826            | InstructionValue::IteratorNext { loc, .. }827            | InstructionValue::NextPropertyOf { loc, .. }828            | InstructionValue::PrefixUpdate { loc, .. }829            | InstructionValue::PostfixUpdate { loc, .. }830            | InstructionValue::Debugger { loc, .. }831            | InstructionValue::StartMemoize { loc, .. }832            | InstructionValue::FinishMemoize { loc, .. }833            | InstructionValue::UnsupportedNode { loc, .. } => loc.as_ref(),834        }835    }836}837838// =============================================================================839// Supporting types840// =============================================================================841842#[derive(Debug, Clone, PartialEq, Eq, Hash)]843pub enum PrimitiveValue {844    Null,845    Undefined,846    Boolean(bool),847    Number(FloatValue),848    String(react_compiler_diagnostics::JsString),849}850851#[derive(Debug, Clone, Copy, PartialEq, Eq)]852pub enum BinaryOperator {853    Equal,854    NotEqual,855    StrictEqual,856    StrictNotEqual,857    LessThan,858    LessEqual,859    GreaterThan,860    GreaterEqual,861    ShiftLeft,862    ShiftRight,863    UnsignedShiftRight,864    Add,865    Subtract,866    Multiply,867    Divide,868    Modulo,869    Exponent,870    BitwiseOr,871    BitwiseXor,872    BitwiseAnd,873    In,874    InstanceOf,875}876877impl std::fmt::Display for BinaryOperator {878    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {879        match self {880            BinaryOperator::Equal => write!(f, "=="),881            BinaryOperator::NotEqual => write!(f, "!="),882            BinaryOperator::StrictEqual => write!(f, "==="),883            BinaryOperator::StrictNotEqual => write!(f, "!=="),884            BinaryOperator::LessThan => write!(f, "<"),885            BinaryOperator::LessEqual => write!(f, "<="),886            BinaryOperator::GreaterThan => write!(f, ">"),887            BinaryOperator::GreaterEqual => write!(f, ">="),888            BinaryOperator::ShiftLeft => write!(f, "<<"),889            BinaryOperator::ShiftRight => write!(f, ">>"),890            BinaryOperator::UnsignedShiftRight => write!(f, ">>>"),891            BinaryOperator::Add => write!(f, "+"),892            BinaryOperator::Subtract => write!(f, "-"),893            BinaryOperator::Multiply => write!(f, "*"),894            BinaryOperator::Divide => write!(f, "/"),895            BinaryOperator::Modulo => write!(f, "%"),896            BinaryOperator::Exponent => write!(f, "**"),897            BinaryOperator::BitwiseOr => write!(f, "|"),898            BinaryOperator::BitwiseXor => write!(f, "^"),899            BinaryOperator::BitwiseAnd => write!(f, "&"),900            BinaryOperator::In => write!(f, "in"),901            BinaryOperator::InstanceOf => write!(f, "instanceof"),902        }903    }904}905906#[derive(Debug, Clone, Copy, PartialEq, Eq)]907pub enum UnaryOperator {908    Minus,909    Plus,910    Not,911    BitwiseNot,912    TypeOf,913    Void,914}915916impl std::fmt::Display for UnaryOperator {917    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {918        match self {919            UnaryOperator::Minus => write!(f, "-"),920            UnaryOperator::Plus => write!(f, "+"),921            UnaryOperator::Not => write!(f, "!"),922            UnaryOperator::BitwiseNot => write!(f, "~"),923            UnaryOperator::TypeOf => write!(f, "typeof"),924            UnaryOperator::Void => write!(f, "void"),925        }926    }927}928929#[derive(Debug, Clone, Copy, PartialEq, Eq)]930pub enum UpdateOperator {931    Increment,932    Decrement,933}934935impl std::fmt::Display for UpdateOperator {936    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {937        match self {938            UpdateOperator::Increment => write!(f, "++"),939            UpdateOperator::Decrement => write!(f, "--"),940        }941    }942}943944#[derive(Debug, Clone, Copy, PartialEq, Eq)]945pub enum FunctionExpressionType {946    ArrowFunctionExpression,947    FunctionExpression,948    FunctionDeclaration,949}950951#[derive(Debug, Clone)]952pub struct TemplateQuasi {953    pub raw: String,954    pub cooked: Option<String>,955}956957#[derive(Debug, Clone)]958pub struct ManualMemoDependency {959    pub root: ManualMemoDependencyRoot,960    pub path: Vec<DependencyPathEntry>,961    pub loc: Option<SourceLocation>,962}963964#[derive(Debug, Clone)]965pub enum ManualMemoDependencyRoot {966    NamedLocal { value: Place, constant: bool },967    Global { identifier_name: String },968}969970#[derive(Debug, Clone, PartialEq, Eq)]971pub struct DependencyPathEntry {972    pub property: PropertyLiteral,973    pub optional: bool,974    pub loc: Option<SourceLocation>,975}976977// =============================================================================978// Place, Identifier, and related types979// =============================================================================980981#[derive(Debug, Clone)]982pub struct Place {983    pub identifier: IdentifierId,984    pub effect: Effect,985    pub reactive: bool,986    pub loc: Option<SourceLocation>,987}988989#[derive(Debug, Clone)]990pub struct Identifier {991    pub id: IdentifierId,992    pub declaration_id: DeclarationId,993    pub name: Option<IdentifierName>,994    pub mutable_range: MutableRange,995    pub scope: Option<ScopeId>,996    pub type_: TypeId,997    pub loc: Option<SourceLocation>,998}9991000#[derive(Debug, Clone)]1001pub struct MutableRange {1002    /// Unique identity for this logical range. Cloning preserves the ID1003    /// (same logical range); use `Environment::new_mutable_range()` to create1004    /// a range with a fresh ID.1005    pub id: MutableRangeId,1006    pub start: EvaluationOrder,1007    pub end: EvaluationOrder,1008}10091010impl MutableRange {1011    /// Returns true if the given evaluation order falls within this mutable range.1012    /// Corresponds to TS `inRange({id}, range)` / `isMutable(instr, place)`.1013    pub fn contains(&self, eval_order: EvaluationOrder) -> bool {1014        eval_order >= self.start && eval_order < self.end1015    }10161017    /// Returns true if this range has the same identity as `other`.1018    /// In the TS compiler, this corresponds to checking whether two mutableRange1019    /// references point to the same JS object (=== identity).1020    pub fn same_range(&self, other: &MutableRange) -> bool {1021        self.id == other.id1022    }1023}10241025#[derive(Debug, Clone)]1026pub enum IdentifierName {1027    Named(String),1028    Promoted(String),1029}10301031impl IdentifierName {1032    pub fn value(&self) -> &str {1033        match self {1034            IdentifierName::Named(v) | IdentifierName::Promoted(v) => v,1035        }1036    }1037}10381039#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]1040pub enum Effect {1041    #[serde(rename = "<unknown>")]1042    Unknown,1043    #[serde(rename = "freeze")]1044    Freeze,1045    #[serde(rename = "read")]1046    Read,1047    #[serde(rename = "capture")]1048    Capture,1049    #[serde(rename = "mutate-iterator?")]1050    ConditionallyMutateIterator,1051    #[serde(rename = "mutate?")]1052    ConditionallyMutate,1053    #[serde(rename = "mutate")]1054    Mutate,1055    #[serde(rename = "store")]1056    Store,1057}10581059impl Effect {1060    /// Returns true if this effect represents a mutable operation.1061    /// Mutable effects are: Capture, Store, ConditionallyMutate,1062    /// ConditionallyMutateIterator, and Mutate.1063    pub fn is_mutable(&self) -> bool {1064        matches!(1065            self,1066            Effect::Capture1067                | Effect::Store1068                | Effect::ConditionallyMutate1069                | Effect::ConditionallyMutateIterator1070                | Effect::Mutate1071        )1072    }1073}10741075impl std::fmt::Display for Effect {1076    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {1077        match self {1078            Effect::Unknown => write!(f, "<unknown>"),1079            Effect::Freeze => write!(f, "freeze"),1080            Effect::Read => write!(f, "read"),1081            Effect::Capture => write!(f, "capture"),1082            Effect::ConditionallyMutateIterator => write!(f, "mutate-iterator?"),1083            Effect::ConditionallyMutate => write!(f, "mutate?"),1084            Effect::Mutate => write!(f, "mutate"),1085            Effect::Store => write!(f, "store"),1086        }1087    }1088}10891090#[derive(Debug, Clone)]1091pub struct SpreadPattern {1092    pub place: Place,1093}10941095#[derive(Debug, Clone, Copy, PartialEq, Eq)]1096pub enum Hole {1097    Hole,1098}10991100#[derive(Debug, Clone)]1101pub struct ArrayPattern {1102    pub items: Vec<ArrayPatternElement>,1103    pub loc: Option<SourceLocation>,1104}11051106#[derive(Debug, Clone)]1107pub enum ArrayPatternElement {1108    Place(Place),1109    Spread(SpreadPattern),1110    Hole,1111}11121113#[derive(Debug, Clone)]1114pub struct ObjectPattern {1115    pub properties: Vec<ObjectPropertyOrSpread>,1116    pub loc: Option<SourceLocation>,1117}11181119#[derive(Debug, Clone)]1120pub enum ObjectPropertyOrSpread {1121    Property(ObjectProperty),1122    Spread(SpreadPattern),1123}11241125#[derive(Debug, Clone)]1126pub struct ObjectProperty {1127    pub key: ObjectPropertyKey,1128    pub property_type: ObjectPropertyType,1129    pub place: Place,1130}11311132#[derive(Debug, Clone)]1133pub enum ObjectPropertyKey {1134    String { name: String },1135    Identifier { name: String },1136    Computed { name: Place },1137    Number { name: FloatValue },1138}11391140#[derive(Debug, Clone, Copy, PartialEq, Eq)]1141pub enum ObjectPropertyType {1142    Property,1143    Method,1144}11451146impl std::fmt::Display for ObjectPropertyType {1147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {1148        match self {1149            ObjectPropertyType::Property => write!(f, "property"),1150            ObjectPropertyType::Method => write!(f, "method"),1151        }1152    }1153}11541155#[derive(Debug, Clone, PartialEq, Eq, Hash)]1156pub enum PropertyLiteral {1157    String(String),1158    Number(FloatValue),1159}11601161impl std::fmt::Display for PropertyLiteral {1162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {1163        match self {1164            PropertyLiteral::String(s) => write!(f, "{}", s),1165            PropertyLiteral::Number(n) => write!(f, "{}", n),1166        }1167    }1168}11691170#[derive(Debug, Clone)]1171pub enum PlaceOrSpread {1172    Place(Place),1173    Spread(SpreadPattern),1174}11751176#[derive(Debug, Clone)]1177pub enum ArrayElement {1178    Place(Place),1179    Spread(SpreadPattern),1180    Hole,1181}11821183#[derive(Debug, Clone)]1184pub struct LoweredFunction {1185    pub func: FunctionId,1186}11871188#[derive(Debug, Clone)]1189pub struct BuiltinTag {1190    pub name: String,1191    pub loc: Option<SourceLocation>,1192}11931194#[derive(Debug, Clone)]1195pub enum JsxTag {1196    Place(Place),1197    Builtin(BuiltinTag),1198}11991200#[derive(Debug, Clone)]1201pub enum JsxAttribute {1202    SpreadAttribute { argument: Place },1203    Attribute { name: String, place: Place },1204}12051206// =============================================================================1207// Variable Binding types1208// =============================================================================12091210#[derive(Debug, Clone, Copy, PartialEq, Eq)]1211pub enum BindingKind {1212    Var,1213    Let,1214    Const,1215    Param,1216    Module,1217    Hoisted,1218    Local,1219    Unknown,1220}12211222#[derive(Debug, Clone)]1223pub enum VariableBinding {1224    Identifier {1225        identifier: IdentifierId,1226        binding_kind: BindingKind,1227    },1228    Global {1229        name: String,1230    },1231    ImportDefault {1232        name: String,1233        module: String,1234    },1235    ImportSpecifier {1236        name: String,1237        module: String,1238        imported: String,1239    },1240    ImportNamespace {1241        name: String,1242        module: String,1243    },1244    ModuleLocal {1245        name: String,1246    },1247}12481249#[derive(Debug, Clone)]1250pub enum NonLocalBinding {1251    ImportDefault {1252        name: String,1253        module: String,1254    },1255    ImportSpecifier {1256        name: String,1257        module: String,1258        imported: String,1259    },1260    ImportNamespace {1261        name: String,1262        module: String,1263    },1264    ModuleLocal {1265        name: String,1266    },1267    Global {1268        name: String,1269    },1270}12711272impl NonLocalBinding {1273    /// Returns the `name` field common to all variants.1274    pub fn name(&self) -> &str {1275        match self {1276            NonLocalBinding::ImportDefault { name, .. }1277            | NonLocalBinding::ImportSpecifier { name, .. }1278            | NonLocalBinding::ImportNamespace { name, .. }1279            | NonLocalBinding::ModuleLocal { name, .. }1280            | NonLocalBinding::Global { name, .. } => name,1281        }1282    }1283}12841285// =============================================================================1286// Type system (from Types.ts)1287// =============================================================================12881289#[derive(Debug, Clone)]1290pub enum Type {1291    Primitive,1292    Function {1293        shape_id: Option<String>,1294        return_type: Box<Type>,1295        is_constructor: bool,1296    },1297    Object {1298        shape_id: Option<String>,1299    },1300    TypeVar {1301        id: TypeId,1302    },1303    Poly,1304    Phi {1305        operands: Vec<Type>,1306    },1307    Property {1308        object_type: Box<Type>,1309        object_name: String,1310        property_name: PropertyNameKind,1311    },1312    ObjectMethod,1313}13141315#[derive(Debug, Clone)]1316pub enum PropertyNameKind {1317    Literal { value: PropertyLiteral },1318    Computed { value: Box<Type> },1319}13201321// =============================================================================1322// ReactiveScope1323// =============================================================================13241325#[derive(Debug, Clone)]1326pub struct ReactiveScope {1327    pub id: ScopeId,1328    pub range: MutableRange,13291330    /// The inputs to this reactive scope (populated by later passes)1331    pub dependencies: Vec<ReactiveScopeDependency>,13321333    /// The set of values produced by this scope (populated by later passes)1334    pub declarations: Vec<(IdentifierId, ReactiveScopeDeclaration)>,13351336    /// Identifiers which are reassigned by this scope (populated by later passes)1337    pub reassignments: Vec<IdentifierId>,13381339    /// If the scope contains an early return, this stores info about it (populated by later passes)1340    pub early_return_value: Option<ReactiveScopeEarlyReturn>,13411342    /// Scopes that were merged into this one (populated by later passes)1343    pub merged: Vec<ScopeId>,13441345    /// Source location spanning the scope1346    pub loc: Option<SourceLocation>,1347}13481349/// A dependency of a reactive scope.1350#[derive(Debug, Clone)]1351pub struct ReactiveScopeDependency {1352    pub identifier: IdentifierId,1353    pub reactive: bool,1354    pub path: Vec<DependencyPathEntry>,1355    pub loc: Option<SourceLocation>,1356}13571358/// A declaration produced by a reactive scope.1359#[derive(Debug, Clone)]1360pub struct ReactiveScopeDeclaration {1361    pub identifier: IdentifierId,1362    pub scope: ScopeId,1363}13641365/// Early return value info for a reactive scope.1366#[derive(Debug, Clone)]1367pub struct ReactiveScopeEarlyReturn {1368    pub value: IdentifierId,1369    pub loc: Option<SourceLocation>,1370    pub label: BlockId,1371}13721373// =============================================================================1374// Aliasing effects (runtime types, from AliasingEffects.ts)1375// =============================================================================13761377use crate::object_shape::FunctionSignature;1378use crate::type_config::ValueKind;1379use crate::type_config::ValueReason;13801381/// Reason for a mutation, used for generating hints (e.g. rename to "Ref").1382#[derive(Debug, Clone, PartialEq, Eq)]1383pub enum MutationReason {1384    AssignCurrentProperty,1385}13861387/// Describes the aliasing/mutation/data-flow effects of an instruction or terminal.1388/// Ported from TS `AliasingEffect` in `AliasingEffects.ts`.1389#[derive(Debug, Clone)]1390pub enum AliasingEffect {1391    /// Marks the given value and its direct aliases as frozen.1392    Freeze { value: Place, reason: ValueReason },1393    /// Mutate the value and any direct aliases.1394    Mutate {1395        value: Place,1396        reason: Option<MutationReason>,1397    },1398    /// Mutate the value conditionally (only if mutable).1399    MutateConditionally { value: Place },1400    /// Mutate the value and transitive captures.1401    MutateTransitive { value: Place },1402    /// Mutate the value and transitive captures conditionally.1403    MutateTransitiveConditionally { value: Place },1404    /// Information flow from `from` to `into` (non-aliasing capture).1405    Capture { from: Place, into: Place },1406    /// Direct aliasing: mutation of `into` implies mutation of `from`.1407    Alias { from: Place, into: Place },1408    /// Potential aliasing relationship.1409    MaybeAlias { from: Place, into: Place },1410    /// Direct assignment: `into = from`.1411    Assign { from: Place, into: Place },1412    /// Creates a value of the given kind at the given place.1413    Create {1414        into: Place,1415        value: ValueKind,1416        reason: ValueReason,1417    },1418    /// Creates a new value with the same kind as the source.1419    CreateFrom { from: Place, into: Place },1420    /// Immutable data flow (escape analysis only, no mutable range influence).1421    ImmutableCapture { from: Place, into: Place },1422    /// Function call application.1423    Apply {1424        receiver: Place,1425        function: Place,1426        mutates_function: bool,1427        args: Vec<PlaceOrSpreadOrHole>,1428        into: Place,1429        signature: Option<FunctionSignature>,1430        loc: Option<SourceLocation>,1431    },1432    /// Function expression creation with captures.1433    CreateFunction {1434        captures: Vec<Place>,1435        function_id: FunctionId,1436        into: Place,1437    },1438    /// Mutation of a value known to be frozen (error).1439    MutateFrozen {1440        place: Place,1441        error: CompilerDiagnostic,1442    },1443    /// Mutation of a global value (error).1444    MutateGlobal {1445        place: Place,1446        error: CompilerDiagnostic,1447    },1448    /// Side-effect not safe during render.1449    Impure {1450        place: Place,1451        error: CompilerDiagnostic,1452    },1453    /// Value is accessed during render.1454    Render { place: Place },1455}14561457/// Combined Place/Spread/Hole for Apply args.1458#[derive(Debug, Clone)]1459pub enum PlaceOrSpreadOrHole {1460    Place(Place),1461    Spread(SpreadPattern),1462    Hole,1463}14641465/// Aliasing signature for function calls.1466/// Ported from TS `AliasingSignature` in `AliasingEffects.ts`.1467#[derive(Debug, Clone)]1468pub struct AliasingSignature {1469    pub receiver: IdentifierId,1470    pub params: Vec<IdentifierId>,1471    pub rest: Option<IdentifierId>,1472    pub returns: IdentifierId,1473    pub effects: Vec<AliasingEffect>,1474    pub temporaries: Vec<Place>,1475}14761477// =============================================================================1478// Type helper functions (ported from HIR.ts)1479// =============================================================================14801481use crate::object_shape::BUILT_IN_ARRAY_ID;1482use crate::object_shape::BUILT_IN_JSX_ID;1483use crate::object_shape::BUILT_IN_MAP_ID;1484use crate::object_shape::BUILT_IN_PROPS_ID;1485use crate::object_shape::BUILT_IN_REF_VALUE_ID;1486use crate::object_shape::BUILT_IN_SET_ID;1487use crate::object_shape::BUILT_IN_USE_OPERATOR_ID;1488use crate::object_shape::BUILT_IN_USE_REF_ID;14891490/// Returns true if the type (looked up via identifier) is primitive.1491pub fn is_primitive_type(ty: &Type) -> bool {1492    matches!(ty, Type::Primitive)1493}14941495/// Returns true if the type is the props object.1496pub fn is_props_type(ty: &Type) -> bool {1497    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_PROPS_ID)1498}14991500/// Returns true if the type is an array.1501pub fn is_array_type(ty: &Type) -> bool {1502    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_ARRAY_ID)1503}15041505/// Returns true if the type is a Set.1506pub fn is_set_type(ty: &Type) -> bool {1507    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_SET_ID)1508}15091510/// Returns true if the type is a Map.1511pub fn is_map_type(ty: &Type) -> bool {1512    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_MAP_ID)1513}15141515/// Returns true if the type is JSX.1516pub fn is_jsx_type(ty: &Type) -> bool {1517    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_JSX_ID)1518}15191520/// Returns true if the identifier type is a ref value.1521pub fn is_ref_value_type(ty: &Type) -> bool {1522    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_REF_VALUE_ID)1523}15241525/// Returns true if the identifier type is useRef.1526pub fn is_use_ref_type(ty: &Type) -> bool {1527    matches!(ty, Type::Object { shape_id: Some(id) } if id == BUILT_IN_USE_REF_ID)1528}15291530/// Returns true if the type is a ref or ref value.1531pub fn is_ref_or_ref_value(ty: &Type) -> bool {1532    is_use_ref_type(ty) || is_ref_value_type(ty)1533}15341535/// Returns true if the type is a useState result (BuiltInUseState).1536pub fn is_use_state_type(ty: &Type) -> bool {1537    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_USE_STATE_ID)1538}15391540/// Returns true if the type is a setState function (BuiltInSetState).1541pub fn is_set_state_type(ty: &Type) -> bool {1542    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_SET_STATE_ID)1543}15441545/// Returns true if the type is a useEffect hook.1546pub fn is_use_effect_hook_type(ty: &Type) -> bool {1547    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_HOOK_ID)1548}15491550/// Returns true if the type is a useLayoutEffect hook.1551pub fn is_use_layout_effect_hook_type(ty: &Type) -> bool {1552    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_LAYOUT_EFFECT_HOOK_ID)1553}15541555/// Returns true if the type is a useInsertionEffect hook.1556pub fn is_use_insertion_effect_hook_type(ty: &Type) -> bool {1557    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_INSERTION_EFFECT_HOOK_ID)1558}15591560/// Returns true if the type is a useEffectEvent function.1561pub fn is_use_effect_event_type(ty: &Type) -> bool {1562    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_USE_EFFECT_EVENT_ID)1563}15641565/// Returns true if the type is a ref or ref-like mutable type (e.g. Reanimated shared values).1566pub fn is_ref_or_ref_like_mutable_type(ty: &Type) -> bool {1567    matches!(ty, Type::Object { shape_id: Some(id) }1568        if id == object_shape::BUILT_IN_USE_REF_ID || id == object_shape::REANIMATED_SHARED_VALUE_ID)1569}15701571/// Returns true if the type is the `use()` operator (React.use).1572pub fn is_use_operator_type(ty: &Type) -> bool {1573    matches!(1574        ty,1575        Type::Function { shape_id: Some(id), .. }1576            if id == BUILT_IN_USE_OPERATOR_ID1577    )1578}15791580/// Returns true if the type is a plain object (BuiltInObject).1581pub fn is_plain_object_type(ty: &Type) -> bool {1582    matches!(ty, Type::Object { shape_id: Some(id) } if id == object_shape::BUILT_IN_OBJECT_ID)1583}15841585/// Returns true if the type is a startTransition function (BuiltInStartTransition).1586pub fn is_start_transition_type(ty: &Type) -> bool {1587    matches!(ty, Type::Function { shape_id: Some(id), .. } if id == object_shape::BUILT_IN_START_TRANSITION_ID)1588}15891590#[cfg(test)]1591mod tests {1592    use super::*;15931594    #[test]1595    fn test_format_js_number() {1596        // Scientific notation for large numbers (>= 1e21)1597        assert_eq!(format_js_number(1e21), "1e+21");1598        assert_eq!(format_js_number(1.5e21), "1.5e+21");1599        assert_eq!(1600            format_js_number(2.18739127891275e22),1601            "2.18739127891275e+22"1602        );1603        assert_eq!(format_js_number(1e100), "1e+100");1604        assert_eq!(format_js_number(-1e21), "-1e+21");1605        assert_eq!(format_js_number(-1e100), "-1e+100");16061607        // Scientific notation for small numbers (< 1e-6)1608        assert_eq!(format_js_number(1e-7), "1e-7");1609        assert_eq!(format_js_number(5e-7), "5e-7");1610        assert_eq!(format_js_number(1.5e-8), "1.5e-8");1611        assert_eq!(format_js_number(-1.5e-8), "-1.5e-8");16121613        // Non-scientific large numbers (< 1e21)1614        assert_eq!(format_js_number(1e20), "100000000000000000000");1615        assert_eq!(format_js_number(1e-6), "0.000001");16161617        // Integers1618        assert_eq!(format_js_number(0.0), "0");1619        assert_eq!(format_js_number(-0.0), "0");1620        assert_eq!(format_js_number(1.0), "1");1621        assert_eq!(format_js_number(100.0), "100");16221623        // Regular floats1624        assert_eq!(format_js_number(1.5), "1.5");1625        assert_eq!(format_js_number(0.5), "0.5");1626        assert_eq!(format_js_number(0.1), "0.1");16271628        // Special values1629        assert_eq!(format_js_number(f64::NAN), "NaN");1630        assert_eq!(format_js_number(f64::INFINITY), "Infinity");1631        assert_eq!(format_js_number(f64::NEG_INFINITY), "-Infinity");1632    }1633}

Code quality findings 3

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let (coeff, exp_str) = formatted.split_once('e').unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let exp: i32 = exp_str.parse().unwrap();
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use super::*;

Get this view in your editor

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