Skip to content

Commit 95a124f

Browse files
authored
Fix crash on bad namespace parse (microsoft#37626)
* Fix crash on bad namespace parse `global` inside a class body is parsed as a module declaration, and in the following example has no body: ```ts class C { global x } ``` `x` is parsed as a separate ExpressionStatement. This caused a crash in emit because the code didn't expect a module declaration with no body. * inline node.body variable * fix missed references to body
1 parent ed1863b commit 95a124f

6 files changed

Lines changed: 87 additions & 19 deletions

File tree

src/compiler/transformers/ts.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,27 +2693,28 @@ namespace ts {
26932693
const statements: Statement[] = [];
26942694
startLexicalEnvironment();
26952695

2696-
let statementsLocation: TextRange;
2696+
let statementsLocation: TextRange | undefined;
26972697
let blockLocation: TextRange | undefined;
2698-
const body = node.body!;
2699-
if (body.kind === SyntaxKind.ModuleBlock) {
2700-
saveStateAndInvoke(body, body => addRange(statements, visitNodes((<ModuleBlock>body).statements, namespaceElementVisitor, isStatement)));
2701-
statementsLocation = body.statements;
2702-
blockLocation = body;
2703-
}
2704-
else {
2705-
const result = visitModuleDeclaration(<ModuleDeclaration>body);
2706-
if (result) {
2707-
if (isArray(result)) {
2708-
addRange(statements, result);
2709-
}
2710-
else {
2711-
statements.push(result);
2712-
}
2698+
if (node.body) {
2699+
if (node.body.kind === SyntaxKind.ModuleBlock) {
2700+
saveStateAndInvoke(node.body, body => addRange(statements, visitNodes((<ModuleBlock>body).statements, namespaceElementVisitor, isStatement)));
2701+
statementsLocation = node.body.statements;
2702+
blockLocation = node.body;
27132703
}
2704+
else {
2705+
const result = visitModuleDeclaration(<ModuleDeclaration>node.body);
2706+
if (result) {
2707+
if (isArray(result)) {
2708+
addRange(statements, result);
2709+
}
2710+
else {
2711+
statements.push(result);
2712+
}
2713+
}
27142714

2715-
const moduleBlock = <ModuleBlock>getInnerMostModuleDeclarationFromDottedModule(node)!.body;
2716-
statementsLocation = moveRangePos(moduleBlock.statements, -1);
2715+
const moduleBlock = <ModuleBlock>getInnerMostModuleDeclarationFromDottedModule(node)!.body;
2716+
statementsLocation = moveRangePos(moduleBlock.statements, -1);
2717+
}
27172718
}
27182719

27192720
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
@@ -2750,7 +2751,7 @@ namespace ts {
27502751
// })(hi = hello.hi || (hello.hi = {}));
27512752
// })(hello || (hello = {}));
27522753
// We only want to emit comment on the namespace which contains block body itself, not the containing namespaces.
2753-
if (body.kind !== SyntaxKind.ModuleBlock) {
2754+
if (!node.body || node.body.kind !== SyntaxKind.ModuleBlock) {
27542755
setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments);
27552756
}
27562757
return block;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(3,5): error TS1068: Unexpected token. A constructor, method, accessor, or property was expected.
2+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(3,5): error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
3+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(3,5): error TS2670: Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.
4+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(3,12): error TS1005: ';' expected.
5+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(3,12): error TS2304: Cannot find name 'x'.
6+
tests/cases/compiler/nestedGlobalNamespaceInClass.ts(4,1): error TS1128: Declaration or statement expected.
7+
8+
9+
==== tests/cases/compiler/nestedGlobalNamespaceInClass.ts (6 errors) ====
10+
// should not crash - from #35717
11+
class C {
12+
global x
13+
~~~~~~
14+
!!! error TS1068: Unexpected token. A constructor, method, accessor, or property was expected.
15+
~~~~~~
16+
!!! error TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
17+
~~~~~~
18+
!!! error TS2670: Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.
19+
~
20+
!!! error TS1005: ';' expected.
21+
~
22+
!!! error TS2304: Cannot find name 'x'.
23+
}
24+
~
25+
!!! error TS1128: Declaration or statement expected.
26+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//// [nestedGlobalNamespaceInClass.ts]
2+
// should not crash - from #35717
3+
class C {
4+
global x
5+
}
6+
7+
8+
//// [nestedGlobalNamespaceInClass.js]
9+
// should not crash - from #35717
10+
var C = /** @class */ (function () {
11+
function C() {
12+
}
13+
return C;
14+
}());
15+
var global;
16+
(function (global) {
17+
})(global || (global = {}));
18+
x;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/compiler/nestedGlobalNamespaceInClass.ts ===
2+
// should not crash - from #35717
3+
class C {
4+
>C : Symbol(C, Decl(nestedGlobalNamespaceInClass.ts, 0, 0))
5+
6+
global x
7+
>global : Symbol(global, Decl(nestedGlobalNamespaceInClass.ts, 1, 9))
8+
}
9+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/compiler/nestedGlobalNamespaceInClass.ts ===
2+
// should not crash - from #35717
3+
class C {
4+
>C : C
5+
6+
global x
7+
>global : any
8+
>x : any
9+
}
10+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// should not crash - from #35717
2+
class C {
3+
global x
4+
}

0 commit comments

Comments
 (0)