-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow --noCheck
to be commandLine option
#58839
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this exactly derived from the options? Like, isn't this just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No its not same as options.noCheck since it takes into account run with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, it just seems like that's the same check we do for differing diagnostics-affecting options anyway? Like this is kinda redundant? (Though technically isn't since it's not using that mechanism with this PR.) You run once without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That’s because we don’t want to cached diagnostics that happened as part of checking when running with no check.. This helps with keeping the cache .. if you see the diff of second commit you should see that we are now able to retain diagnostics of unchanged files we do exactly same thing as part of pending emit so we don’t have to emit already emitted files There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I get what we're doing here that's special - we're trying to reuse the That feels kinda weird, since it's basically like we're ignoring the Personally, I assumed you'd want separate I guess the TL;DR is:
reuses diagnostics from the first
instead, since that'll work even if you spawn the differently-option'd There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do care about that and there is buildInfo fidelity built into it. Eg currenly we do support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are not doing this for parallel builds but subsequent builds. Eg imagine running tsc -b --noCheck -w while debugging and once done last running There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it's fine to have this support, I'm just not sure it's gonna help many people in ways that aren't gonna lead them to a pit of failure. Like you see the sequential builds with different options smartly don't trigger rebuilds, so then naturally if you're a |
||
} | ||
|
||
/** @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, | ||
}; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; };`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it funky that a
noCheck
with-b
fetches different diagnostics than anoCheck
normally does? This is going to skip all the program construction errors thatnoCheck
would normally still report, no?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. If typechecking is skipped the semantic and program diagnostics are skipped for that file too.. (Look at getBindAndCheckDiagnostics as well as getProgramDiagnostics in program)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, so this early bail here just skips the per-file iteration that'll internally bail on every individual file anyway; got it. It's not really clear that that's guaranteed to be the case.