function walk()

in src/rules/expectRule.ts [141:227]


function walk(
        ctx: Lint.WalkContext<void>,
        program: Program,
        ts: typeof TsType,
        versionName: string,
        nextHigherVersion: string | undefined): void {
    const { fileName } = ctx.sourceFile;
    const sourceFile = program.getSourceFile(fileName)!;
    if (!sourceFile) {
        ctx.addFailure(0, 0,
            `Program source files differ between TypeScript versions. This may be a dtslint bug.\n` +
            `Expected to find a file '${fileName}' present in ${TsType.version}, but did not find it in ts@${versionName}.`);
        return;
    }

    const checker = program.getTypeChecker();
    // Don't care about emit errors.
    const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile);
    if (sourceFile.isDeclarationFile || !/\$Expect(Type|Error)/.test(sourceFile.text)) {
        // Normal file.
        for (const diagnostic of diagnostics) {
            addDiagnosticFailure(diagnostic);
        }
        return;
    }

    const { errorLines, typeAssertions, duplicates } = parseAssertions(sourceFile);

    for (const line of duplicates) {
        addFailureAtLine(line, Rule.FAILURE_STRING_DUPLICATE_ASSERTION);
    }

    const seenDiagnosticsOnLine = new Set<number>();

    for (const diagnostic of diagnostics) {
        const line = lineOfPosition(diagnostic.start!, sourceFile);
        seenDiagnosticsOnLine.add(line);
        if (!errorLines.has(line)) {
            addDiagnosticFailure(diagnostic);
        }
    }

    for (const line of errorLines) {
        if (!seenDiagnosticsOnLine.has(line)) {
            addFailureAtLine(line, Rule.FAILURE_STRING_EXPECTED_ERROR);
        }
    }

    const { unmetExpectations, unusedAssertions } = getExpectTypeFailures(sourceFile, typeAssertions, checker, ts);
    for (const { node, expected, actual } of unmetExpectations) {
        ctx.addFailureAtNode(node, Rule.FAILURE_STRING(versionName, expected, actual));
    }
    for (const line of unusedAssertions) {
        addFailureAtLine(line, Rule.FAILURE_STRING_ASSERTION_MISSING_NODE);
    }

    function addDiagnosticFailure(diagnostic: TsType.Diagnostic): void {
        const intro = getIntro();
        if (diagnostic.file === sourceFile) {
            const msg = `${intro}\n${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`;
            ctx.addFailureAt(diagnostic.start!, diagnostic.length!, msg);
        } else {
            ctx.addFailureAt(0, 0, `${intro}\n${fileName}${diagnostic.messageText}`);
        }
    }

    function getIntro(): string {
        if (nextHigherVersion === undefined) {
            return `TypeScript@${versionName} compile error: `;
        } else {
            const msg = `Compile error in typescript@${versionName} but not in typescript@${nextHigherVersion}.\n`;
            const explain = nextHigherVersion === "next"
                ? "TypeScript@next features not yet supported."
                : `Fix with a comment '// Minimum TypeScript Version: ${nextHigherVersion}' just under the header.`;
            return msg + explain;
        }
    }

    function addFailureAtLine(line: number, failure: string): void {
        const start = sourceFile.getPositionOfLineAndCharacter(line, 0);
        let end = start + sourceFile.text.split("\n")[line].length;
        if (sourceFile.text[end - 1] === "\r") {
            end--;
        }
        ctx.addFailure(start, end, `TypeScript@${versionName}: ${failure}`);
    }
}