mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-10-28 16:36:04 +08:00
Fix synthesized node ranges across native API encoding (#64859)
Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
1 parent
7fa14948c3
commit
4d19708e1f
8 files changed
+82
-12
No files matched your search
@@ -226,8 +226,8 @@ export function encodeNode(node: Node): Uint8Array {
|
||||
const data = getNodeData(node, strs, extendedDataValues, structuredWriter);
|
||||
nodeValues.push(
|
||||
node.kind,
|
||||
node.pos >= 0 ? node.pos : 0,
|
||||
node.end >= 0 ? node.end : 0,
|
||||
node.pos,
|
||||
node.end,
|
||||
0, // next (filled in later)
|
||||
parentIndex,
|
||||
data,
|
||||
@@ -259,8 +259,8 @@ export function encodeNode(node: Node): Uint8Array {
|
||||
|
||||
nodeValues.push(
|
||||
KIND_NODE_LIST,
|
||||
list.pos >= 0 ? list.pos : 0,
|
||||
list.end >= 0 ? list.end : 0,
|
||||
list.pos,
|
||||
list.end,
|
||||
0, // next
|
||||
parentIndex,
|
||||
list.length, // data for NodeList is its length
|
||||
@@ -304,8 +304,8 @@ export function encodeNode(node: Node): Uint8Array {
|
||||
const rootData = getNodeData(node, strs, extendedDataValues, structuredWriter);
|
||||
nodeValues.push(
|
||||
node.kind,
|
||||
node.pos >= 0 ? node.pos : 0,
|
||||
node.end >= 0 ? node.end : 0,
|
||||
node.pos,
|
||||
node.end,
|
||||
0,
|
||||
0,
|
||||
rootData,
|
||||
|
||||
@@ -58,11 +58,11 @@ export class RemoteNodeList extends Array<RemoteNode> implements NodeArray<Remot
|
||||
private _cursorNodeIndex: number = 0;
|
||||
|
||||
get pos(): number {
|
||||
return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);
|
||||
return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true);
|
||||
}
|
||||
|
||||
get end(): number {
|
||||
return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);
|
||||
return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true);
|
||||
}
|
||||
|
||||
get next(): number {
|
||||
|
||||
@@ -39,18 +39,22 @@ import {
|
||||
type RootedFilePath,
|
||||
type SourceFile,
|
||||
SyntaxKind,
|
||||
TokenFlags,
|
||||
tryGetAmbientModuleNameFromSymbolName,
|
||||
unescapeLeadingUnderscores,
|
||||
} from "@typescript/typescript/ast";
|
||||
import {
|
||||
cloneNode,
|
||||
createArrayTypeNode,
|
||||
createCallExpression,
|
||||
createExpressionStatement,
|
||||
createFunctionTypeNode,
|
||||
createIdentifier,
|
||||
createKeywordTypeNode,
|
||||
createNumericLiteral,
|
||||
createParameterDeclaration,
|
||||
createSourceFile,
|
||||
createStringLiteral,
|
||||
createToken,
|
||||
createTypeAliasDeclaration,
|
||||
createTypeReferenceNode,
|
||||
@@ -58,6 +62,7 @@ import {
|
||||
createVariableDeclaration,
|
||||
createVariableDeclarationList,
|
||||
createVariableStatement,
|
||||
updateSourceFile,
|
||||
} from "@typescript/typescript/ast/factory";
|
||||
import { visitEachChild } from "@typescript/typescript/ast/visitor";
|
||||
import {
|
||||
@@ -7964,6 +7969,20 @@ export const obj = { m: 1, s: "hi", b: true };
|
||||
assert.strictEqual(text, "string");
|
||||
});
|
||||
|
||||
test("synthesized nodes do not acquire source comments", async () => {
|
||||
await using api = spawnAPI();
|
||||
await using lease = await api.createSourceFile("/test.ts", "/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n");
|
||||
const { sourceFile } = lease;
|
||||
const added = createExpressionStatement(
|
||||
createCallExpression(createIdentifier("bar"), undefined, undefined, [createStringLiteral("x", TokenFlags.None)], NodeFlags.None),
|
||||
);
|
||||
const updated = updateSourceFile(sourceFile, [added, ...sourceFile.statements], sourceFile.endOfFileToken);
|
||||
const expected = 'bar("x");\n/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n';
|
||||
|
||||
assert.strictEqual(await api.printer.printNode(updated), expected);
|
||||
assert.strictEqual(await api.printer.printFile(updated), expected);
|
||||
});
|
||||
|
||||
test("printFile", async () => {
|
||||
await using api = spawnAPI(emitterFiles);
|
||||
|
||||
|
||||
@@ -74,6 +74,17 @@ function decode(data: Uint8Array): RemoteSourceFile {
|
||||
}
|
||||
|
||||
describe("Encoder", { concurrency }, () => {
|
||||
test("preserves synthesized node and list ranges", () => {
|
||||
const statement = createExpressionStatement(createIdentifier("x"));
|
||||
const sf = makeSF("", "/test.ts", [statement]);
|
||||
const decoded = decode(encodeSourceFile(sf));
|
||||
|
||||
for (const range of [decoded, decoded.statements!, decoded.statements!.at(0)!, decoded.endOfFileToken!]) {
|
||||
assert.strictEqual(range.pos, -1);
|
||||
assert.strictEqual(range.end, -1);
|
||||
}
|
||||
});
|
||||
|
||||
test("encodes empty source file", () => {
|
||||
const sf = makeSF("", "/test.ts", []);
|
||||
|
||||
|
||||
@@ -47,18 +47,22 @@ import {
|
||||
type RootedFilePath,
|
||||
type SourceFile,
|
||||
SyntaxKind,
|
||||
TokenFlags,
|
||||
tryGetAmbientModuleNameFromSymbolName,
|
||||
unescapeLeadingUnderscores,
|
||||
} from "@typescript/typescript/ast";
|
||||
import {
|
||||
cloneNode,
|
||||
createArrayTypeNode,
|
||||
createCallExpression,
|
||||
createExpressionStatement,
|
||||
createFunctionTypeNode,
|
||||
createIdentifier,
|
||||
createKeywordTypeNode,
|
||||
createNumericLiteral,
|
||||
createParameterDeclaration,
|
||||
createSourceFile,
|
||||
createStringLiteral,
|
||||
createToken,
|
||||
createTypeAliasDeclaration,
|
||||
createTypeReferenceNode,
|
||||
@@ -66,6 +70,7 @@ import {
|
||||
createVariableDeclaration,
|
||||
createVariableDeclarationList,
|
||||
createVariableStatement,
|
||||
updateSourceFile,
|
||||
} from "@typescript/typescript/ast/factory";
|
||||
import { visitEachChild } from "@typescript/typescript/ast/visitor";
|
||||
import {
|
||||
@@ -7765,6 +7770,20 @@ export const obj = { m: 1, s: "hi", b: true };
|
||||
assert.strictEqual(text, "string");
|
||||
});
|
||||
|
||||
test("synthesized nodes do not acquire source comments", () => {
|
||||
using api = spawnAPI();
|
||||
using lease = api.createSourceFile("/test.ts", "/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n");
|
||||
const { sourceFile } = lease;
|
||||
const added = createExpressionStatement(
|
||||
createCallExpression(createIdentifier("bar"), undefined, undefined, [createStringLiteral("x", TokenFlags.None)], NodeFlags.None),
|
||||
);
|
||||
const updated = updateSourceFile(sourceFile, [added, ...sourceFile.statements], sourceFile.endOfFileToken);
|
||||
const expected = 'bar("x");\n/** leading comment */\nfoo();\n/* @__PURE__ */ baz();\n';
|
||||
|
||||
assert.strictEqual(api.printer.printNode(updated), expected);
|
||||
assert.strictEqual(api.printer.printFile(updated), expected);
|
||||
});
|
||||
|
||||
test("printFile", () => {
|
||||
using api = spawnAPI(emitterFiles);
|
||||
|
||||
|
||||
@@ -1535,11 +1535,11 @@ function emitRemoteNodeList(w: CodeWriter) {
|
||||
w.write(` private _cursorNodeIndex: number = 0;`);
|
||||
w.write(``);
|
||||
w.write(` get pos(): number {`);
|
||||
w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);`);
|
||||
w.write(` return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true);`);
|
||||
w.write(` }`);
|
||||
w.write(``);
|
||||
w.write(` get end(): number {`);
|
||||
w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);`);
|
||||
w.write(` return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true);`);
|
||||
w.write(` }`);
|
||||
w.write(``);
|
||||
w.write(` get next(): number {`);
|
||||
|
||||
@@ -149,8 +149,8 @@ func (d *astDecoder) decode() (*ast.Node, error) {
|
||||
// Process bottom-up so children exist before parents.
|
||||
for i := d.nodeCount - 1; i >= 1; i-- {
|
||||
kind := d.nodeField(i, NodeOffsetKind)
|
||||
pos := d.nodeField(i, NodeOffsetPos)
|
||||
end := d.nodeField(i, NodeOffsetEnd)
|
||||
pos := int32(d.nodeField(i, NodeOffsetPos))
|
||||
end := int32(d.nodeField(i, NodeOffsetEnd))
|
||||
data := d.nodeField(i, NodeOffsetData)
|
||||
childIndices := d.collectChildren(i)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package encoder_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -37,6 +38,26 @@ func TestDecodeSourceFile_Basic(t *testing.T) {
|
||||
assert.Assert(t, decoded.EndOfFileToken != nil)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_SynthesizedRanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
sf := parseSourceFile("foo();")
|
||||
synthesized := core.NewTextRange(-1, -1)
|
||||
buf, _, err := encoder.EncodeSourceFile(sf)
|
||||
assert.NilError(t, err)
|
||||
nodesOffset := int(binary.LittleEndian.Uint32(buf[encoder.HeaderOffsetNodes:]))
|
||||
for offset := nodesOffset + encoder.NodeSize; offset < len(buf); offset += encoder.NodeSize {
|
||||
binary.LittleEndian.PutUint32(buf[offset+encoder.NodeOffsetPos:], ^uint32(0))
|
||||
binary.LittleEndian.PutUint32(buf[offset+encoder.NodeOffsetEnd:], ^uint32(0))
|
||||
}
|
||||
decoded, err := encoder.DecodeSourceFile(buf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, decoded.AsNode().Loc, synthesized)
|
||||
assert.Equal(t, decoded.Statements.Loc, synthesized)
|
||||
assert.Equal(t, decoded.Statements.Nodes[0].Loc, synthesized)
|
||||
assert.Equal(t, decoded.EndOfFileToken.Loc, synthesized)
|
||||
}
|
||||
|
||||
func TestDecodeSourceFile_Metadata(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in new issue
Block a user