Skip to content

Commit 2743ebe

Browse files
committed
fix(parser): reject function implementations in ambient contexts (TS1183)
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. Two linter rule fixtures relied on the old leniency and are updated to valid TS: `no_invalid_void_type` moves a function implementation out of a `declare module` into a non-ambient `module`, and `check_tag_names` replaces a `.d.ts` function body with a `declare namespace` scope. parser_babel +3 and parser_typescript +1 negative-pass; positive/AST stay 100%.
1 parent c645615 commit 2743ebe

5 files changed

Lines changed: 53 additions & 14 deletions

File tree

crates/oxc_linter/src/rules/jsdoc/check_tag_names.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,9 +1182,9 @@ fn test() {
11821182
),
11831183
(
11841184
"
1185-
function test() {
1185+
declare namespace test {
11861186
/** @abstract */
1187-
declare let a;
1187+
let a;
11881188
}
11891189
",
11901190
Some(serde_json::json!([

crates/oxc_linter/src/rules/typescript/no_invalid_void_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ fn test() {
693693
),
694694
(
695695
"
696-
declare module foo {
696+
module foo {
697697
function f(): void;
698698
function f(x: string): string;
699699
function f(x?: string): string | void {

crates/oxc_parser/src/js/function.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,18 @@ impl<'a, C: Config> ParserImpl<'a, C> {
286286
self.asi();
287287
}
288288

289+
// A function declaration's implementation (body) cannot be declared in an ambient context,
290+
// whether the ambient context comes from the function's own `declare` modifier or is
291+
// inherited from an enclosing `declare module`/`declare namespace` or a `.d.ts` file
292+
// (TS1183). Class methods are checked separately in `check_method_definition`, so they are
293+
// excluded here to avoid a duplicate diagnostic.
289294
if ctx.has_ambient()
290-
&& modifiers.contains_declare()
295+
&& matches!(
296+
func_kind,
297+
FunctionKind::Declaration
298+
| FunctionKind::DefaultExport
299+
| FunctionKind::TSDeclaration
300+
)
291301
&& let Some(body) = &body
292302
{
293303
self.error(diagnostics::implementation_in_ambient(Span::empty(body.span.start)));

tasks/coverage/snapshots/parser_babel.snap

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ commit: d9fe348c
33
parser_babel Summary:
44
AST Parsed : 2236/2236 (100.00%)
55
Positive Passed: 2222/2236 (99.37%)
6-
Negative Passed: 1702/1723 (98.78%)
6+
Negative Passed: 1705/1723 (98.96%)
77
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts
88

99
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts
@@ -14,14 +14,8 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty
1414

1515
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/conditional/arrow-param/input.ts
1616

17-
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/module-function/input.ts
18-
19-
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/namespace-function/input.ts
20-
2117
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/decorators/type-arguments-invalid/input.ts
2218

23-
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/dts/invalid-class-implementation/input.ts
24-
2519
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/equals-in-script/input.ts
2620

2721
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/export/invalid-as-namespace-duplicate-identifier/input.ts
@@ -13492,6 +13486,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1349213486
4 │ }
1349313487
╰────
1349413488

13489+
× TS(1183): An implementation cannot be declared in ambient contexts.
13490+
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/module-function/input.ts:2:18]
13491+
1 │ declare module "m" {
13492+
2 │ function foo() {}
13493+
· ▲
13494+
3 │ }
13495+
╰────
13496+
1349513497
× TS(1039): Initializers are not allowed in ambient contexts.
1349613498
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/namespace-class/input.ts:3:13]
1349713499
2 │ class C {
@@ -13500,6 +13502,20 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
1350013502
4 │ }
1350113503
╰────
1350213504

13505+
× TS(1183): An implementation cannot be declared in ambient contexts.
13506+
╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/namespace-function/input.ts:2:18]
13507+
1 │ declare namespace n {
13508+
2 │ function foo() {}
13509+
· ▲
13510+
3 │ }
13511+
╰────
13512+
13513+
× TS(1183): An implementation cannot be declared in ambient contexts.
13514+
╭─[babel/packages/babel-parser/test/fixtures/typescript/dts/invalid-class-implementation/input.ts:1:21]
13515+
1 │ function foo(): any {}
13516+
· ▲
13517+
╰────
13518+
1350313519
× TS(1039): Initializers are not allowed in ambient contexts.
1350413520
╭─[babel/packages/babel-parser/test/fixtures/typescript/dts/invalid-class-initializer/input.ts:2:9]
1350513521
1 │ class Foo {

tasks/coverage/snapshots/parser_typescript.snap

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ commit: e5509e21
33
parser_typescript Summary:
44
AST Parsed : 9779/9779 (100.00%)
55
Positive Passed: 9779/9779 (100.00%)
6-
Negative Passed: 1592/2640 (60.30%)
6+
Negative Passed: 1593/2640 (60.34%)
77
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration3.ts
88

99
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration4.ts
@@ -1782,8 +1782,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec
17821782

17831783
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration1.ts
17841784

1785-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration2.d.ts
1786-
17871785
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration3.ts
17881786

17891787
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration4.ts
@@ -13846,6 +13844,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
1384613844
35 │ function fn() { }
1384713845
╰────
1384813846

13847+
× TS(1183): An implementation cannot be declared in ambient contexts.
13848+
╭─[typescript/tests/cases/conformance/ambient/ambientErrors.ts:35:19]
13849+
34 │ var x = 3;
13850+
35 │ function fn() { }
13851+
· ▲
13852+
36 │ class C {
13853+
╰────
13854+
1384913855
× TS(1039): Initializers are not allowed in ambient contexts.
1385013856
╭─[typescript/tests/cases/conformance/ambient/ambientErrors.ts:37:20]
1385113857
36 │ class C {
@@ -25260,6 +25266,13 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
2526025266
· ─────────
2526125267
╰────
2526225268

25269+
× TS(1183): An implementation cannot be declared in ambient contexts.
25270+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration2.d.ts:1:14]
25271+
1 │ function F() {
25272+
· ▲
25273+
2 │ }
25274+
╰────
25275+
2526325276
× TS(1183): An implementation cannot be declared in ambient contexts.
2526425277
╭─[typescript/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration2.ts:1:24]
2526525278
1 │ declare function Foo() {

0 commit comments

Comments
 (0)