function parseAssertions()

in src/rules/expectRule.ts [238:288]


function parseAssertions(sourceFile: SourceFile): Assertions {
    const errorLines = new Set<number>();
    const typeAssertions = new Map<number, string>();
    const duplicates: number[] = [];

    const { text } = sourceFile;
    const commentRegexp = /\/\/(.*)/g;
    const lineStarts = sourceFile.getLineStarts();
    let curLine = 0;

    // eslint-disable-next-line no-constant-condition
    while (true) {
        const commentMatch = commentRegexp.exec(text);
        if (commentMatch === null) {
            break;
        }
        // Match on the contents of that comment so we do nothing in a commented-out assertion,
        // i.e. `// foo; // $ExpectType number`
        const match = /^ \$Expect((Type (.*))|Error)$/.exec(commentMatch[1]);
        if (match === null) {
            continue;
        }
        const line = getLine(commentMatch.index);
        if (match[1] === "Error") {
            if (errorLines.has(line)) {
                duplicates.push(line);
            }
            errorLines.add(line);
        } else {
            const expectedType = match[3];
            // Don't bother with the assertion if there are 2 assertions on 1 line. Just fail for the duplicate.
            if (typeAssertions.delete(line)) {
                duplicates.push(line);
            } else {
                typeAssertions.set(line, expectedType);
            }
        }
    }

    return { errorLines, typeAssertions, duplicates };

    function getLine(pos: number): number {
        // advance curLine to be the line preceding 'pos'
        while (lineStarts[curLine + 1] <= pos) {
            curLine++;
        }
        // If this is the first token on the line, it applies to the next line.
        // Otherwise, it applies to the text to the left of it.
        return isFirstOnLine(text, lineStarts[curLine], pos) ? curLine + 1 : curLine;
    }
}