Skip to content

getTokenAtPosition throws "Cannot read property 'text' of undefined" #25505

Closed
@Quantumplation

Description

@Quantumplation

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)

Related Issues:
#23917
#14808

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptFixedA PR has been merged for this issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions