Skip to content

Commit 7a08ab1

Browse files
committed
fix: Fix crashes when dealing with statemented nodes that don't have a body.
1 parent 808198c commit 7a08ab1

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

src/compiler/statement/StatementedNode.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
472472
insertStatements(index: number, writerFunction: WriterFunction): Statement[];
473473
insertStatements(index: number, textOrWriterFunction: string | WriterFunction): Statement[];
474474
insertStatements(index: number, textOrWriterFunction: string | WriterFunction) {
475+
addBodyIfNotExists(this);
476+
475477
return getChildSyntaxList.call(this).insertChildText(index, textOrWriterFunction);
476478

477479
function getChildSyntaxList(this: Node) {
@@ -625,7 +627,6 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
625627
}
626628

627629
getFunctions(): FunctionDeclaration[] {
628-
// todo: remove type assertion
629630
return (this.getChildSyntaxListOrThrow().getChildrenOfKind(SyntaxKind.FunctionDeclaration))
630631
.filter(f => f.isAmbient() || f.isImplementation());
631632
}
@@ -877,8 +878,12 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
877878
// need to get the inner-most body for namespaces
878879
return (this.getInnerBody().compilerNode as ts.Block).statements;
879880
}
880-
else if (TypeGuards.isBodyableNode(this))
881-
return (this.getBodyOrThrow().compilerNode as any).statements as ts.NodeArray<ts.Statement>;
881+
else if (TypeGuards.isBodyableNode(this)) {
882+
const body = this.getBody();
883+
if (body == null)
884+
return [] as any as ts.NodeArray<ts.Statement>;
885+
return (body.compilerNode as any).statements as ts.NodeArray<ts.Statement>;
886+
}
882887
else if (TypeGuards.isBodiedNode(this))
883888
return (this.getBody().compilerNode as any).statements as ts.NodeArray<ts.Statement>;
884889
else if (TypeGuards.isBlock(this))
@@ -888,6 +893,8 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
888893
}
889894

890895
_insertChildren<TNode extends Node, TStructure>(opts: InsertChildrenOptions<TStructure>) {
896+
addBodyIfNotExists(this);
897+
891898
return insertIntoBracesOrSourceFileWithGetChildren<TNode, TStructure>({
892899
expectedKind: opts.expectedKind,
893900
getIndexedChildren: () => this.getStatements(),
@@ -918,3 +925,8 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
918925
}
919926
};
920927
}
928+
929+
function addBodyIfNotExists(node: Node) {
930+
if (TypeGuards.isBodyableNode(node) && !node.hasBody())
931+
node.addBody();
932+
}

src/tests/compiler/statement/statementedNode/classTests.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from "chai";
2-
import { ClassDeclaration, StatementedNode } from "../../../../compiler";
2+
import { FunctionDeclaration, ClassDeclaration, StatementedNode } from "../../../../compiler";
33
import { ClassDeclarationStructure } from "../../../../structures";
44
import { getInfoFromText } from "../../testHelpers";
55

@@ -87,6 +87,20 @@ describe(nameof(StatementedNode), () => {
8787

8888
expect(sourceFile.getFullText()).to.equal("declare module Namespace {\n class Identifier {\n myMethod();\n }\n}\n");
8989
});
90+
91+
function doFunctionTest(startText: string, endText: string) {
92+
const { firstChild, sourceFile } = getInfoFromText<FunctionDeclaration>(startText);
93+
firstChild.insertClasses(0, [{ name: "C" }]);
94+
expect(sourceFile.getFullText()).to.equal(endText);
95+
}
96+
97+
it("should insert into a function with no body", () => {
98+
doFunctionTest("function test();", "function test() {\n class C {\n }\n}");
99+
});
100+
101+
it("should insert into a function with a body", () => {
102+
doFunctionTest("function test() {\n}", "function test() {\n class C {\n }\n}");
103+
});
90104
});
91105

92106
describe(nameof<StatementedNode>(n => n.insertClass), () => {

src/tests/compiler/statement/statementedNodeTests.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ describe(nameof(StatementedNode), () => {
3232
doFirstChildTest<FunctionDeclaration>("function i() { var t; var m; }", ["var t;", "var m;"]);
3333
});
3434

35+
it("should get the statements of a function with no body", () => {
36+
doFirstChildTest<FunctionDeclaration>("function i();", []);
37+
});
38+
3539
it("should get the statements of a namespace", () => {
3640
doFirstChildTest<NamespaceDeclaration>("namespace n { var t; var m; }", ["var t;", "var m;"]);
3741
});
@@ -158,6 +162,11 @@ describe(nameof(StatementedNode), () => {
158162
expect(sourceFile.getFullText()).to.equal(expectedCode);
159163
}
160164

165+
it("should insert statements into a function with no body", () => {
166+
doFirstChildTest<FunctionDeclaration>("function i();\n", 0, "statement;", 1,
167+
"function i() {\n statement;\n}\n");
168+
});
169+
161170
it("should insert statements into an empty function", () => {
162171
doFirstChildTest<FunctionDeclaration>("function i() {\n}\n", 0, "statement;", 1,
163172
"function i() {\n statement;\n}\n");

0 commit comments

Comments
 (0)