Named exports must have a string literal module source once `from` is present. The parser previously combined consuming `from` with checking whether the next token was a literal:
```rust
if self.eat(Kind::From) && self.cur_kind().is_literal()
```
When the next token was not a literal, `from` had already been consumed, but parsing fell back to a local export. This incorrectly accepted `export {} from;`, `export {} from` at EOF, and this input:
```js
export {} from
foo();
```
In the last case, the newline allowed automatic semicolon insertion, so the parser accepted a local empty export followed by `foo()` despite the incomplete `from` clause.
Remove the literal-kind guard so consuming `from` always leads to `parse_literal_string()`. That helper already requires a string literal and handles malformed input through the existing parser diagnostics. A missing source now produces a syntax error instead of becoming a local export, while unterminated strings and comments retain the standard lexer diagnostics.
Valid local exports such as `export { foo };` and re-exports such as `export { foo } from "./module.js";` keep their existing behavior, including a module source placed on the next line.
Stop parsing delimited-list elements after a separator when EOF or an undetermined token follows, preventing malformed tuple syntax from creating reversed spans during error recovery.
Validated with `cargo coverage -- parser --filter oxc-23670 --detail`.
AI assistance was used to prepare this change.
Closes#23670.
## Summary
Narrow `TSInterfaceHeritage` from a general JavaScript `Expression` to `TSTypeName`.
```rust
pub struct TSInterfaceHeritage<'a> {
pub type_name: TSTypeName<'a>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Breaking Change
The Rust AST field changes from:
```rust
expression: Expression<'a>
```
to:
```rust
type_name: TSTypeName<'a>
```
## Why?
The current AST can represent shapes such as:
```ts
interface A extends foo() {}
interface A extends A + B {}
interface A extends new Foo() {}
interface A extends true {}
```
Which is an overly wide type since all of the above variants are invalid.
This PR tightens up the AST, making these invalid variants irrepresentable.
## How to migrate?
This should be a trivial migration:
1. instead of accessing `expression` on `TSInterfaceHeritage`, now access `type_name`
2. change any pattern matching on the `type_name` field to use `TypeName` (this is a smaller subset so should allow deleting code!)
closes https://github.com/oxc-project/backlog/issues/215
Quoted module names are only valid in ambient contexts in TypeScript, but we were accepting them in regular TypeScript files.
This reports TS1035 when a quoted module name appears outside a declaration file or ambient declaration. The conformance snapshots now cover both TypeScript cases, and the old invalid codegen fixture has been replaced with a valid internal module declaration.
## Summary
- report TS1234 for function-local ambient module declarations
- preserve TS1235 for function-local namespace and internal module declarations
- report TS2435 for ambient modules nested in namespaces while allowing module augmentation inside ambient external modules
## Babel Conformance Snapshot Parse Failure
This intentionally introduces one known Babel conformance false positive: `typescript/scope/redeclaration-in-nested-module/input.ts`. The Babel harness supplies `sourceType: "module"`, so Oxc reports TS2435 for its nested ambient module. This affects one unique fixture in both the parser and semantic Babel summaries. This change aligns with TS, and TS expects this error.
## Summary
- report TypeScript diagnostic TS18041 for `return` statements in class static initialization blocks
- reuse the existing `NewTarget`/`Return` parser-context invariant without adding a new context flag
- update the TypeScript, Babel, and Test262 parser snapshots
## Root cause
Class static blocks disable the parser's `Return` context, so their `return` statements previously fell through to the generic top-level TS1108 diagnostic. Static blocks already enable `NewTarget`, which distinguishes this case when `Return` is absent.
## Summary
- add a dedicated diagnostic for invalid `for await` loops
- report “`for await` loops are only allowed…” instead of the generic `await` expression message
- update Babel and TypeScript parser conformance snapshots
## Impact
Users now receive an error that identifies the invalid loop construct directly while retaining the existing guidance to remove `await` or mark the enclosing function `async`.
## Summary
Adds context notes to `Cannot assign to '{x0}' in strict mode` diagnostics, explaining whether strict mode comes from an explicit directive, class body, or module.
## Summary
Reserved-keyword diagnostics now explain the language context that makes an identifier invalid, rather than only reporting that the keyword is reserved.
## Details
- Add context-aware notes for strict mode, classes, and modules.
- Add a dedicated note for `await` in module code.
- Update parser-conformance snapshots for the expanded diagnostics.
Before:
```text
× The keyword 'package' is reserved
```
After, for strict-mode code:
```text
× The keyword 'package' is reserved
note: This identifier is reserved in strict mode code
```
After, for class code:
```js
class Example {
method() {
const static = 1;
}
}
```
```text
× The keyword 'static' is reserved
note: Classes are always strict mode code
```
After, for module code:
```js
export const await = 1;
```
```text
× The keyword 'await' is reserved
note: The identifier `await` is reserved in module code
## Summary
Make TS1221 underline the generator declaration consistently from the `*` token.
## Why
The previous diagnostic used the generic function span, but that span has different start points depending on how the function is parsed:
- A class method enters function parsing after its name, so `declare class C { *generator(): any; }` highlighted only `(): any;` (starting at column 15), omitting both the generator marker and its name.
- A function declaration starts its span at the `function` keyword, so `declare namespace M { function *generator(): any; }` highlighted the whole declaration (starting at column 5), including text that is not the invalid generator construct.
For example, the prior output treated these equivalent generator markers differently:
```ts
declare class C {
*generator(): any;
// ^^^^^^^^
}
declare namespace M {
function *generator(): any;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
```
The parser now carries the `*` token location as `Option<u32>` while parsing a function. TS1221 uses that location through the declaration end, so both cases consistently highlight the relevant generator declaration:
```ts
*generator(): any;
^^^^^^^^^^^^^^^^^^
```
The AST continues to store `generator` as a boolean; the location exists only while parsing diagnostics.
## Summary
- report TS1016 while parsing formal parameter lists
- preserve initialized-parameter and rest-parameter behavior
- remove the equivalent semantic check and diagnostic
- update parser and semantic coverage snapshots
Follows #24596, which has now merged.
## Summary
- Report operator-specific diagnostics when unary and `await` expressions are used as exponentiation left operands.
- Reject unparenthesized TypeScript type assertions in the same position while preserving explicitly parenthesized forms.
- Add regression fixtures and update the affected conformance snapshots.
## Summary
- Align auto-accessor definite assignment assertions with TypeScript.
- Report TS1263 when an accessor has both `!` and an initializer.
- Report TS1264 when an accessor has `!` but no type annotation.
- Report TS1255 for typed definite assertions in static, abstract, and ambient contexts.
- Highlight only the `!` token, matching TypeScript.
fixes#24503.
## TypeScript behavior
Given:
```ts
class A {
accessor missingType!;
// ^: TS1264: Declarations with definite assignment assertions must also have type annotations.
accessor initialized! = 1;
// ^: TS1263: Declarations with initializers cannot also have definite assignment assertions.
accessor typed!: number;
// ^: valid
static accessor staticTyped!: number;
// ^: TS1255: A definite assignment assertion '!' is not permitted in this context.
}
```
TypeScript treats auto-accessors as property declarations and applies the following grammar check:
https://github.com/microsoft/TypeScript/blob/637d5746b70257028fb95aad32ddec6b26ab0a14/src/compiler/checker.ts#L53443-L53452
This establishes the diagnostic precedence:
1. An initializer produces TS1263.
2. Otherwise, a missing type annotation produces TS1264.
3. Otherwise, an invalid context produces TS1255.
Part of the effort to remove all checkpoint usage from the parser.
Replace the speculative JSX parse (checkpoint + rewind) in `parse_jsx_in_non_jsx_error` with a direct fatal error on the `<` token, with help to enable JSX in the parser options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Store class property definite assignment assertion positions as `u32` offsets instead of carrying a full `Span`.
- Reconstruct the one-character diagnostic span only when emitting class definite-assignment diagnostics.
- aligns the behaviour for definite assertion errors with variables by reporting on the definite assertion rather than on the whole class member
- Match TypeScript’s diagnostic precedence for class property definite assignment assertions.
- Report TS1264 when a class property uses ! without a type annotation.
- Report TS1255 when a definite assignment assertion is used in disallowed class property contexts, including static.
Inside an internal `namespace N {}` / `module N {}`, statements that
reference a
module are not permitted — this matches both babel and TypeScript:
- `import ... from "m"` / `import x = require("m")` → TS1147
- `export ... from "m"` / `export * from "m"` → TS1194
- `export default ...` → TS1319
- `export = ...` → TS1063
Bare `export { x }` (re-export of locals, no module source) and `import
x = A.B`
(entity alias) stay valid, as do external modules (`declare module "x"
{}`) where
these forms are allowed — the check only runs for internal namespaces,
so those
never reach it.
Also removes TS1147 from the coverage denylist
(`NOT_SUPPORTED_ERROR_CODES`), since
oxc now detects it.
Conformance (no regressions, positive/AST stay 100% everywhere):
- parser_babel negative 1696 → 1704
- parser_typescript negative 1585 → 1594
A generator (`function*` / `*method`) is not allowed in an ambient context
(TS1221), and a generator overload signature with no body is not allowed
(TS1222). Mirrors tsc's `checkGrammarForGenerator`:
declare function* f(): any; // now TS1221
declare namespace N { function* g(); } // now TS1221
declare class C { *m(); } // now TS1221
function* f(): any; // overload → now TS1222
function* f() { yield 1; }
function* f() { yield 1; } // normal generator → still valid
The check lives in `parse_function` next to the TS1183 implementation-in-ambient
check and keys off `ctx.has_ambient()` / `body.is_none()`. Class methods are
covered too (`class.rs` does not check generators, so there is no duplicate
diagnostic).
Also fixes a latent bug: `parse_ts_declare_function` hard-coded `generator: false`
and never consumed the `*`, so `declare function* f()` silently dropped the
generator flag. It now eats the `*` and threads the flag through (which is what
lets TS1221 fire there).
parser_typescript +7 negative-pass (generatorInAmbientContext1-4, generatorOverloads1-3);
positive/AST stay 100%.
`export as namespace X` declares a UMD global export, which may only appear at the
top level of a module or a `.d.ts` file. Inside an internal `namespace`/`module`
body it is invalid:
namespace N { export as namespace N; } // now TS1316
module M { export as namespace M; } // now TS1316
declare namespace N { export as namespace N; } // now TS1316
export as namespace Lib; // top level → still valid
This extends the namespace-body validation (`check_namespace_body_statement`) with
a `TSNamespaceExportDeclaration` arm emitting TS1316 ("Global module exports may
only appear at top level."), matching tsc and babel. The statement is already
checked inline as each direct child of the namespace body is parsed, so there is
no extra pass. External module declarations (`declare module "x" {}`) are left
unrestricted, consistent with the existing namespace-body checks.
parser_babel +1 negative-pass; positive/AST stay 100%.
A function declaration with a body is an implementation, which is not allowed in
an ambient context. oxc already emitted TS1183 ("An implementation cannot be
declared in ambient contexts") when the function carried its own `declare`
modifier, but missed functions that inherit the ambient context from an enclosing
`declare module` / `declare namespace` or a `.d.ts` file:
declare module "m" { function f() {} } // now TS1183
declare namespace N { function f() {} } // now TS1183
// foo.d.ts
function foo(): any {} // now TS1183
Bodyless signatures stay valid (`declare module "m" { function f(): void; }`,
`export declare function f(): void;`).
The check now keys solely off `ctx.has_ambient()` instead of the function's own
`declare` modifier, scoped to function declarations. Class methods are excluded
because they are already validated in `check_method_definition`, which avoids a
duplicate diagnostic. This matches `typescript-go`'s
`checkGrammarStatementInAmbientContext` / function-like body check, and babel.
The fast path is unchanged: `has_ambient()` is a cheap bitflag test that
short-circuits for all non-ambient code.
parser_babel +3 and parser_typescript +1 negative-pass; positive/AST stay 100%.
`static`/`abstract` and `declare`/`override` are mutually exclusive in either
order (TS1243):
- `static abstract m()` / `abstract static m()`
- `declare override x` / `override declare x`
oxc already validated modifier order (TS1029) and duplicates (TS1030). This extends
the existing `ILLEGAL_PRECEDING_MODIFIERS` table in both directions for these pairs
and reports them as "cannot be used with" (TS1243) rather than "must precede" — the
fast path is unchanged (still a table lookup + AND); only the cold error path picks
the message.
Matches babel and TypeScript: parser_babel +1 negative-pass
(`class/modifiers-incompatible`), positive/AST stay 100% across all suites, and the
correct TS1243 diagnostic is now emitted on existing TypeScript cases.
Inside an internal `namespace N {}` / `module N {}`, statements that reference a
module are not permitted — this matches both babel and TypeScript:
- `import ... from "m"` / `import x = require("m")` → TS1147
- `export ... from "m"` / `export * from "m"` → TS1194
- `export default ...` → TS1319
- `export = ...` → TS1063
Bare `export { x }` (re-export of locals, no module source) and `import x = A.B`
(entity alias) stay valid, as do external modules (`declare module "x" {}`) where
these forms are allowed — the check only runs for internal namespaces.
Each statement is validated inline as the namespace body is parsed (no second pass).
Also removes TS1147 from the coverage denylist (`NOT_SUPPORTED_ERROR_CODES`), since
oxc now detects it.
Conformance: parser_babel +8 and parser_typescript +9 negative-passes vs main, with
positive/AST staying 100% across all parser suites (no regressions).
Made the errors for `({...[]} = {});` and `const {...[]} = obj;` a bit more easier to understand:
```
× Invalid rest element target in destructuring assignment
╭─[misc/fail/rest-assignment-target.js:1:6]
1 │ ({...[]} = {});
· ──
╰────
help: Expected an identifier or member expression, like `...rest` or `...obj.prop`.
```
```
× Invalid rest element target in destructuring pattern
╭─[misc/fail/rest-binding-element.js:1:11]
1 │ const {...[]} = obj;
· ──
╰────
help: Expected an identifier, like `...rest`.
```
This improves the parser diagnostic for invalid `import.<property>` access.
Previously, invalid properties like `import.sync("baz")` were reported as if only `import.meta` were valid, which is misleading now that `import.source()` and `import.defer()` are also supported.
Before:
```text
× The only valid meta property for import is import.meta
╭─[index.ts]
37 │
38 │ import.foo();
──────────
39 │
╰────
```
After:
```text
× The only valid property accesses on import are `import.meta`, `import.source()`, and `import.defer()`
╭─[index.ts]
37 │
38 │ import.foo();
──────────
39 │
╰────
```
Also adds a coverage fixture for the `import.sync("baz")` case and updates the parser coverage snapshots.
`illegal_modifier_error` contained a large amount of repetitious code, and was hard to read. Convert it to a shorter version which makes the logic clearer, reusing the lookup table introduced in #20748.
This change also reduces the size of generated code considerably and makes it faster, but #20748 moved this code onto a cold path, so that doesn't really matter. The main benefit is greater readability.
One test case produces a slightly different error after this change.
```ts
class D extends B {
constructor(override readonly public foo: string) {}
}
```
```diff
- TS(1029): 'public' modifier must precede 'override' modifier.
+ TS(1029): 'public' modifier must precede 'readonly' modifier.
```
Both errors are correct, and which gets flagged was arbitrary before - it wasn't dependent on the order of modifiers in the source code, only the order of `if` branches in `illegal_modifier_error`. So IMO this change isn't important - it's just a *different* arbitrary order now.
## Summary
- Report a syntax error for `new super()` per the ECMAScript spec grammar
- `MemberExpression` includes `SuperProperty` (`super.x`, `super[x]`) but NOT bare `super`, so `new super()` is invalid
- `new super.method()` and `new super[x]()` remain valid
Closes#20306🤖 Generated with [Claude Code](https://claude.com/claude-code)