Description
TypeScript Version: master (Commit 10b174a)
Search Terms: getChildren, getTokenAtPosition
Code
const ts = require('typescript'); // Built from latest master, commit listed above
// V--- index 13 points right before the 1 here
const exampleSource = 'let z = { w: 1 };'
const sourceFile = ts.createSourceFile('1.ts', exampleSource, ts.ScriptTarget.ES2016)
const node = ts.getTokenAtPosition(sourceFile, 13);
Expected behavior:
node
refers to the NumericLiteral token with text value '1' from position 12 to 14.
Actual behavior:
TypeError: Cannot read property 'text' of undefined
at createChildren (C:\proj\minimal-repro\typescript\typescript.js:110389:65)
at NodeObject.getChildren (C:\proj\minimal-repro\typescript\typescript.js:110352:56)
at getTokenAtPositionWorker (C:\proj\minimal-repro\typescript\typescript.js:88902:43)
at Object.getTokenAtPosition (C:\proj\minimal-repro\typescript\typescript.js:88894:16)
at Object.<anonymous> (C:\proj\minimal-repro\main.js:5:17)
at Module._compile (module.js:660:30)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
at Function.Module._load (module.js:505:3)
Comments:
As mentioned in #23917 by @Andy-MS, it seems that it is best practice to pass a sourceFile to getChildren and related methods, for two reasons:
- without it, getChildren will attempt to traverse
parent
nodes, which may not have been set yet, and can lead to the exception above - even if the
parent
nodes are set, it results in wasted CPU time
The method getTokenAtPositionWorker
(seen in-part below) has a non-optional sourceFile parameter which it passes to getStart, but does not pass to getChildren.
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includePrecedingTokenAtEndPosition: ((n: Node) => boolean) | undefined, includeEndPosition: boolean): Node {
let current: Node = sourceFile;
outer: while (true) {
// find the child that contains 'position'
for (const child of current.getChildren()) {
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true);
I'm happy to submit a pull request. I've already made and manually tested the changes locally and everything is working fine, including existing tests. However, I could use some guidance on where and how to add a test for this change.