fix(parser): span ambient generator diagnostics (#24711)

## 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.
This commit is contained in:
camc314 committed 2026-07-20 09:44:55 +00:00
1 parent e750a8243a
commit 48b59f450d
6 files changed
+47 -43

No files matched your search

+5 -5
View File
@@ -471,7 +471,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let (name, computed) = self.parse_class_element_name(modifiers);
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
false,
None,
FunctionKind::ClassMethod,
);
let method_definition = MethodDefinition::boxed(
@@ -512,7 +512,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let value = self.parse_method(
modifiers.contains(ModifierKind::Async),
false,
None,
FunctionKind::Constructor,
);
let method_definition = MethodDefinition::boxed(
@@ -555,7 +555,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
modifiers: &Modifiers,
decorators: ArenaVec<'a, Decorator<'a>>,
) -> ClassElement<'a> {
let generator = self.eat(Kind::Star);
let generator = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let (name, computed) = self.parse_class_element_name(modifiers);
let cur_token = self.cur_token();
@@ -567,7 +567,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let optional = optional_span.is_some();
if generator || matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) {
if generator.is_some() || matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) {
self.verify_modifiers(
modifiers,
ModifierKinds::all_except([ModifierKind::Declare, ModifierKind::Readonly]),
@@ -636,7 +636,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
&mut self,
span: u32,
r#type: MethodDefinitionType,
generator: bool,
generator: Option<u32>,
name: PropertyKey<'a>,
computed: bool,
optional: bool,
+17 -13
View File
@@ -257,7 +257,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
span: u32,
id: Option<BindingIdentifier<'a>>,
r#async: bool,
generator: bool,
generator: Option<u32>,
func_kind: FunctionKind,
param_kind: FormalParameterKind,
modifiers: &Modifiers,
@@ -265,8 +265,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let ctx = self.ctx;
// `new.target` is allowed in a function's parameters and body (but not arrow
// functions, which are parsed via `parse_function_body` directly).
self.ctx =
self.ctx.and_in(true).and_await(r#async).and_yield(generator).and_new_target(true);
self.ctx = self
.ctx
.and_in(true)
.and_await(r#async)
.and_yield(generator.is_some())
.and_new_target(true);
let type_parameters = self.parse_ts_type_parameters();
let (this_param, params) = self.parse_formal_parameters(func_kind, param_kind);
let return_type = if self.is_ts { self.parse_ts_return_type_annotation() } else { None };
@@ -328,9 +332,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
self.error(diagnostics::implementation_in_ambient(Span::empty(body.span.start)));
}
if generator {
if let Some(generator) = generator {
if ctx.has_ambient() {
self.error(diagnostics::generator_in_ambient_context(self.end_span(span)));
self.error(diagnostics::generator_in_ambient_context(self.end_span(generator)));
} else if body.is_none() {
self.error(diagnostics::overload_signature_generator(self.end_span(span)));
}
@@ -346,7 +350,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
self.end_span(span),
function_type,
id,
generator,
generator.is_some(),
r#async,
modifiers.contains_declare(),
type_parameters,
@@ -392,8 +396,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
func_kind: FunctionKind,
) -> ArenaBox<'a, Function<'a>> {
self.expect(Kind::Function);
let generator = self.eat(Kind::Star);
let id = self.parse_function_id(func_kind, r#async, generator);
let generator = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let id = self.parse_function_id(func_kind, r#async, generator.is_some());
self.parse_function(
span,
id,
@@ -415,8 +419,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
) -> ArenaBox<'a, Function<'a>> {
let r#async = modifiers.contains(ModifierKind::Async);
self.expect(Kind::Function);
let generator = self.eat(Kind::Star);
let id = self.parse_function_id(func_kind, r#async, generator);
let generator = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let id = self.parse_function_id(func_kind, r#async, generator.is_some());
self.parse_function(
start_span,
id,
@@ -433,8 +437,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let func_kind = FunctionKind::Expression;
self.expect(Kind::Function);
let generator = self.eat(Kind::Star);
let id = self.parse_function_id(func_kind, r#async, generator);
let generator = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let id = self.parse_function_id(func_kind, r#async, generator.is_some());
let function = self.parse_function(
span,
id,
@@ -458,7 +462,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
pub(crate) fn parse_method(
&mut self,
r#async: bool,
generator: bool,
generator: Option<u32>,
func_kind: FunctionKind,
) -> ArenaBox<'a, Function<'a>> {
let span = self.start_span();
+3 -3
View File
@@ -59,12 +59,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
return self.parse_method_getter_setter(span, PropertyKind::Set, &modifiers);
}
let asterisk_token = self.eat(Kind::Star);
let asterisk_token = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let token_is_identifier =
self.cur_kind().is_identifier_reference(self.ctx.has_yield(), self.ctx.has_await());
let (key, computed) = self.parse_property_name();
if asterisk_token || matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) {
if asterisk_token.is_some() || matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) {
self.verify_modifiers(
&modifiers,
ModifierKinds::new([ModifierKind::Async]),
@@ -216,7 +216,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
modifiers: &Modifiers,
) -> ArenaBox<'a, ObjectProperty<'a>> {
let (key, computed) = self.parse_property_name();
let function = self.parse_method(false, false, FunctionKind::ObjectMethod);
let function = self.parse_method(false, None, FunctionKind::ObjectMethod);
match kind {
PropertyKind::Get => self.check_getter(&function),
PropertyKind::Set => self.check_setter(&function),
+2 -2
View File
@@ -691,9 +691,9 @@ impl<'a, C: Config> ParserImpl<'a, C> {
) -> ArenaBox<'a, Function<'a>> {
let r#async = modifiers.contains(ModifierKind::Async);
self.expect(Kind::Function);
let generator = self.eat(Kind::Star);
let generator = self.eat(Kind::Star).then_some(self.prev_token_end - 1);
let func_kind = FunctionKind::TSDeclaration;
let id = self.parse_function_id(func_kind, r#async, generator);
let id = self.parse_function_id(func_kind, r#async, generator.is_some());
self.parse_function(
start_span,
id,
+6 -6
View File
@@ -12820,10 +12820,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
help: Allowed modifiers are: private, protected, public, static, abstract, override, async
× TS(1221): Generators are not allowed in an ambient context.
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/generator-method-with-modifiers/input.ts:7:13]
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/generator-method-with-modifiers/input.ts:7:11]
6 │ readonly *e() {}
7 │ declare *f() {}
· ─────
· ───────
8 │ protected *g() {}
╰────
@@ -13189,10 +13189,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
help: Allowed modifiers are: private, protected, public, static, abstract, override, async
× TS(1221): Generators are not allowed in an ambient context.
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/optional-generator-method-with-invalid-modifiers/input.ts:4:14]
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/optional-generator-method-with-invalid-modifiers/input.ts:4:11]
3 │ readonly *e?() { }
4 │ declare *f?() { }
· ──────
· ─────────
5 │ }
╰────
@@ -13231,10 +13231,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
help: Allowed modifiers are: private, protected, public, static, abstract, override, async
× TS(1221): Generators are not allowed in an ambient context.
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/optional-generator-method-with-invalid-modifiers/input.ts:10:19]
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/optional-generator-method-with-invalid-modifiers/input.ts:10:11]
9 │ readonly *[e?.e]?() { }
10 │ declare *[f?.f]?() { }
· ──────
· ──────────────
11 │ }
╰────
+14 -14
View File
@@ -20225,34 +20225,34 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext1.ts:2:15]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext1.ts:2:5]
1 │ declare class C {
2 │ *generator(): any;
· ────────
· ──────────────────
3 │ }
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext2.ts:2:5]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext2.ts:2:14]
1 │ declare namespace M {
2 │ function *generator(): any;
· ───────────────────────────
· ──────────────────
3 │ }
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext3.d.ts:2:15]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext3.d.ts:2:5]
1 │ declare class C {
2 │ *generator(): any;
· ────────
· ──────────────────
3 │ }
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext4.d.ts:2:5]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorInAmbientContext4.d.ts:2:14]
1 │ declare namespace M {
2 │ function *generator(): any;
· ───────────────────────────
· ──────────────────
3 │ }
╰────
@@ -20273,26 +20273,26 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:2:5]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:2:13]
1 │ declare namespace M {
2 │ function* f(s: string): Iterable<any>;
· ──────────────────────────────────────
· ──────────────────────────────
3 │ function* f(s: number): Iterable<any>;
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:3:5]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:3:13]
2 │ function* f(s: string): Iterable<any>;
3 │ function* f(s: number): Iterable<any>;
· ──────────────────────────────────────
· ──────────────────────────────
4 │ function* f(s: any): Iterable<any>;
╰────
× TS(1221): Generators are not allowed in an ambient context.
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:4:5]
╭─[typescript/tests/cases/conformance/es6/yieldExpressions/generatorOverloads2.ts:4:13]
3 │ function* f(s: number): Iterable<any>;
4 │ function* f(s: any): Iterable<any>;
· ───────────────────────────────────
· ───────────────────────────
5 │ }
╰────