fix(parser): reject statements in ambient contexts (TS1036) (#22849)

A non-declaration statement (`if`/`for`/`while`/expression/`try`/block/etc.) is not
allowed directly inside an ambient block — a `.d.ts` top level or a `declare`
namespace/module body:

    declare namespace M { while (true); }   // now TS1036
    // foo.d.ts
    foo();                                    // now TS1036

    declare namespace M { export const x = 1; }  // declarations stay valid
    namespace N { while (true); }                // non-ambient namespace: valid

Implemented inline in `parse_directives_and_statements`. Ambient-ness is captured
once at block entry (`(is_top_level || in_ts_namespace_body) && has_ambient()`),
which excludes function bodies (they report TS1183 for the body instead) and keeps
non-ambient blocks unaffected. A statement is flagged when it is neither a
`Declaration` nor a `ModuleDeclaration` (so imports/exports keep their specific
namespace diagnostics). The error is reported once per block, matching tsc's
`checkGrammarStatementInAmbientContext`. The hot path is unchanged: `has_ambient()`
short-circuits for all non-ambient code.

Three linter fixtures used invalid ambient statements and are updated to valid TS:
`no_shadow` (`declare; foo5: boolean;` -> `declare var foo5: boolean;`),
`check_tag_names` (a `.d.ts` block -> `declare namespace`), and
`prefer_namespace_keyword` (an assignment in a module body -> an ambient binding).

parser_typescript +18 negative-pass; positive/AST stay 100%.
This commit is contained in:
Boshen committed 2026-05-30 08:08:44 +00:00
1 parent 4f9afc56bd
commit 51528545ea
6 files changed
+188 -48

No files matched your search

@@ -1337,8 +1337,7 @@ fn test_eslint() {
), // { "globals": { "Foopy5": false, }, },
(
"
declare;
foo5: boolean;
declare var foo5: boolean;
",
Some(serde_json::json!([{ "builtinGlobals": true }])),
None,
@@ -1426,8 +1425,7 @@ fn test_eslint() {
), // { "globals": { "Foopy5": false, }, },
(
"
declare;
foo5: boolean;
declare var foo5: boolean;
",
Some(serde_json::json!([{ "builtinGlobals": true }])),
None,
@@ -1515,8 +1513,7 @@ fn test_eslint() {
), // { "globals": { "Foopy5": false, }, },
(
"
declare;
foo5: boolean;
declare var foo5: boolean;
",
Some(serde_json::json!([{ "builtinGlobals": true }])),
None,
@@ -3220,8 +3217,7 @@ fn test_typescript_eslint() {
), // { "globals": { "Foopy5": false, }, },
(
"
declare;
foo5: boolean;
declare var foo5: boolean;
",
Some(serde_json::json!([{ "builtinGlobals": true }])),
None,
@@ -1171,7 +1171,7 @@ fn test() {
(
"
/** @abstract */
{ declare let a; }
declare namespace b { let a; }
",
Some(serde_json::json!([
{
@@ -156,8 +156,8 @@ fn test() {
),
("declare /* module */ module foo {}", "declare /* module */ namespace foo {}", None),
(
"declare module X.Y.module { x = 'module'; }",
"declare namespace X.Y.module { x = 'module'; }",
"declare module X.Y.module { let x: 'module'; }",
"declare namespace X.Y.module { let x: 'module'; }",
None,
),
];
+5
View File
@@ -308,6 +308,11 @@ pub fn global_export_in_namespace(span: Span) -> OxcDiagnostic {
ts_error("1316", "Global module exports may only appear at top level.").with_label(span)
}
#[cold]
pub fn statement_in_ambient_context(span: Span) -> OxcDiagnostic {
ts_error("1036", "Statements are not allowed in ambient contexts.").with_label(span)
}
#[cold]
pub fn async_function_declaration(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Async functions can only be declared at the top level or inside a block")
+10
View File
@@ -39,6 +39,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let is_top_level = self.ctx.has_top_level();
let stmt_ctx = StatementContext::StatementList;
let is_ambient_block = (is_top_level || in_ts_namespace_body) && self.ctx.has_ambient();
let mut reported_ambient_statement = false;
// Check if we need to track potential await reparsing.
// This is only needed in unambiguous mode at top level when not in await context.
@@ -106,6 +108,14 @@ impl<'a, C: Config> ParserImpl<'a, C> {
if in_ts_namespace_body {
self.check_namespace_body_statement(&stmt);
}
if is_ambient_block
&& !reported_ambient_statement
&& !stmt.is_declaration()
&& !stmt.is_module_declaration()
{
self.error(diagnostics::statement_in_ambient_context(stmt.span()));
reported_ambient_statement = true;
}
statements.push(stmt);
}
+166 -37
View File
@@ -3,7 +3,7 @@ commit: e5509e21
parser_typescript Summary:
AST Parsed : 9779/9779 (100.00%)
Positive Passed: 9779/9779 (100.00%)
Negative Passed: 1593/2640 (60.34%)
Negative Passed: 1611/2640 (61.02%)
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration3.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration4.ts
@@ -836,8 +836,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/scopeCheckIn
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/selfReferencesInFunctionParameters.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/semicolonsInModuleDeclarations.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/setterWithReturn.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/shorthandPropertyUndefined.ts
@@ -1726,8 +1724,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/node/lega
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/node/nodeModulesCJSEmit1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/node/nodeModulesDeclarationEmitWithPackageExports.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/node/nodeModulesExportsBlocksSpecifierResolution.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/node/nodeModulesExportsBlocksTypesVersions.ts
@@ -1824,48 +1820,18 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList17.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/RegressionTests/parser509618.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/ContinueStatements/parser_continueNotInIterationStatement4.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserBlockStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserDebuggerStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserDoStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement20.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserEmptyStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserExpressionStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement4.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement8.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement5.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserIfStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserLabeledStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserSwitchStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserThrowStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserTryStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserVariableStatement2.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserWhileStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/StrictMode/parserStrictMode1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/StrictMode/parserStrictMode16.ts
@@ -1902,8 +1868,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName41.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement1.d.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement20.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/salsa/assignmentToVoidZero2.ts
@@ -2588,6 +2552,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
7 │
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/compiler/ambientStatement1.ts:2:6]
1 │ declare namespace M1 {
2 │ while(true);
· ────────────
3 │
╰────
× TS(1039): Initializers are not allowed in ambient contexts.
╭─[typescript/tests/cases/compiler/ambientStatement1.ts:4:22]
3 │
@@ -2596,6 +2568,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
5 │ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/compiler/ambientWithStatements.ts:3:5]
2 │ declare namespace M {
3 │ break;
· ──────
4 │ continue;
╰────
× TS(1108): A 'return' statement can only be used within a function body.
╭─[typescript/tests/cases/compiler/ambientWithStatements.ts:12:5]
11 │ L: var y;
@@ -11621,6 +11601,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
· ──────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/compiler/semicolonsInModuleDeclarations.ts:2:27]
1 │ declare namespace ambiModule {
2 │ export interface i1 { };
· ─
3 │ export interface i2 { }
╰────
× Identifier `v` has already been declared
╭─[typescript/tests/cases/compiler/shadowedFunctionScopedVariablesByBlockScopedOnes.ts:4:14]
3 │ function test1() {
@@ -22495,6 +22483,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
13 │
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/externalModules/initializersInDeclarations.ts:15:2]
14 │ declare namespace M1 {
15 │ while(true);
· ────────────
16 │
╰────
× TS(1039): Initializers are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/externalModules/initializersInDeclarations.ts:17:18]
16 │
@@ -23789,6 +23785,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: TypeScript transforms 'import ... =' to 'const ... ='
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/node/nodeModulesDeclarationEmitWithPackageExports.ts:5:1]
4 │ import * as type from "inner";
5 │ cjs;
· ────
6 │ mjs;
╰────
× TS(7060): This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma or explicit constraint.
╭─[typescript/tests/cases/conformance/node/nodeModulesForbidenSyntax.ts:2:12]
1 │ // cjs format file
@@ -26022,6 +26026,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Try inserting a semicolon here
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RegressionTests/parser509618.ts:2:20]
1 │ declare namespace ambiModule {
2 │ interface i1 { };
· ─
3 │ }
╰────
× Unexpected token
╭─[typescript/tests/cases/conformance/parser/ecmascript5/RegressionTests/parser509630.ts:3:1]
2 │ public examples = [ // typing here
@@ -26497,6 +26509,18 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
3 │ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserBlockStatement1.d.ts:1:1]
1 │ {}
· ──
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserBreakStatement1.d.ts:1:1]
1 │ break;
· ──────
╰────
× Illegal break statement
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserBreakStatement1.d.ts:1:1]
1 │ break;
@@ -26504,6 +26528,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: A `break` statement can only be used within an enclosing iteration or switch statement.
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserContinueStatement1.d.ts:1:1]
1 │ continue;
· ─────────
╰────
× Illegal continue statement: no surrounding iteration statement
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserContinueStatement1.d.ts:1:1]
1 │ continue;
@@ -26511,6 +26541,25 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: A `continue` statement can only be used within an enclosing `for`, `while` or `do while`
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserDebuggerStatement1.d.ts:1:1]
1 │ debugger;
· ─────────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserDoStatement1.d.ts:1:1]
1 │ ╭─▶ do {
2 │ │ }
3 │ ╰─▶ while (e);
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement1.d.ts:1:1]
1 │ ╭─▶ for (var i of e) {
2 │ ╰─▶ }
╰────
× Expected `;` but found `Identifier`
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement2.ts:1:13]
1 │ for (var of X) {
@@ -26577,6 +26626,24 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation.
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserEmptyStatement1.d.ts:1:1]
1 │ ;
· ─
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserExpressionStatement1.d.ts:1:1]
1 │ Foo();
· ──────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement1.d.ts:1:1]
1 │ ╭─▶ for (var i in e) {
2 │ ╰─▶ }
╰────
× Identifier expected. 'in' is a reserved word that cannot be used here.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement2.ts:1:10]
1 │ for (var in X) {
@@ -26629,6 +26696,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation.
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement1.d.ts:1:1]
1 │ ╭─▶ for (;;) {
2 │ ╰─▶ }
╰────
× Cannot assign to this expression
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement4.ts:1:6]
1 │ for (a = 1 in b) {
@@ -26657,18 +26730,68 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
2 │ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserIfStatement1.d.ts:1:1]
1 │ ╭─▶ if (foo) {
2 │ ╰─▶ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserLabeledStatement1.d.ts:1:1]
1 │ ╭─▶ foo:
2 │ ╰─▶ bar();
╰────
× TS(1108): A 'return' statement can only be used within a function body.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserReturnStatement1.d.ts:1:1]
1 │ return;
· ──────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserReturnStatement1.d.ts:1:1]
1 │ return;
· ───────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserSwitchStatement1.d.ts:1:1]
1 │ ╭─▶ switch (foo) {
2 │ ╰─▶ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserThrowStatement1.d.ts:1:1]
1 │ throw e;
· ────────
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserTryStatement1.d.ts:1:1]
1 │ ╭─▶ try {
2 │ │ }
3 │ │ catch (e) {
4 │ ╰─▶ }
╰────
× TS(1039): Initializers are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserVariableStatement1.d.ts:1:9]
1 │ var v = 1;
· ─
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserWhileStatement1.d.ts:1:1]
1 │ ╭─▶ while (e) {
2 │ ╰─▶ }
╰────
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserWithStatement1.d.ts:1:1]
1 │ ╭─▶ with (foo) {
2 │ ╰─▶ }
╰────
× 'with' statements are not allowed
╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserWithStatement1.d.ts:1:1]
1 │ with (foo) {
@@ -27057,6 +27180,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: No modifiers are allowed here.
× TS(1036): Statements are not allowed in ambient contexts.
╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement1.d.ts:1:1]
1 │ ╭─▶ for (var i of e) {
2 │ ╰─▶ }
╰────
× Expected `;` but found `Identifier`
╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement2.ts:1:13]
1 │ for (var of X) {