Allow --noCheck to be commandLine option (#58839)

This commit is contained in:
Sheetal Nandi
2024-06-14 11:40:32 -07:00
committed by GitHub
parent c2e48e564a
commit e834989ebd
86 changed files with 29996 additions and 4931 deletions

View File

@@ -76,7 +76,7 @@ import {
SemanticDiagnosticsBuilderProgram,
SignatureInfo,
skipAlias,
skipTypeChecking,
skipTypeCheckingIgnoringNoCheck,
some,
SourceFile,
sourceFileMayBeEmitted,
@@ -158,6 +158,8 @@ export interface ReusableBuilderProgramState extends BuilderState {
* emitKind pending for a program with --out
*/
programEmitPending?: BuilderFileEmit;
/** If semantic diagnsotic check is pending */
checkPending?: true;
/*
* true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic
*/
@@ -329,6 +331,7 @@ function createBuilderProgramState(
}
state.changedFilesSet = new Set();
state.latestChangedDtsFile = compilerOptions.composite ? oldState?.latestChangedDtsFile : undefined;
state.checkPending = state.compilerOptions.noCheck ? true : undefined;
const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState);
const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
@@ -473,6 +476,11 @@ function createBuilderProgramState(
state.buildInfoEmitPending = true;
}
}
if (
useOldState &&
state.semanticDiagnosticsPerFile.size !== state.fileInfos.size &&
oldState!.checkPending !== state.checkPending
) state.buildInfoEmitPending = true;
return state;
function addFileToChangeSet(path: Path) {
@@ -741,7 +749,7 @@ function removeDiagnosticsOfLibraryFiles(state: BuilderProgramStateWithDefinedPr
const options = state.program.getCompilerOptions();
forEach(state.program.getSourceFiles(), f =>
state.program.isSourceFileDefaultLibrary(f) &&
!skipTypeChecking(f, options, state.program) &&
!skipTypeCheckingIgnoringNoCheck(f, options, state.program) &&
removeSemanticDiagnosticsOf(state, f.resolvedPath));
}
}
@@ -986,6 +994,7 @@ function getSemanticDiagnosticsOfFile(
cancellationToken: CancellationToken | undefined,
semanticDiagnosticsPerFile?: BuilderProgramState["semanticDiagnosticsPerFile"],
): readonly Diagnostic[] {
if (state.compilerOptions.noCheck) return emptyArray;
return concatenate(
getBinderAndCheckerDiagnosticsOfFile(state, sourceFile, cancellationToken, semanticDiagnosticsPerFile),
state.program.getProgramDiagnostics(sourceFile),
@@ -1084,6 +1093,7 @@ export interface IncrementalBuildInfoBase extends BuildInfo {
// Because this is only output file in the program, we dont need fileId to deduplicate name
latestChangedDtsFile?: string | undefined;
errors: true | undefined;
checkPending: true | undefined;
}
/** @internal */
@@ -1131,6 +1141,7 @@ export function isIncrementalBuildInfo(info: BuildInfo): info is IncrementalBuil
export interface NonIncrementalBuildInfo extends BuildInfo {
root: readonly string[];
errors: true | undefined;
checkPending: true | undefined;
}
/** @internal */
@@ -1190,6 +1201,7 @@ function getBuildInfo(state: BuilderProgramStateWithDefinedProgram): BuildInfo {
const buildInfo: NonIncrementalBuildInfo = {
root: arrayFrom(rootFileNames, r => relativeToBuildInfo(r)),
errors: state.hasErrors ? true : undefined,
checkPending: state.checkPending,
version,
};
return buildInfo;
@@ -1223,6 +1235,7 @@ function getBuildInfo(state: BuilderProgramStateWithDefinedProgram): BuildInfo {
false : // Pending emit is same as deteremined by compilerOptions
state.programEmitPending, // Actual value
errors: state.hasErrors ? true : undefined,
checkPending: state.checkPending,
version,
};
return buildInfo;
@@ -1313,6 +1326,7 @@ function getBuildInfo(state: BuilderProgramStateWithDefinedProgram): BuildInfo {
emitSignatures,
latestChangedDtsFile,
errors: state.hasErrors ? true : undefined,
checkPending: state.checkPending,
version,
};
return buildInfo;
@@ -1952,7 +1966,13 @@ export function createBuilderProgram(
while (true) {
const affected = getNextAffectedFile(state, cancellationToken, host);
let result;
if (!affected) return undefined; // Done
if (!affected) {
if (state.checkPending && !state.compilerOptions.noCheck) {
state.checkPending = undefined;
state.buildInfoEmitPending = true;
}
return undefined; // Done
}
else if (affected !== state.program) {
// Get diagnostics for the affected file if its not ignored
const affectedSourceFile = affected as SourceFile;
@@ -1984,6 +2004,7 @@ export function createBuilderProgram(
result = diagnostics || emptyArray;
state.changedFilesSet.clear();
state.programEmitPending = getBuilderFileEmit(state.compilerOptions);
if (!state.compilerOptions.noCheck) state.checkPending = undefined;
state.buildInfoEmitPending = true;
}
return { result, affected };
@@ -2021,6 +2042,10 @@ export function createBuilderProgram(
for (const sourceFile of state.program.getSourceFiles()) {
diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken));
}
if (state.checkPending && !state.compilerOptions.noCheck) {
state.checkPending = undefined;
state.buildInfoEmitPending = true;
}
return diagnostics || emptyArray;
}
}
@@ -2089,6 +2114,7 @@ export function createBuilderProgramUsingIncrementalBuildInfo(
outSignature: buildInfo.outSignature,
programEmitPending: buildInfo.pendingEmit === undefined ? undefined : toProgramEmitPending(buildInfo.pendingEmit, buildInfo.options),
hasErrors: buildInfo.errors,
checkPending: buildInfo.checkPending,
};
}
else {
@@ -2125,6 +2151,7 @@ export function createBuilderProgramUsingIncrementalBuildInfo(
latestChangedDtsFile,
emitSignatures: emitSignatures?.size ? emitSignatures : undefined,
hasErrors: buildInfo.errors,
checkPending: buildInfo.checkPending,
};
}

View File

@@ -502,6 +502,25 @@ export const commonOptionsWithBuild: CommandLineOption[] = [
description: Diagnostics.Include_sourcemap_files_inside_the_emitted_JavaScript,
defaultValueDescription: false,
},
{
name: "noCheck",
type: "boolean",
showInSimplifiedHelpView: false,
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Disable_full_type_checking_only_critical_parse_and_emit_errors_will_be_reported,
transpileOptionValue: true,
defaultValueDescription: false,
// Not setting affectsSemanticDiagnostics or affectsBuildInfo because we dont want all diagnostics to go away, its handled in builder
},
{
name: "noEmit",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_emitting_files_from_a_compilation,
transpileOptionValue: undefined,
defaultValueDescription: false,
},
{
name: "assumeChangesOnlyAffectDirectDependencies",
type: "boolean",
@@ -772,29 +791,6 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
defaultValueDescription: false,
description: Diagnostics.Disable_emitting_comments,
},
{
name: "noCheck",
type: "boolean",
showInSimplifiedHelpView: false,
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Disable_full_type_checking_only_critical_parse_and_emit_errors_will_be_reported,
transpileOptionValue: true,
defaultValueDescription: false,
affectsSemanticDiagnostics: true,
affectsBuildInfo: true,
extraValidation() {
return [Diagnostics.Unknown_compiler_option_0, "noCheck"];
},
},
{
name: "noEmit",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_emitting_files_from_a_compilation,
transpileOptionValue: undefined,
defaultValueDescription: false,
},
{
name: "importHelpers",
type: "boolean",

View File

@@ -1535,7 +1535,11 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
};
}
if ((buildInfo as IncrementalBuildInfo | NonIncrementalBuildInfo).errors) {
if (
!project.options.noCheck &&
((buildInfo as IncrementalBuildInfo | NonIncrementalBuildInfo).errors || // TODO: syntax errors????
(buildInfo as IncrementalBuildInfo | NonIncrementalBuildInfo).checkPending)
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfoWithErrors,
buildInfoFile: buildInfoPath,
@@ -1545,8 +1549,9 @@ function getUpToDateStatusWorker<T extends BuilderProgram>(state: SolutionBuilde
if (incrementalBuildInfo) {
// If there are errors, we need to build project again to report it
if (
incrementalBuildInfo.semanticDiagnosticsPerFile?.length ||
(!project.options.noEmit && getEmitDeclarations(project.options) && incrementalBuildInfo.emitDiagnosticsPerFile?.length)
!project.options.noCheck &&
(incrementalBuildInfo.semanticDiagnosticsPerFile?.length ||
(!project.options.noEmit && getEmitDeclarations(project.options) && incrementalBuildInfo.emitDiagnosticsPerFile?.length))
) {
return {
type: UpToDateStatusType.OutOfDateBuildInfoWithErrors,

View File

@@ -10074,13 +10074,35 @@ export interface HostWithIsSourceOfProjectReferenceRedirect {
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
}
/** @internal */
export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOptions, host: HostWithIsSourceOfProjectReferenceRedirect) {
export function skipTypeChecking(
sourceFile: SourceFile,
options: CompilerOptions,
host: HostWithIsSourceOfProjectReferenceRedirect,
) {
return skipTypeCheckingWorker(sourceFile, options, host, /*ignoreNoCheck*/ false);
}
/** @internal */
export function skipTypeCheckingIgnoringNoCheck(
sourceFile: SourceFile,
options: CompilerOptions,
host: HostWithIsSourceOfProjectReferenceRedirect,
) {
return skipTypeCheckingWorker(sourceFile, options, host, /*ignoreNoCheck*/ true);
}
function skipTypeCheckingWorker(
sourceFile: SourceFile,
options: CompilerOptions,
host: HostWithIsSourceOfProjectReferenceRedirect,
ignoreNoCheck: boolean,
) {
// If skipLibCheck is enabled, skip reporting errors if file is a declaration file.
// If skipDefaultLibCheck is enabled, skip reporting errors if file contains a
// '/// <reference no-default-lib="true"/>' directive.
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
options.noCheck ||
(!ignoreNoCheck && options.noCheck) ||
host.isSourceOfProjectReferenceRedirect(sourceFile.fileName) ||
!canIncludeBindAndCheckDiagnostics(sourceFile, options);
}

View File

@@ -122,6 +122,7 @@ export * from "./unittests/tsc/incremental.js";
export * from "./unittests/tsc/libraryResolution.js";
export * from "./unittests/tsc/listFilesOnly.js";
export * from "./unittests/tsc/moduleResolution.js";
export * from "./unittests/tsc/noCheck.js";
export * from "./unittests/tsc/noEmit.js";
export * from "./unittests/tsc/noEmitOnError.js";
export * from "./unittests/tsc/projectReferences.js";

View File

@@ -0,0 +1,92 @@
import { jsonToReadableText } from "../helpers.js";
import {
noChangeRun,
TestTscEdit,
verifyTsc,
} from "./tsc.js";
import { loadProjectFromFiles } from "./vfs.js";
export function forEachTscScenarioWithNoCheck(buildType: "-b" | "-p") {
const commandLineArgs = buildType === "-b" ?
["-b", "/src/tsconfig.json", "-v"] :
["-p", "/src/tsconfig.json"];
function forEachNoCheckScenarioWorker(
subScenario: string,
aText: string,
) {
const checkNoChangeRun: TestTscEdit = {
...noChangeRun,
caption: "No Change run with checking",
commandLineArgs,
};
const noCheckFixError: TestTscEdit = {
caption: "Fix `a` error with noCheck",
edit: fs => fs.writeFileSync("/src/a.ts", `export const a = "hello";`),
};
const noCheckError: TestTscEdit = {
caption: "Introduce error with noCheck",
edit: fs => fs.writeFileSync("/src/a.ts", aText),
};
const noChangeRunWithCheckPendingDiscrepancy: TestTscEdit = {
...noChangeRun,
discrepancyExplanation: () => [
"Clean build will have check pending since it didnt type check",
"Incremental build has typechecked before this so wont have checkPending",
],
};
[undefined, true].forEach(incremental => {
[{}, { module: "amd", outFile: "../outFile.js" }].forEach(options => {
verifyTsc({
scenario: "noCheck",
subScenario: `${options.outFile ? "outFile" : "multiFile"}/${subScenario}${incremental ? " with incremental" : ""}`,
fs: () =>
loadProjectFromFiles({
"/src/a.ts": aText,
"/src/b.ts": `export const b = 10;`,
"/src/tsconfig.json": jsonToReadableText({
compilerOptions: {
declaration: true,
incremental,
...options,
},
}),
}),
commandLineArgs: [...commandLineArgs, "--noCheck"],
edits: [
noChangeRun, // Should be no op
noCheckFixError, // Fix error with noCheck
noChangeRun, // Should be no op
checkNoChangeRun, // Check errors - should not report any errors - update buildInfo
checkNoChangeRun, // Should be no op
incremental || buildType === "-b" ?
noChangeRunWithCheckPendingDiscrepancy : // Should be no op
noChangeRun, // Should be no op
noCheckError,
noChangeRun, // Should be no op
checkNoChangeRun, // Should check errors and update buildInfo
noCheckFixError, // Fix error with noCheck
checkNoChangeRun, // Should check errors and update buildInfo
{
caption: "Add file with error",
edit: fs => fs.writeFileSync("/src/c.ts", `export const c: number = "hello";`),
commandLineArgs,
},
noCheckError,
noCheckFixError,
checkNoChangeRun,
incremental || buildType === "-b" ?
noChangeRunWithCheckPendingDiscrepancy : // Should be no op
noChangeRun, // Should be no op
checkNoChangeRun, // Should be no op
],
baselinePrograms: true,
});
});
});
}
forEachNoCheckScenarioWorker("syntax errors", `export const a = "hello`);
forEachNoCheckScenarioWorker("semantic errors", `export const a: number = "hello";`);
forEachNoCheckScenarioWorker("dts errors", `export const a = class { private p = 10; };`);
}

View File

@@ -352,31 +352,69 @@ function verifyTscEditDiscrepancies({
}
}
}
if ((incrementalReadableBuildInfo as ReadableIncrementalBuildInfo)?.emitDiagnosticsPerFile) {
(incrementalReadableBuildInfo as ReadableIncrementalBuildInfo).emitDiagnosticsPerFile!.forEach(([actualFileOrArray]) => {
const actualFile = ts.isString(actualFileOrArray) ? actualFileOrArray : actualFileOrArray[0];
if (
// Does not have emit diagnostics in clean buildInfo
!ts.find(
(cleanReadableBuildInfo as ReadableIncrementalBuildInfo).emitDiagnosticsPerFile,
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
) &&
// Is not marked as affectedFilesPendingEmit in clean buildInfo
(!ts.find(
(cleanReadableBuildInfo as ReadableIncrementalMultiFileEmitBuildInfo).affectedFilesPendingEmit,
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
)) &&
// Program emit is not pending in clean buildInfo
!(cleanReadableBuildInfo as ReadableIncrementalBundleEmitBuildInfo).pendingEmit
) {
addBaseline(
`Incremental build contains ${actualFile} file has errors, clean build does not have errors or does not mark is as pending emit: ${outputFile}::`,
`Incremental buildInfoText:: ${incrementalBuildText}`,
`Clean buildInfoText:: ${cleanBuildText}`,
);
}
});
}
const readableIncrementalBuildInfo = incrementalReadableBuildInfo as ReadableIncrementalBuildInfo | undefined;
const readableCleanBuildInfo = cleanReadableBuildInfo as ReadableIncrementalBuildInfo | undefined;
readableIncrementalBuildInfo?.semanticDiagnosticsPerFile?.forEach((
[actualFile, notCachedORDiagnostics],
) => {
const cleanFileDiagnostics = ts.find(
readableCleanBuildInfo?.semanticDiagnosticsPerFile,
([expectedFile]) => actualFile === expectedFile,
);
// Incremental build should have same diagnostics as clean build
if (cleanFileDiagnostics && JSON.stringify(notCachedORDiagnostics) === JSON.stringify(cleanFileDiagnostics[1])) return;
// Otherwise marked as "not Cached" in clean build with errors in incremental build is ok
if (
ts.isString(cleanFileDiagnostics?.[1]) &&
!ts.isString(notCachedORDiagnostics)
) return;
addBaseline(
`Incremental build contains ${actualFile} file ${!ts.isString(notCachedORDiagnostics) ? "has" : "does not have"} semantic errors, it does not match with clean build: ${outputFile}::`,
`Incremental buildInfoText:: ${incrementalBuildText}`,
`Clean buildInfoText:: ${cleanBuildText}`,
);
});
readableCleanBuildInfo?.semanticDiagnosticsPerFile?.forEach((
[expectedFile, cleanFileDiagnostics],
) => {
if (
// if there are errors in the file
!ts.isString(cleanFileDiagnostics?.[1]) &&
// and its not already verified, its issue with the error caching
!ts.find(
readableIncrementalBuildInfo?.semanticDiagnosticsPerFile,
([actualFile]) => actualFile === expectedFile,
)
) {
addBaseline(
`Incremental build does not contain ${expectedFile} file for semantic errors, clean build has semantic errors: ${outputFile}::`,
`Incremental buildInfoText:: ${incrementalBuildText}`,
`Clean buildInfoText:: ${cleanBuildText}`,
);
}
});
readableIncrementalBuildInfo?.emitDiagnosticsPerFile?.forEach(([actualFile]) => {
if (
// Does not have emit diagnostics in clean buildInfo
!ts.find(
readableCleanBuildInfo!.emitDiagnosticsPerFile,
([expectedFile]) => actualFile === expectedFile,
) &&
// Is not marked as affectedFilesPendingEmit in clean buildInfo
(!ts.find(
(readableCleanBuildInfo as ReadableIncrementalMultiFileEmitBuildInfo).affectedFilesPendingEmit,
([expectedFileOrArray]) => actualFile === (ts.isString(expectedFileOrArray) ? expectedFileOrArray : expectedFileOrArray[0]),
)) &&
// Program emit is not pending in clean buildInfo
!(readableCleanBuildInfo as ReadableIncrementalBundleEmitBuildInfo).pendingEmit
) {
addBaseline(
`Incremental build contains ${actualFile} file has emit errors, clean build does not have errors or does not mark is as pending emit: ${outputFile}::`,
`Incremental buildInfoText:: ${incrementalBuildText}`,
`Clean buildInfoText:: ${cleanBuildText}`,
);
}
});
}
}
if (!headerAdded && discrepancyExplanation) addBaseline("*** Supplied discrepancy explanation but didnt find any difference");
@@ -462,11 +500,15 @@ function getBuildInfoForIncrementalCorrectnessCheck(text: string | undefined): {
fileIdsList: undefined,
fileInfos: sanitizedFileInfos,
// Ignore noEmit since that shouldnt be reason to emit the tsbuild info and presence of it in the buildinfo file does not matter
options: readableBuildInfo.options ? { ...readableBuildInfo.options, noEmit: undefined } : undefined,
options: readableBuildInfo.options && {
...readableBuildInfo.options,
noEmit: undefined,
},
affectedFilesPendingEmit: undefined,
pendingEmit: undefined,
emitDiagnosticsPerFile: undefined,
latestChangedDtsFile: readableBuildInfo.latestChangedDtsFile ? "FakeFileName" : undefined,
semanticDiagnosticsPerFile: undefined,
} : undefined),
size: undefined, // Size doesnt need to be equal
}),

View File

@@ -1,86 +1,5 @@
import {
CommandLineOption,
optionDeclarations,
} from "../../_namespaces/ts.js";
import { jsonToReadableText } from "../helpers.js";
import {
noChangeRun,
verifyTsc,
} from "../helpers/tsc.js";
import { loadProjectFromFiles } from "../helpers/vfs.js";
import { forEachTscScenarioWithNoCheck } from "../helpers/noCheck.js";
function verifyNoCheckFlag(variant: string) {
function verifyNoCheckWorker(subScenario: string, declAText: string, commandLineArgs: readonly string[]) {
verifyTsc({
scenario: variant,
subScenario,
fs: () =>
loadProjectFromFiles({
"/src/a.ts": getATsContent(declAText),
"/src/tsconfig.json": jsonToReadableText({
compilerOptions: { noCheck: true, emitDeclarationOnly: true, declaration: true },
}),
}),
commandLineArgs,
edits: [
noChangeRun,
{
caption: "Fix `a` error",
edit: fs => fs.writeFileSync("/src/a.ts", getATsContent(`const a = "hello"`)),
},
noChangeRun,
{
caption: "Disable noCheck",
edit: fs =>
fs.writeFileSync(
"/src/tsconfig.json",
jsonToReadableText({
compilerOptions: { emitDeclarationOnly: true, declaration: true },
}),
),
},
noChangeRun,
],
baselinePrograms: true,
});
function getATsContent(declAText: string) {
return `const err: number = "error";
${declAText}`;
}
}
function verifyNoCheck(subScenario: string, aTsContent: string) {
verifyNoCheckWorker(subScenario, aTsContent, ["--b", "/src/tsconfig.json", "-v"]);
verifyNoCheckWorker(`${subScenario} with incremental`, aTsContent, ["--b", "/src/tsconfig.json", "-v", "--incremental"]);
}
verifyNoCheck("syntax errors", `const a = "hello`);
verifyNoCheck("semantic errors", `const a: number = "hello"`);
}
describe("unittests:: tsbuild:: noCheck", () => {
// Enable the `noCheck` option on the CLI for testing purposes, to ensure it works with incremental/build
let validate: CommandLineOption["extraValidation"];
before(() => {
for (const opt of optionDeclarations) {
if (opt.name === "noCheck") {
validate = opt.extraValidation;
opt.extraValidation = () => undefined;
}
}
});
after(() => {
for (const opt of optionDeclarations) {
if (opt.name === "noCheck") {
opt.extraValidation = validate;
}
}
});
verifyNoCheckFlag("noCheck");
});
describe("unittests:: tsbuild:: noCheck:: errors", () => {
verifyNoCheckFlag("noCheck-errors");
describe("unittests:: tsbuild:: noCheck::", () => {
forEachTscScenarioWithNoCheck("-b");
});

View File

@@ -261,10 +261,6 @@ declare global {
{
caption: "Delete output for class3",
edit: fs => fs.unlinkSync("/src/projects/project1/class3.d.ts"),
discrepancyExplanation: () => [
"Ts buildinfo will be updated but will retain lib file errors from previous build and not others because they are emitted because of change which results in clearing their semantic diagnostics cache",
"But in clean build because of global diagnostics, semantic diagnostics are not queried so not cached in tsbuildinfo",
],
},
{
caption: "Create output for class3",

View File

@@ -0,0 +1,5 @@
import { forEachTscScenarioWithNoCheck } from "../helpers/noCheck.js";
describe("unittests:: tsc:: noCheck::", () => {
forEachTscScenarioWithNoCheck("-p");
});