Commit Graph

110 Commits

Author SHA1 Message Date
camc314 d61e3bf887 fix(parser): validate TS named tuple rest elements (#26419)
## Problem

Named tuple rest elements bypass the parser's existing checks for optional elements after an array rest and for multiple array rests. For example, both of these declarations currently parse successfully even though TypeScript rejects them:

```ts
type OptionalAfterRest = [...a: string[], b?: number];
type RestAfterRest = [...a: string[], ...b: number[]];
```

The equivalent unnamed forms already produce TS1266 and TS1265, respectively. Mixed named and unnamed array rests have the same gap when either rest is named.

## Cause and change

`parse_tuple_type` inspects `TSRestType.type_annotation` to decide whether an element is an array rest. For a named element, that annotation is a `TSNamedTupleMember`, and the actual type is stored in its `element_type`. Matching directly on the outer annotation therefore skips the array check and fails to track the rest element.

Unwrap the named member with `as_ts_type()` before applying the existing classification. Named and unnamed elements then share the same validation and existing diagnostics:

- An optional element after an array rest reports TS1266.
- A second array rest reports TS1265, including mixed named and unnamed forms.

The diagnostic spans continue to use the complete tuple elements, including their labels. The change borrows the underlying type without changing the AST representation or introducing allocations.

## Preserved behavior

A rest marker alone does not make a spread an unbounded array. Fixed-tuple and generic variadic spreads continue to be accepted, including:

```ts
type FixedBeforeOptional = [...a: [string], b?: number];
type RestBeforeFixed = [...a: string[], ...b: [number]];
type VariadicBeforeOptional<T extends unknown[]> = [...a: T, b?: number];
type RestBeforeVariadic<T extends unknown[]> = [...a: string[], ...b: T];
type MultipleVariadic<T extends unknown[], U extends unknown[]> = [...a: T, ...b: U];
```

This change preserves the existing type-reference classification; it does not add type resolution or change how shadowed `Array` references are handled.
2026-09-07 17:43:20 +00:00
camc314 10521b288f fix(parser): allow escaped type default import bindings (#26409)
TypeScript allows a default import binding named `type` to contain Unicode escapes. Oxc currently reports `Keywords cannot contain escape characters` for these valid imports:

```ts
import t\u0079pe from "m";
import t\u0079pe, { x } from "m";
import t\u0079pe, * as ns from "m";
```

In each example, `t\u0079pe` is an identifier whose decoded name is `type`, and the declaration is a value import.

The parser initially recognizes the decoded `type` token before it has enough context to distinguish a default binding from a type-only import modifier. The escape check ran at that point, so it rejected the binding even when the following tokens established that this was a value import.

Move the escape check into the two branches that interpret `type` as a modifier: before a named or namespace import, and before another binding identifier after ruling out `import type from "m"`. The latter check also runs before the early return for a type-only import-equals declaration.

Escaped modifiers remain invalid, including these forms:

```ts
import t\u0079pe Foo = require("m");
import t\u0079pe { x } from "m";
import t\u0079pe * as ns from "m";
import { t\u0079pe Bar } from "m";
```

The change is limited to when the declaration-level escape diagnostic is emitted; import classification and specifier parsing retain their existing behavior.
2026-09-07 16:03:31 +00:00
camc314 72cb5e3539 fix(parser): reject rest parameters in getters (#26400)
## Summary

Getter accessors must have no formal parameters, but Oxc currently accepts a getter whose only parameter is a rest parameter. This affects class and object getters, as well as getter signatures in TypeScript interfaces and type literals.

```ts
class C { get x(...args) {} }
({ get x(...args) {} });
interface I { get x(...args: any[]): string }
type T = { get x(...args: any[]): string };
```

Each declaration above previously parsed without a diagnostic. It now reports the existing error: `A 'get' accessor must not have any formal parameters.`

## Cause and correction

`FormalParameters` stores ordinary parameters in `items` and the rest parameter separately in `rest`. Both getter checks inspected only whether `items` was empty, so a rest-only parameter list looked like a zero-parameter list.

Use `parameters_count() != 0` in both checks so the count includes the rest parameter. The shared `check_getter` path covers class and object getters; `parse_getter_setter_signature_member` covers interface and type-literal getter signatures.

The diagnostic still points at the full parameter list. Zero-parameter getters remain valid, ordinary getter parameters retain their existing diagnostic, and class/object accessors with type parameters retain the current type-parameter diagnostic precedence. Setter validation is unchanged.
2026-09-07 15:34:10 +00:00
camc314 4e76602fdd fix(parser): allow in in arrow block bodies within for initializers (#26395)
## Summary

Allow the `in` operator inside an arrow function’s block body when the arrow appears in a `for` initializer:

```js
for (() => { a in b; };;);
```

This is valid JavaScript, but the parser previously reported a missing semicolon at `in`. The block now parses successfully, including async arrows and arrows with a single unparenthesized parameter.

## Cause and fix

A `for` initializer disables `Context::In` so that an unparenthesized `in` can delimit a `for…in` statement. Arrow block bodies inherited that disabled context through `parse_arrow_function_expression_body`, which called `parse_function_body` directly.

The block-body branch now calls `parse_function_body` through `context_add(Context::In, ...)`. This enables `in` throughout the function body and restores the enclosing context afterward. It also permits nested concise arrows inside that block to use `in`.

The change is confined to block bodies because concise expression bodies inherit the surrounding grammar’s `In` parameter. For example, this remains invalid:

```js
for (var f = () => a in b;;);
```

Restoring the enclosing context also keeps this invalid, rather than accidentally treating `a in b` as a regular initializer expression after parsing the first arrow:

```js
for (var f = () => {}, value = a in b;;);
```
2026-09-07 15:07:31 +00:00
camc314 b20fc1950c fix(parser): reject partially parenthesized mixed coalesce expressions (#26394)
The parser currently accepts mixed nullish and logical expressions when parentheses cover only the first operand of the logical expression:

```js
(a) || b ?? c;
a ?? (b) || c;
(a) && b ?? c;
a ?? (b) && c;
```

These expressions require a syntax error because `??` cannot be mixed directly with `||` or `&&`. Parentheses around `a` or `b` do not group the complete logical expression. This change reports the existing mixed-coalesce diagnostic for all four forms.

Fully grouped operands remain valid:

```js
(a || b) ?? c;
a ?? (b || c);
(a && b) ?? c;
a ?? (b && c);
```

### Why the existing check misses these expressions

`parse_binary_expression_rest` used `lhs_parenthesized` and `rhs_parenthesized` flags captured before parsing an operand. Those flags describe whether the initial token is `(`, but the parser can subsequently accumulate a larger logical expression outside those parentheses. Applying the initial flag to that complete expression incorrectly suppresses the diagnostic.

### Complete-operand checks

Check both operands independently for an `And` or `Or` logical expression whose span starts at the operand's starting position. A logical expression accumulated outside initial parentheses starts at that position and must be rejected. Checking both operands also prevents a grouped right operand from hiding an invalid left operand.

This works with either `preserve_parens` setting. When parentheses are preserved, a fully grouped operand is a `ParenthesizedExpression`. When they are omitted, its inner logical expression retains a span beginning after the opening `(`, so its start differs from the operand start and it remains allowed. For `(a) || b`, the complete logical expression instead starts at the opening `(` and is correctly rejected.
2026-09-07 14:34:52 +00:00
camc314 1400a0f8be fix(parser): require a string source after export ... from (#26389)
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.
2026-09-07 12:41:32 +00:00
camc314 8b35abdfd9 fix(semantic): detect duplicate private class elements (#26361)
The duplicate class element check stored only the last element for each name. This allowed a public element to hide an earlier private declaration, and a setter to hide an earlier getter (or vice versa):

```js
class DuplicateField {
  #x;
  x;
  #x;
}

class DuplicateGetter {
  get #x() {}
  set #x(value) {}
  get #x() {}
}
```

Both classes were accepted despite declaring the same private field or getter twice. The same problem affected setter/getter/setter sequences.

Key a single map by both the element name and whether it is private, so public elements cannot replace private declarations. This keeps the namespaces separate while reserving map capacity only once. For each private name, retain separate slots for setters and other declarations. Look for a previous declaration in the matching slot first, then fall back to the other slot. This catches repeated getters and setters while still allowing one getter/setter pair when both have the same static status.

The existing diagnostic logic continues to reject private names shared by static and instance elements, including the TypeScript-specific diagnostic. Public elements continue through the existing public-element duplicate check.
2026-09-06 21:17:34 +00:00
camc314 457ed57ff2 fix(semantic): reject await and yield in rest parameter defaults (#26359)
The parameter check only recognized `FormalParameter`, allowing `await` and `yield` in destructuring defaults inside rest parameters:

```js
async function f(...[x = await 0]) {}
function* g(...[x = yield 0]) {}
```

Include `FormalParameterRest` so both cases report the existing parameter diagnostic. The check still stops at nested functions, where `await` and `yield` can be valid.
2026-09-06 08:36:56 +00:00
camc314 4b89c7bd55 fix(semantic): reject jumps across arrow functions (#26357)
Reject labeled and unlabeled `break` and `continue` statements that cross an arrow-function boundary.

For example:
```ts
while (true) {
  (() => {
    break;
  });
}
```

now reports an illegal break, while jumps targeting loops inside the arrow remain valid.
2026-09-06 07:44:47 +00:00
camc314 4adfb4c949 fix(semantic): validate chained continue labels (#26157)
## Summary

- follow chained labels to their terminal statement when validating labeled `continue`
- reject label chains that terminate in a non-iteration statement
- add a focused negative coverage fixture and update the parser coverage snapshot
2026-08-28 18:45:13 +00:00
Boshen fca2e0cfed fix(parser): reject initialized lexical declarations in for-in (#25700)
Rejects initialized `let` and `const` declarations in `for...in` during parsing while preserving Annex B's non-strict `var` form.

Adds misc coverage for valid and invalid initializer forms.

Verified with `just ready` and parser conformance.

Closes #25149.

AI-assisted.
2026-08-15 06:58:08 +00:00
Boshen ae9be8f8bd fix(parser): forbid type parameters on quoted constructors (#25696)
Quoted constructor methods bypassed the AST-based constructor validation. Validate type parameters after resolving the method key, preserving the AST shape.

Validation: `just ready`, parser conformance, and allocation checks.

Closes #25692.

AI-assisted with Codex.
2026-08-15 05:02:23 +00:00
Boshen 23a7ad096a fix(parser): stop delimited lists at end of file (#25542)
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.
2026-08-12 13:58:51 +00:00
Boshen 73acba93fb fix(parser): preserve fatal errors during await reparse (#25541)
Fixes #23681.

Preserve initial fatal parser errors during top-level `await` reparsing, preventing malformed recovery AST nodes from reaching Oxlint.

Validated with parser unit/conformance suites and allocation snapshots. `just ready` only fails three unrelated ANSI snapshot checks in the current environment.

AI-assisted.
2026-08-12 11:25:27 +00:00
Cameron 12937b3201 fix(parser): reject comma expressions in class fields (#25385)
## Summary

- parse ordinary class field initializers as `AssignmentExpression`
- reject unparenthesized comma expressions while preserving
parenthesized sequence expressions
- add paired misc conformance fixtures and update the generated
snapshots

## Root cause

Ordinary class field initializers used `parse_expr`, which accepts a
top-level comma and constructs a sequence expression. The ECMAScript
`Initializer` grammar requires an `AssignmentExpression`, so the
unparenthesized comma must remain for the class-element terminator check
to reject.
2026-08-08 10:59:23 +00:00
camc314 9e93ba6908 fix(parser): reset auto-accessor initializer context (#25384)
## Summary

- Align auto-accessor initializer parsing with class-field initializer grammar.
- Add regressions for `in` in a `for` initializer and disallowed inherited `await`/`yield` contexts.

## Root cause

The auto-accessor path only enabled `new.target`. It therefore inherited outer `~In`, `Await`, and `Yield` grammar contexts: `a in b` could not parse in a `for` initializer, while `await` and `yield` could incorrectly parse from enclosing async/generator functions. The fix applies the same `In | NewTarget` addition and `Yield | Await` removal already used for class fields, while retaining AssignmentExpression parsing.

## References

- [ECMAScript `FieldDefinition` grammar](https://tc39.es/ecma262/#prod-FieldDefinition)
- [ECMAScript `Initializer` grammar](https://tc39.es/ecma262/#prod-Initializer)
- [TC39 decorators proposal: class auto-accessors](https://github.com/tc39/proposal-decorators#introduction)
2026-08-07 23:03:24 +00:00
camc314 5c5cdcd522 feat(ast)!: narrow TSInterfaceHeritage::expression to TSTypeName (#24360)
## 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
2026-08-07 06:00:42 +00:00
camc314 4d6f623cda feat(parser): emit error for type args in JS files (#24896)
## Summary

- Emit TS8011 when a JavaScript class heritage clause uses TypeScript type arguments.
- Cover the diagnostic with a misc parser failure fixture and snapshot.
2026-07-24 09:46:34 +00:00
camc314 47ddedf011 fix(semantic): allow arguments in ambient declarations (#24758)
## Summary

- allow `eval` and `arguments` as direct bindings in ambient declarations
- continue reporting strict-mode errors for destructured ambient bindings
- add focused coverage for declared functions, classes, object patterns, and array patterns

## Why

The semantic checker previously identified allowed bindings by walking to a `TSDeclareFunction`. That missed other ambient declarations, including declared class constructors and methods, and produced diagnostics for TypeScript cases that require no runtime code generation.

The check now uses the existing ambient-context state and rejects only destructuring binding shapes.
2026-07-21 12:43:21 +00:00
camc314 129b759131 feat(parser): improve diagnostics for unparenthesized LHS on exponential expr (#24569)
## 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.
2026-07-15 18:38:30 +00:00
camc314 7b4baff16c fix(parser): reject new import member access (#23459)
Reject `new import(...).prop`, `new import.source(...).prop`, and `new import.defer(...).prop` by treating member access on an unparenthesized import expression as invalid after `new`.

Fixes #23458
2026-07-15 10:38:34 +00:00
camc314 c517aa08a8 fix(parser): reject invalid accessor assertions (#24504)
## 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.
2026-07-14 10:16:51 +00:00
sapphi-red 38c4b0628b feat(parser): add friendly error for adjacent JSX elements (#23378)
Add a friendly error for adjancent JSX element case:

```
  × Adjacent JSX elements must be wrapped in an enclosing tag.
   ╭─[misc/fail/jsx-adjacent-elements.jsx:3:3]
 2 │   <div />
 3 │   <span />
   ·   ─
 4 │ );
   ╰────
  help: Did you want a JSX fragment `<>...</>`?
```

This error message exists in babel.
2026-06-16 14:50:53 +00:00
camc314 1d3af58c4a feat(parser): add TS2398 parameter property diagnostic (#23216)
## Summary

- Report TS2398 when a TypeScript constructor parameter property is named `constructor`.
- Add coverage for the upstream TypeScript fixture plus dedicated misc pass/fail cases for ordinary `constructor` parameters versus invalid parameter properties.

```
  × TS(2398): 'constructor' cannot be used as a parameter property name.
   ╭─[misc/fail/oxc-ts-2398.ts:2:31]
 1 │ class A {constructor(public constructor){}}
 2 │ class B {constructor(readonly constructor){}}
   ·                               ───────────
   ╰────
```
2026-06-11 08:39:25 +00:00
camc314 8edd23459d fix(parser): report accessor definite assertion on token (#23203)
## Summary
- Pass the definite-assignment assertion token offset through the class accessor-property parser path.
- Report invalid ambient and abstract `accessor prop!` diagnostics on the `!` token, matching the span behavior used by variable declarators and normal class properties.
- Add focused misc parser coverage for the accessor-specific invalid cases.

## Rationale
This follows up #23170. That parser change already narrowed definite-assignment diagnostics for variables and normal class properties, but accessor properties still kept only a boolean. That meant `accessor prop!` could only report TS1255 using the full class element span. Carrying the offset into `parse_class_accessor_property` aligns accessor diagnostics with the other definite-assignment diagnostics and keeps the AST shape unchanged by storing only `definite.is_some()`.

The misc fixture is needed because upstream TypeScript coverage exercises invalid definite assertions on normal fields, but not invalid accessor properties. Existing misc coverage only had a valid `accessor y!` pass case, so `just coverage` did not specifically protect the accessor diagnostic span this fixes.
2026-06-10 07:46:45 +00:00
Boshen cf53285466 fix(parser): report reserved type-declaration names in the parser (#23035)
## What

Moves the reserved built-in type name check (a type's name may not be `any`, `string`, `number`, …) out of the semantic checker into the parser, via a reusable `check_reserved_type_name` helper. Covers **all** declaration forms:

- class → TS2414
- interface → TS2427
- type alias → TS2457
- enum → TS2431
- type parameter → TS2368

The semantic `check_type_name_is_reserved` helper and its diagnostic are removed.

## Why it moves cleanly

- **Zero state:** each is a name-vs-reserved-list match at the parse site where the identifier is parsed.
- **Node-final:** these declarations are never reinterpreted by cover grammar.
- **Negative-only:** confirmed by `cargo coverage` — no positive/negative count changes; only the errors relocating in `parser_typescript.snap`.

## Class needs an `is_ts` guard + a fixture fix

`class string {}` is valid JavaScript (these names are reserved only as *type* names), so the class check is guarded by `is_ts`. The interface/type-alias/enum/type-parameter forms are TypeScript-only parser paths, so they need no guard.

The `no-shadow-restricted-names` rule had `class undefined {}` / `(class undefined {})` fixtures. Since the lint test harness parses fixtures as TSX, those are now parse errors (TS2414). They're dropped — class-name shadowing is still covered by `class globalThis {}` (globalThis isn't a reserved type name), and `undefined` shadowing by the `var`/`import` fixtures.

## Verification

- `class number {}`, `interface any {}`, `type any = number`, `enum string {}`, `function f<any>() {}` → error; `class string {}` in `.js` → clean; valid names → clean.
- Parser/semantic/linter (1155) tests pass; clippy clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-07 02:05:14 +00:00
Boshen 17e7cf3ce4 fix(parser): disallow unerasable as/satisfies assertions (#22986)
Port of [microsoft/typescript-go#4192](https://github.com/microsoft/typescript-go/pull/4192), which fixes [microsoft/TypeScript#63527](https://github.com/microsoft/TypeScript/issues/63527).

## Problem

`as`/`satisfies` are supposed to be erasable without changing runtime semantics. But for an expression of the form `a ## b as T $$ c`, where `##` has *lower* precedence than `$$`, erasing the assertion silently changes the parse:

```ts
const x = 1 + 1 as number * 2;
```

This parses as `((1 + 1) as number) * 2` (= `4`), but textually erasing `as number` yields `1 + 1 * 2` (= `3`). TypeScript now rejects such constructs as a syntax error, forcing explicit parentheses.

## Change

In `parse_binary_expression_rest`, after parsing an `as`/`satisfies` type, stop parsing if the following operator binds tighter than the operator that produced the running left-hand operand. This leaves the higher-precedence operator dangling and surfaces as a parse error.

A chained `a as T as U` keeps referring back to the original operator's precedence (the tracked precedence is intentionally not updated while consuming `as`/`satisfies`), so `1 + 1 as any as number * 2` is rejected too.

## Behavior

| Expression | Result |
| --- | --- |
| `1 as number * 2` | ok |
| `1 * 1 as number + 2` | ok (`+` does not bind tighter than `*`) |
| `1 + 1 as number * 2` | **error** |
| `1 + 1 as any as number * 2` | **error** |
| `1 >> 1 as number + 2` | **error** |
| `(1 + 1 as number) * 2` | ok |

`satisfies` behaves identically to `as`.

## Tests

Added `misc` coverage fixtures mirroring the upstream test matrix: `pass/ts-erasable-as-satisfies.ts` for the erasable cases and `fail/ts-unerasable-{as,as-chained,satisfies}.ts` for the rejected ones. Full conformance (test262 / babel / typescript) shows zero regressions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-05 13:10:42 +00:00
Boshen beb46d31fa fix(parser): commit to module goal on decorated exports (#22941)
## Summary

In unambiguous (`.js`) sources, `@foo export default class C { x = await + 1 }` reported a spurious `A module cannot have multiple default exports`.

A leading decorator makes the statement start with `@`, not `export`, so the eager Module-goal switch from #22684 didn't fire. The `await` identifier in the field initializer then triggered the unambiguous-mode top-level-await reparse, which re-ran `parse_statement_list_item` and recorded the `export default` a second time.

## Fix

- Commit to the Module goal in `parse_export_declaration` — the single place both leading and decorated exports flow through — so the export body parses under `Await` on the first pass, matching a non-decorated leading `export` (`ExportDeclaration` is unambiguously a `ModuleItem`, ECMA-262 §16.2.3).
- Skip the reparse for module declarations (`!stmt.is_module_declaration()`): an `export` / `import` is recorded on the first pass and is goal-independent, so reparsing only double-records it.

This also simplifies `parse_directives_and_statements`: the mutable `track_await_reparse` flag and the inline eager-commit block are removed; committing is now driven by `!ctx.has_await()`.

`await` stays correctly reserved in the module, so the input is still a syntax error (`The keyword 'await' is reserved`) — just no longer a *spurious* duplicate-export one. `tasks/coverage/misc/fail/oxc-22684.js` covers this.

Conformance: zero snapshot drift outside the new `misc` fixture.

Follow-up to #22684.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-04 04:05:46 +00:00
sapphi-red 661132d5ac feat(parser): more friendly error messages for rest assignment target and rest binding element (#22719)
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`.
```
2026-05-25 12:47:34 +00:00
camc314 e184f74d11 fix(parser): improve invalid import property access diagnostic (#22693)
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.
2026-05-25 09:58:50 +00:00
Boshen e431a0eb52 fix(parser): break extends clause loop on fatal error (#22517)
## Summary

`parse_extends_clause` kept iterating after `parse_lhs_expression_or_higher` set a fatal error, producing a `TSInterfaceHeritage` with an inverted span (`start > end`). `parse_class` then passed that span to `classes_can_only_extend_single_class`, which panicked in `miette` when converting the span to a label (`debug_assert!(self.start <= self.end)` in `Span::size`). Release builds silently produced garbage diagnostics.

Repro from the issue:

```js
class extends,{
```

Closes #22515

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-05-18 05:50:19 +00:00
camc314 ffacf99cc1 fix(semantic): report error on await inside block inside static block (#20750) 2026-03-26 14:17:31 +00:00
camc314 ccf9301b93 fix(semantic): report error on for-await inside block inside static block (#20749) 2026-03-26 14:00:25 +00:00
Boshen c205b0df51 fix(ast): remove ThisExpression from TSModuleReference (#18489)
## Summary

- Removes `ThisExpression` variant from `TSModuleReference` since `import x = this` is invalid TypeScript (error TS1359)
- Adds parser error when `this` is used in import equals declaration
- Updates codegen, transformer, and linter rules to handle the new enum structure

Closes #16841

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-24 15:25:10 +00:00
Boshen aed3669a02 fix(parser): parse HTML-like comments in unambiguous mode (#18442)
## Summary

Closes #18392

Support HTML-like comments (`<!--` and `-->`) per ECMAScript Annex B.1.1:

- **Script/CommonJS mode**: HTML comments recognized as single-line comments
- **Unambiguous mode (.js)**: HTML comments recognized; error deferred until ESM syntax detected
- **Module mode (.mjs)**: HTML comments at start of line emit "HTML comments are not allowed in modules" error; `<!--` in middle of expression (e.g. `foo <!--bar`) parses as operators

This enables proper detection of HTML comment injection vectors in security tools like [AikidoSec/zen-internals](https://github.com/AikidoSec/zen-internals/pull/125).

🤖 Generated with [Claude Code](https://claude.ai/code)
2026-01-23 11:35:33 +00:00
overlookmotel 22b4d39bd5 test(semantic): add test cases for function redeclaration check (#18288)
Follow-on after #18231. Add test cases to make sure function redeclaration check works correctly in script, module, and CommonJS files.
2026-01-20 14:09:09 +00:00
Boshen 92e27b4e01 feat(parser): reparse await expressions in unambiguous mode when ESM detected (#18139)
## Summary

- Implements TypeScript-compatible await expression reparsing for unambiguous mode
- When `sourceType` is `unambiguous`, `await / 0 /u; export {}` now correctly parses as `await /0/u` (await expression with regex)
- Follows TypeScript's two-pass approach: initially parse `await` as identifier, then reparse with await context if ESM is detected

## Test plan

- Added `tasks/coverage/misc/pass/unambiguous-await-regex.js` test case
- Moved `tasks/coverage/misc/fail/babel-16776-s.js` to pass (now parses as division in script mode)
- All parser tests pass
- Conformance tests show improvements in parser_babel and semantic_babel

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-17 15:27:42 +00:00
Boshen 66b8c022ab feat(parser): implement unambiguous module parsing for JS/TS files (#18124)
## Summary

- Implements speculative/unambiguous parsing for `.js`, `.jsx`, `.ts`, and `.tsx` files
- Instead of assuming module or script upfront, parses with `ModuleKind::Unambiguous` and upgrades to module mode when ESM syntax is detected
- ESM indicators: `import`, `export`, `import.meta` (NOT top-level await alone)
- Defers top-level await errors until module type is resolved
- Vue loader upgrades unambiguous scripts to module mode
- Updates linter tests to use explicit ESM extensions (`.mjs`/`.mts`) where needed

## Test plan

- [x] All parser tests pass
- [x] All linter tests pass
- [x] All semantic tests pass
- [x] Coverage conformance tests updated

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-17 13:40:28 +00:00
Boshen fb181ccdff fix(semantic): error on using at top level in scripts (#18090)
## Summary

- Add check to error on `using` declarations at the top level of scripts
- `using` declarations require block scope semantics and are not allowed
at the top level of scripts

`using` declarations are allowed:
- At the top level of ES modules
- At the top level of CommonJS modules (files are wrapped in a function)
- Inside any block scope in scripts

But NOT at the top level of scripts.

Closes #11862

## Test plan

- [x] Added test case
`tasks/coverage/misc/fail/script-top-level-using.js` for invalid usage
- [x] Added test case
`tasks/coverage/misc/pass/script-using-in-block.js` for valid usage in
block
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 12:18:26 +08:00
Boshen 08dad63678 feat(span): add sourceType: 'commonjs' support (#18089)
## Summary

- Add `ModuleKind::CommonJS` variant to support CommonJS module type in the parser
- Enable top-level `return` statements (CommonJS files are wrapped in a function)
- Enable top-level `new.target` (allowed in CommonJS function wrapper)
- Add proper errors for `import`/`export` statements in CommonJS
- Add proper errors for `import.meta` in CommonJS

The CommonJS source type can be set via:
- `SourceType::with_commonjs(true)` method
- Babel's `sourceType: "commonjs"` option

Closes #16200

## Test plan

- [x] Added test cases in `tasks/coverage/misc/pass/` for valid CommonJS features
- [x] Added test cases in `tasks/coverage/misc/fail/` for invalid CommonJS usage
- [x] Enabled Babel conformance tests for `sourceType: "commonjs"`
- [x] All existing tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-01-16 16:08:27 +00:00
Copilot 59a6228ef9 fix(parser): detect TS1363 error for type-only imports with mixed default and named/namespace bindings (#17712)
The parser was not detecting `import type foo, {} from 'bar';` and `import type A, * as NS from 'mod';` as errors. TypeScript's TS1363 requires that type-only imports specify either a default import or named/namespace bindings, but not both—even when the named bindings list is empty.

## Changes

- **Parser**: Added TS1363 check in `parse_import_declaration_specifiers` when encountering `LCurly` or
`Star` token after a type-only default import
- **Module record**: Removed redundant TS1363 validation that couldn't detect empty `{}` cases
- **Error span**: Now points at the default specifier instead of the entire import statement

## Examples

The following patterns now correctly error with TS1363:

```typescript
// Empty named bindings
import type foo, {} from 'bar';

// Named bindings with specifiers
import type foo, { bar } from 'baz';

// Namespace import
import type A, * as NS from 'mod';
```

The parser-level check is comprehensive and catches all cases including
empty braces and namespace imports, which weren't being detected
previously.


- Fixes oxc-project/oxc#17684

---------

Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: camc314 <18101008+camc314@users.noreply.github.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
2026-01-06 14:45:14 +00:00
camc314 d99134d70c feat(parser): improve diagnostic message for phase imports (#17092)
We now produce the same error codes as typescript
2025-12-21 08:14:16 +00:00
sapphi-red cb515fac28 feat(parser): improve error message for yield as identifier usage (#16950)
Improved error message for `yield` as identifier usage.

**Before**
```
  × Cannot use `yield` as an identifier in a generator context
    ╭─[test262/test/language/expressions/generators/yield-weak-binding.js:17:33]
 16 │
 17 │ var g = function*() { yield 3 + yield 4; };
    ·                                 ─────
    ╰────
```
**After**
```
  × Cannot use `yield` as an identifier in a generator context
    ╭─[test262/test/language/expressions/generators/yield-weak-binding.js:17:33]
 16 │
 17 │ var g = function*() { yield 3 + yield 4; };
    ·                                 ─────
    ╰────
  help: Wrap this in parentheses if you want to use a `yield` expression here
```
2025-12-16 11:30:44 +00:00
overlookmotel 75f9800166 test(parser): combine test cases for let as identifier (#16861)
Follow-on after #16840 and #16857. We had a few test cases relating to the same thing, and one of them called the undescriptive `oxc.js`. Combine them, and rename the test case files with a more accurate name.
2025-12-14 21:30:55 +00:00
Copilot d3208dc969 fix(parser): parse let?.x (#16840)
Fixes #16839.

Make parser parse `let?.x` successfully.

There's a failure in codegen idempotency tests, but that's an unrelated
and pre-existing bug, so am leaving that to another PR.

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
2025-12-14 19:26:57 +00:00
sapphi-red da87812ac7 feat(semantic): add TS2309 error for export assignment with other exports (#15992)
Added TS2309 error check.
2025-12-06 08:30:06 +00:00
copilot-swe-agent 74cf5720bc feat(ast)!: Make source field of TSImportType a StringLiteral (#16114)
- [x] Change `source` field type in `TSImportType` from `TSType<'a>` to `StringLiteral<'a>` in `crates/oxc_ast/src/ast/ts.rs`
- [x] Remove `#[estree(via = TSImportTypeSource)]` attribute from the field
- [x] Run `just ast` to regenerate all derived code
- [x] Update the parser in `crates/oxc_parser/src/ts/types.rs` to parse a `StringLiteral` directly and emit errors for non-string literals
- [x] Delete the dead code `TSImportTypeSource` serializer from `crates/oxc_ast/src/serialize/ts.rs`
- [x] Add `ts_string_literal_expected` diagnostic (TS1141)
- [x] Update semantic snapshot for `import-type-with-type-params.snap`
- [x] Add test case for invalid import type source
- [x] Verify all tests pass
- [x] Simplify error recovery - just report error and create dummy string literal (per review feedback)
- [x] Fix spurious "Expected ')'" error by skipping invalid tokens in error recovery

<!-- START COPILOT CODING AGENT SUFFIX -->

<details>

<summary>Original prompt</summary>

>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>AST: Make `source` field of `TSImportType` a `StringLiteral`</issue_title>
> <issue_description>Continuation of oxc-project/oxc#16074.
>
> The expression inside `import(...)` in TS context (`TSImportType`) cannot legally be anything other than a string literal.
>
> Our parser is too liberal. We currently accept all kind of crazy stuff like:
>
> ```ts
> type A = typeof import(`react`);
> type B = typeof import(A);
> type C = typeof import(`${A} ${B}`);
> type D = typeof import(typeof import('react'));
> ```
>
> [Playground](https://playground.oxc.rs/#eNptU9tOGzEQ/ZXIQqJIqUSRKqFUfQjQSJVooaUqL31g1jsbDF7bHc/molX+nfEmdkDhyfaZ+5njXmk1UbwOOJqOvo7SxTcj0wZP/OGBEDQ/nHz55waPiwOPabFdHkYf9dPN6Ki/2OwzXB14vX0dDxWPT1KAGiuvJr2izqXDGsdq0oCNOFaNpxaYkQrCBC4muCAmeguM9RVqCwRsvIvFuHw0jDGAxgK14OZ2/9S+DYRxHxK1D2Jm6tJj3Vbe5pdu5ju3zVgFoJga6xWuGF2UuoniKPOAtX75G7kjd9NxNDXOOqdTZzlTKom0wFsgCc3oEPf3/LtjMpJQF0NE6ZqN/kbkaYdKC4mroQW5v6KqV13EP1DtZ2Ko7k3Nj2pyNlbo6pvm2jgZUtlG2g1SjXf281OpZhJDvzrPe5qe4uruHfh/et2SD1JLQfzoEGusJafsyUh780vftpBs1goqc5gyrMyyzPOLfQnrxF1FoJ+R72RnEp6dMwotbjvf1ffVE2q+JwiSI5OaCg29TlmIrDrpEOlNGK4CkmnRMdgbuQJ7uvXRbFeUGFJCadHallQGmqNoU2E8O/30WaoIzVfYSOKZp0sLMc4M2rps7XUVUacfyuytreGC/kCGGliY2q32fVX3KspEQQSC5EB0mcWofY1zHD6QS0Iokn2KtZevv6PcOc9DroxYnGffIUv+C0lR+aPI5D5c4wJL0mfE8FNWkQU2hIpovZ2JgFPEAqnyMROe5nFpUclUbhuB64G+BJfbJuGJZoisNi/WFa5W)
>
> TypeScript says these are all errors: [TS Playground](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAglC8VSQPYDMoEsC2YUCdgAKAA3wgEMBjYEgSgG4AoZaAIQSXAnS1wOIxGLblADCnVrxx5CpACQBvGAF8oStivrNWUACKTu0-nKkYZAogHJy1YFbrCgA)
>
> ### AST change
>
> We should alter the type of the `source` field from `TSType<'a>` to `StringLiteral<'a>`:
>
> https://github.com/oxc-project/oxc/blob/35d0ee491cbcd271ebceaac7e25cff0490ded5c4/crates/oxc_ast/src/ast/ts.rs#L1382-L1389
>
> ```diff
> pub struct TSImportType<'a> {
>     pub span: Span,
> -   #[estree(via = TSImportTypeSource)]
> -   pub source: TSType<'a>,
> +   pub source: StringLiteral<'a>,
>     pub options: Option<Box<'a, ObjectExpression<'a>>>,
>     pub qualifier: Option<TSImportTypeQualifier<'a>>,
>     pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
> }
> ```
>
> ### Parser changes
>
> If we change the AST first and run `just ast` to regenerate traits code, then all the places which rely on the type of `source` field will become apparent. We'll then need to alter the parser to throw an error if it encounters something that isn't a string literal.
>
> ### `ESTree` serialization
>
> Currently there's a custom deserializer to alter the AST on JS side from `TSType` to `StringLiteral`:
>
> https://github.com/oxc-project/oxc/blob/91eb3f2be509cef11cffeff51fac61a3a4fbd12c/crates/oxc_ast/src/serialize/ts.rs#L506-L538
>
> Once we've changed the field type in AST, this code can be deleted - it's dead code once `#[estree(via = TSImportTypeSource)]` attribute is removed from the `source` field in AST struct.</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>

</details>

- Fixes oxc-project/oxc#16111

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
2025-11-25 18:51:16 +00:00
camc314 1199cee3ae fix(parser): reject invalid modifiers on parameter properties with binding patterns (#16083)
Fixes #16077
2025-11-25 09:43:09 +00:00
sapphi-red d60ca818e7 fix(parser): reject import something 'source' (#15746)
```js
import something 'source'
```
was not rejected but this should be rejected.
https://tc39.es/ecma262/2025/multipage/ecmascript-language-scripts-and-modules.html#prod-ImportDeclaration
2025-11-17 02:58:51 +00:00
sapphi-red 9f54a36de3 fix(semantic): error on \00 in strict mode (#15743)
`\00` matches LegacyOctalEscapeSequence and should be an error in strict mode.
https://tc39.es/ecma262/2025/multipage/ecmascript-language-lexical-grammar.html#sec-string-literals-early-errors
2025-11-16 09:25:13 +00:00