Skip to content

Commit 055a0ad

Browse files
committed
Add a hack for tree-sitter maybe-null-element arrays
1 parent a813d8b commit 055a0ad

File tree

7 files changed

+49
-21
lines changed

7 files changed

+49
-21
lines changed

src/control-flow/cfg-c.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
GenericCFGBuilder,
1919
type StatementHandlers,
2020
} from "./generic-cfg-builder.ts";
21+
import { treeSitterNoNullNodes } from "./hacks.ts";
2122
import { buildSwitch, collectCases } from "./switch-utils.ts";
2223

2324
function getChildFieldText(node: SyntaxNode, fieldName: string): string {
@@ -94,9 +95,9 @@ const caseTypes = new Set(["case_statement"]);
9495

9596
function getCases(switchSyntax: SyntaxNode): SyntaxNode[] {
9697
const switchBody = switchSyntax.namedChildren[1] as SyntaxNode;
97-
return switchBody.namedChildren
98-
.filter((x) => x !== null)
99-
.filter((child) => caseTypes.has(child.type));
98+
return treeSitterNoNullNodes(switchBody.namedChildren).filter((child) =>
99+
caseTypes.has(child.type),
100+
);
100101
}
101102

102103
function parseCase(caseSyntax: SyntaxNode): {
@@ -105,9 +106,9 @@ function parseCase(caseSyntax: SyntaxNode): {
105106
hasFallthrough: boolean;
106107
} {
107108
const isDefault = !caseSyntax.childForFieldName("value");
108-
const consequence = caseSyntax.namedChildren
109-
.filter((x) => x !== null)
110-
.slice(isDefault ? 0 : 1);
109+
const consequence = treeSitterNoNullNodes(caseSyntax.namedChildren).slice(
110+
isDefault ? 0 : 1,
111+
);
111112
const hasFallthrough = true;
112113
return { isDefault, consequence, hasFallthrough };
113114
}

src/control-flow/cfg-go.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
GenericCFGBuilder,
1717
type StatementHandlers,
1818
} from "./generic-cfg-builder";
19+
import { treeSitterNoNullNodes } from "./hacks.ts";
1920
import { type SwitchOptions, buildSwitch, collectCases } from "./switch-utils";
2021

2122
const processBreakStatement = labeledBreakProcessor(`
@@ -322,9 +323,9 @@ const caseTypes = new Set([
322323
]);
323324

324325
function getCases(switchSyntax: SyntaxNode): SyntaxNode[] {
325-
return switchSyntax.namedChildren
326-
.filter((x) => x !== null)
327-
.filter((child) => caseTypes.has(child.type));
326+
return treeSitterNoNullNodes(switchSyntax.namedChildren).filter((child) =>
327+
caseTypes.has(child.type),
328+
);
328329
}
329330

330331
function parseCase(caseSyntax: SyntaxNode): {
@@ -333,9 +334,9 @@ function parseCase(caseSyntax: SyntaxNode): {
333334
hasFallthrough: boolean;
334335
} {
335336
const isDefault = caseSyntax.type === "default_case";
336-
const consequence = caseSyntax.namedChildren
337-
.filter((x) => x !== null)
338-
.slice(isDefault ? 0 : 1);
337+
const consequence = treeSitterNoNullNodes(caseSyntax.namedChildren).slice(
338+
isDefault ? 0 : 1,
339+
);
339340
const hasFallthrough = consequence
340341
.map((node) => node.type)
341342
.includes("fallthrough_statement");

src/control-flow/cfg-typescript.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
GenericCFGBuilder,
2121
type StatementHandlers,
2222
} from "./generic-cfg-builder.ts";
23+
import { treeSitterNoNullNodes } from "./hacks.ts";
2324
import { buildSwitch, collectCases } from "./switch-utils.ts";
2425

2526
export function createCFGBuilder(options: BuilderOptions): CFGBuilder {
@@ -115,9 +116,9 @@ const caseTypes = new Set(["switch_case", "switch_default"]);
115116

116117
function getCases(switchSyntax: SyntaxNode): SyntaxNode[] {
117118
const switchBody = switchSyntax.namedChildren[1] as SyntaxNode;
118-
return switchBody.namedChildren
119-
.filter((x) => x !== null)
120-
.filter((child) => caseTypes.has(child.type));
119+
return treeSitterNoNullNodes(switchBody.namedChildren).filter((child) =>
120+
caseTypes.has(child.type),
121+
);
121122
}
122123

123124
function parseCase(caseSyntax: SyntaxNode): {
@@ -126,9 +127,9 @@ function parseCase(caseSyntax: SyntaxNode): {
126127
hasFallthrough: boolean;
127128
} {
128129
const isDefault = caseSyntax.type === "switch_default";
129-
const consequence = caseSyntax.namedChildren
130-
.filter((x) => x !== null)
131-
.slice(isDefault ? 0 : 1);
130+
const consequence = treeSitterNoNullNodes(caseSyntax.namedChildren).slice(
131+
isDefault ? 0 : 1,
132+
);
132133
const hasFallthrough = true;
133134
return { isDefault, consequence, hasFallthrough };
134135
}

src/control-flow/common-patterns.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Node as SyntaxNode } from "web-tree-sitter";
22
import type { Match } from "./block-matcher.ts";
33
import type { BasicBlock } from "./cfg-defs.ts";
44
import type { Context } from "./generic-cfg-builder.ts";
5+
import { treeSitterNoNullNodes } from "./hacks.ts";
56
import { last, pairwise, zip } from "./itertools.ts";
67

78
export function cStyleIfProcessor(
@@ -514,7 +515,7 @@ export function processStatementSequence(
514515
ctx: Context,
515516
): BasicBlock {
516517
const blockBlock = ctx.dispatch.many(
517-
syntax.namedChildren.filter((x) => x !== null),
518+
treeSitterNoNullNodes(syntax.namedChildren),
518519
syntax,
519520
);
520521
ctx.link.syntaxToNode(syntax, blockBlock.entry);

src/control-flow/generic-cfg-builder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
type BuilderOptions,
88
type CFG,
99
} from "./cfg-defs";
10+
import { treeSitterNoNullNodes } from "./hacks.ts";
1011
import { pairwise } from "./itertools.ts";
1112
import { NodeMapper } from "./node-mapper";
1213

@@ -99,7 +100,7 @@ export class GenericCFGBuilder {
99100
const blockHandler = new BlockHandler();
100101
const { entry, exit } = blockHandler.update(
101102
this.dispatchMany(
102-
bodySyntax.namedChildren.filter((x) => x !== null),
103+
treeSitterNoNullNodes(bodySyntax.namedChildren),
103104
bodySyntax,
104105
),
105106
);

src/control-flow/hacks.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* This module contains temporary hacks.
3+
* This is terrible code that should not exist,
4+
* but is here to circumvent bugs in other projects.
5+
*
6+
* As such, all code here should document the bugs it works around
7+
* and the PRs or issues that will make it obsolete.
8+
*/
9+
import type { Node } from "web-tree-sitter";
10+
11+
/**
12+
* Assert that there are no nulls in a value returned from tree-sitter.
13+
* This can be removed once https://github.com/tree-sitter/tree-sitter/pull/4283 is merged.
14+
* Also relates to https://github.com/tree-sitter/tree-sitter/discussions/4273
15+
* @param array
16+
*/
17+
export function treeSitterNoNullNodes(array: (Node | null)[]): Node[] {
18+
if (array.some((x) => x === null)) {
19+
throw new Error("tree-sitter API actually had a null in an array!");
20+
}
21+
return array as Node[];
22+
}

src/control-flow/overlay.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Container, Element, Svg } from "@svgdotjs/svg.js";
22
import type { Node as SyntaxNode } from "web-tree-sitter";
33
import type { CFG, GraphNode } from "./cfg-defs.ts";
44
import type { AttrMerger } from "./graph-ops.ts";
5+
import { treeSitterNoNullNodes } from "./hacks.ts";
56
import { Lookup } from "./ranges.ts";
67
import { svgFromString } from "./svgFromString.ts";
78

@@ -236,7 +237,7 @@ type OverlayRange = {
236237
};
237238

238239
function parseOverlay(func: SyntaxNode): OverlayRange[] {
239-
const comments = func.descendantsOfType("comment").filter((x) => x !== null);
240+
const comments = treeSitterNoNullNodes(func.descendantsOfType("comment"));
240241
const stack: { startOffset: number; text: string }[] = [];
241242
const overlays: OverlayRange[] = [];
242243
for (const comment of comments) {

0 commit comments

Comments
 (0)