swc-project/swc · error

UnsafeId.to_id() is not allowed because it is very likely to

Error message

UnsafeId.to_id() is not allowed because it is very likely to be unsafe

What it means

UnsafeId (swc_ecma_ast) is a performance-oriented IdentLike implementation created through the unsafe unsafe_id_from_ident path; converting it back to an Id via IdentLike::to_id()/into_id() is deliberately blocked with unreachable! because such a conversion would be 'very likely to be unsafe' given how UnsafeId instances are constructed. The panic is an intentional guard against misusing the fast identifier type in generic code.

Source

Thrown at crates/swc_ecma_utils/src/ident.rs:79

    #[inline]
    fn to_id(&self) -> Id {
        (self.sym.clone(), self.ctxt)
    }

    #[inline]
    fn into_id(self) -> Id {
        (self.sym, self.ctxt)
    }
}

impl IdentLike for UnsafeId {
    fn from_ident(i: &Ident) -> Self {
        unsafe { unsafe_id_from_ident(i) }
    }

    fn to_id(&self) -> Id {
        unreachable!("UnsafeId.to_id() is not allowed because it is very likely to be unsafe")
    }

    fn into_id(self) -> Id {
        unreachable!("UnsafeId.into_id() is not allowed because it is very likely to be unsafe")
    }
}

View on GitHub (pinned to 5176682b65)

Solutions

  1. Key your maps/scopes by Id (or (Atom, SyntaxContext)) wherever downstream code needs to_id/into_id; keep UnsafeId only in leaf passes that never convert back.
  2. Extract the Id at the boundary before switching to UnsafeId: store `(sym.clone(), ctxt)` while you still have the Ident.
  3. Audit call sites with `rg '\.to_id\(\)|\.into_id\(\)'` in code generic over `I: IdentLike`.
  4. If a public swc API forces the conversion, report it as a bug against that API.

Example fix

// before: UnsafeId flows into code that converts keys back to Id
let mut usage: IdentUsageFinder<UnsafeId> = ...; // later calls .to_id()

// after: convert while the Ident is still around, key by Id
let key: Id = (ident.sym.clone(), ident.ctxt);
let mut usage: IdentUsageFinder<Id> = ...;
Defensive patterns

Strategy: type-guard

Validate before calling

// Extract the Id while a real Ident is available, before switching to UnsafeId
use swc_ecma_ast::Ident; use swc_ecma_utils::IdentLike;
fn id_of(i: &Ident) -> Id { (i.sym.clone(), i.ctxt) }
// store id_of(&ident) as the map key; pass UnsafeId only through code that never converts

Type guard

// Compile-time discipline: any API that will call to_id/into_id must be
// instantiated with Id (or (Atom, SyntaxContext)), never UnsafeId.
fn takes_convertible_key<I: IdentLike>(k: &I) -> Id where I: Clone { k.to_id() } // OK for Id, PANICS for UnsafeId — keep UnsafeId out of these call paths

Prevention

When it happens

Trigger: Instantiating generic utilities bound by IdentLike (scopes, ident-usage collectors, caches in swc_ecma_utils etc.) with UnsafeId and letting them call .to_id() or .into_id() on their keys/values — typically when someone swaps Id for UnsafeId for speed and forgets a downstream conversion.

Common situations: Contributors optimizing hot passes by keying maps with UnsafeId; copying Id-based code and only changing the type parameter; using APIs whose default type parameter is UnsafeId on a version where that became opt-in.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/4e84288faf5af3cf. Report an issue: GitHub.