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}`);
}
}