Repository navigation
fix(es/parser): Respect line breaks after declare - #12353
Donny/강동윤 (kdy1) merged 2 commits into
Conversation
TypeScript applies ASI when a line break follows `declare`, so
declare
class C {}
is the expression statement `declare;` followed by a regular class.
SWC parsed it as an ambient class, which the TypeScript strip then
removes, and rejected `declare\nfunction f() {}` with "An implementation
cannot be declared in ambient contexts".
Return early from the `declare` arm of `parse_ts_expr_stmt` when the next
token is on a new line, like the `module` arm already does. Flow is left
unchanged.
🦋 Changeset detectedLatest commit: f235855 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will improve performance by 2.01%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | es/lints/libs/three |
50 ms | 49.1 ms | +2.01% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing Dev-next-gen:fix/es-parser-declare-line-break (f235855) with main (5a2096f)
Footnotes
-
67 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
…terface04 With the line break after `declare` respected, `interface` on the next line is parsed as an expression, so the swc crate's tsc snapshots now also report "Expression expected" there.
|
The red The Benchmark failures are unrelated. |
Description:
I noticed that SWC treats
declareas a modifier even when a line break separates it from the declaration that follows. TypeScript applies ASI there, so this input:is the expression statement
declare;followed by an ordinary class. SWC parsed it as an ambient class instead, which the TypeScript strip then removes from the output, soCsilently disappears at runtime. Withfunctionit fails outright:declare\nfunction f() {}is rejected with "An implementation cannot be declared in ambient contexts" although it is valid TypeScript.The fix returns early from the
declarearm ofparse_ts_expr_stmtwhen the next token is on a new line, which is what themodulearm ofparse_ts_declalready does (declare\nmodule M {}was the one case that parsed correctly). Babel'stsTryParseDeclarehas the sameisLineTerminator()check. I left Flow alone.I compared SWC's statement kinds against
ts.createSourceFile(TypeScript 6.0.3) fordeclarefollowed by a newline andclass,function,const,let,var,enum,const enum,namespace,module,type,interface,abstract class,globaland a multiline comment. Before the change onlymodulematched, and after it all of them do.declare enum E {}and the other same-line forms are unchanged, and so is theexport declarepath.On whether this is safe: the only inputs that change are the ones where a line break follows
declare, and for those the old AST contradicted whattscproduces. The repository already carries TypeScript's own conformance testtests/tsc/asiPreventsParsingAsInterface04.ts, whose comments saydeclare,interfaceandIshould each be identifiers followed by a block, but its snapshot recorded an ambient interface. I regenerated that snapshot, which now matches those comments. I also added adeclare-line-breakfixture. It fails without the change and passes with it.cargo test -p swc_ecma_parser --test typescriptpasses apart fromtype_only_import_specifier/invalid_type_only, which fails the same way onmainin my environment.Related issue (if exists):
Same family as #12341 (line breaks before enum names).
AI tools used