← На главную

Babel больше не падает на неизвестные statement

10.06.2026 09:19 · hackernews

Babel научился обрабатывать такие типы statement, которых нет в его типизированном AST. Раньше при первой же непонятной ноде скрипт просто падал. Теперь всё иначе.

Statement получил новый вариант Unknown(UnknownStatement) — он хранит сырую ноду целиком, без разбора. Десериализация написана вручную: известные теги (type) уходят в KnownStatement, который выдаёт точную ошибку на уровне поля, если нода кривая. А что не вписано — уходит в catch-all. Для top-level такие statement сохраняются как есть, при повторной сериализации они уходят в исходном виде. А внутри функций — стандартный UnsupportedSyntax bailout с инструкцией UnsupportedNode, которая несёт сырую ноду.

Всё это завязано на макрос known_statements!. Он одновременно генерирует enum, From-преобразования и список тегов. Три места не могут разойтись — они берутся из одного источника. Если добавить вариант в Statement, но не в макрос — получится молчаливый разрыв, про него написано в документации.

UnknownStatement кеширует BaseNode для позиций. Мутатор with_raw_mut обновляет кеш и не даёт удалить поле type, чтобы два представления не рассинхронизировались.

Программные анализаторы теперь явно обрабатывают Unknown. Сканер reference-before-declaration ходит по сырой ноде в поисках идентификаторов (например, export = X ссылается на X). Prefilter и return-analysis просто ничего не делают с этим типом.

Конвертеры SWC и OXC при обратном преобразовании ставят runtime-ловушку — throw в сгенерированном коде. Она сработает, если forward-конвертер перестанет переписывать такие statement в EmptyStatement.

Десериализация теперь сначала материализует serde_json::Value для каждого statement, а потом разбирает его типизированно. Раньше derive-десериализация тоже буферизовала каждую ноду через внутренний Content serde, так что разница лишь в форме аллокаций, не в асимптотике.

Проверено: юнит-тесты AST (включая битые и граничные случаи), интеграционный тест на bailout в теле функции, round_trip на трёх фикстурах, полные e2e-тесты Babel с паритетом событий, cargo test --workspace зелёный.

Есть отдельная правка для codegen (коммит 0957b55). Раньше UnsupportedNode пытался дискриминировать statement vs expression через десериализацию в Statement. С новым снисходительным десериализатором это ломало ~10 фикстур: expression-ноды превращались в сырые statement и теряли lvalue-временные переменные. Теперь codegen дискриминирует явно: известные statement-теги парсятся типизированно (падение при ошибке — это инвариант), теги Expression и PatternLike (строгие enum, без catch-all) идут как в expression-кодогенерации. А genuinely unmodeled statement — только от unknown-statement lowering bailout, то есть из позиции statement — уходят как Statement::Unknown и выводятся дословно. Макрос known_statements! теперь экспортирует is_known_statement_type для этого.

Читать оригинал →