Skip to content

Commit f76727d

Browse files
authored
Add noCheck API option (#57934)
1 parent 95d23ca commit f76727d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3674
-7
lines changed

src/compiler/checker.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8797,6 +8797,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
87978797
}
87988798

87998799
function serializeSymbol(symbol: Symbol, isPrivate: boolean, propertyAsAlias: boolean): void {
8800+
void getPropertiesOfType(getTypeOfSymbol(symbol)); // resolve symbol's type and properties, which should trigger any required merges
88008801
// cache visited list based on merged symbol, since we want to use the unmerged top-level symbol, but
88018802
// still skip reserializing it if we encounter the merged product later on
88028803
const visitedSym = getMergedSymbol(symbol);
@@ -48893,6 +48894,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4889348894
if (!sym) {
4889448895
return !node.locals ? [] : nodeBuilder.symbolTableToDeclarationStatements(node.locals, node, flags, tracker);
4889548896
}
48897+
resolveExternalModuleSymbol(sym); // ensures cjs export assignment is setup
4889648898
return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker);
4889748899
},
4889848900
isImportRequiredByAugmentation,

src/compiler/commandLineParser.ts

+14
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,20 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
772772
defaultValueDescription: false,
773773
description: Diagnostics.Disable_emitting_comments,
774774
},
775+
{
776+
name: "noCheck",
777+
type: "boolean",
778+
showInSimplifiedHelpView: false,
779+
category: Diagnostics.Compiler_Diagnostics,
780+
description: Diagnostics.Disable_full_type_checking_only_critical_parse_and_emit_errors_will_be_reported,
781+
transpileOptionValue: undefined,
782+
defaultValueDescription: false,
783+
affectsSemanticDiagnostics: true,
784+
affectsBuildInfo: true,
785+
extraValidation() {
786+
return [Diagnostics.Unknown_compiler_option_0, "noCheck"];
787+
},
788+
},
775789
{
776790
name: "noEmit",
777791
type: "boolean",

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -6388,6 +6388,10 @@
63886388
"category": "Message",
63896389
"code": 6804
63906390
},
6391+
"Disable full type checking (only critical parse and emit errors will be reported).": {
6392+
"category": "Message",
6393+
"code": 6805
6394+
},
63916395

63926396
"one of:": {
63936397
"category": "Message",

src/compiler/emitter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,8 @@ export function emitFiles(resolver: EmitResolver, host: EmitHost, targetSourceFi
848848
const filesForEmit = forceDtsEmit ? sourceFiles : filter(sourceFiles, isSourceFileNotJson);
849849
// Setup and perform the transformation to retrieve declarations from the input files
850850
const inputListOrBundle = compilerOptions.outFile ? [factory.createBundle(filesForEmit)] : filesForEmit;
851-
if (emitOnly && !getEmitDeclarations(compilerOptions)) {
852-
// Checker wont collect the linked aliases since thats only done when declaration is enabled.
851+
if ((emitOnly && !getEmitDeclarations(compilerOptions)) || compilerOptions.noCheck) {
852+
// Checker wont collect the linked aliases since thats only done when declaration is enabled and checking is performed.
853853
// Do that here when emitting only dts files
854854
filesForEmit.forEach(collectLinkedAliases);
855855
}

src/compiler/program.ts

+9
Original file line numberDiff line numberDiff line change
@@ -4425,6 +4425,15 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
44254425
}
44264426
}
44274427

4428+
if (options.noCheck) {
4429+
if (options.noEmit) {
4430+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noCheck", "noEmit");
4431+
}
4432+
if (!options.emitDeclarationOnly) {
4433+
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "noCheck", "emitDeclarationOnly");
4434+
}
4435+
}
4436+
44284437
if (
44294438
options.emitDecoratorMetadata &&
44304439
!options.experimentalDecorators

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7270,6 +7270,7 @@ export interface CompilerOptions {
72707270
moduleDetection?: ModuleDetectionKind;
72717271
newLine?: NewLineKind;
72727272
noEmit?: boolean;
7273+
/** @internal */ noCheck?: boolean;
72737274
/** @internal */ noEmitForJsFiles?: boolean;
72747275
noEmitHelpers?: boolean;
72757276
noEmitOnError?: boolean;

src/compiler/utilities.ts

+1
Original file line numberDiff line numberDiff line change
@@ -9967,6 +9967,7 @@ export function skipTypeChecking(sourceFile: SourceFile, options: CompilerOption
99679967
// '/// <reference no-default-lib="true"/>' directive.
99689968
return (options.skipLibCheck && sourceFile.isDeclarationFile ||
99699969
options.skipDefaultLibCheck && sourceFile.hasNoDefaultLib) ||
9970+
options.noCheck ||
99709971
host.isSourceOfProjectReferenceRedirect(sourceFile.fileName);
99719972
}
99729973

src/harness/harnessIO.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ export namespace Compiler {
303303
{ name: "noTypesAndSymbols", type: "boolean", defaultValueDescription: false },
304304
// Emitted js baseline will print full paths for every output file
305305
{ name: "fullEmitPaths", type: "boolean", defaultValueDescription: false },
306+
{ name: "noCheck", type: "boolean", defaultValueDescription: false },
306307
{ name: "reportDiagnostics", type: "boolean", defaultValueDescription: false }, // used to enable error collection in `transpile` baselines
307308
];
308309

@@ -371,6 +372,8 @@ export namespace Compiler {
371372
fileOptions?: any;
372373
}
373374

375+
export type CompileFilesResult = compiler.CompilationResult & { repeat(newOptions: TestCaseParser.CompilerSettings): CompileFilesResult; };
376+
374377
export function compileFiles(
375378
inputFiles: TestFile[],
376379
otherFiles: TestFile[],
@@ -379,7 +382,8 @@ export namespace Compiler {
379382
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
380383
currentDirectory: string | undefined,
381384
symlinks?: vfs.FileSet,
382-
): compiler.CompilationResult {
385+
): CompileFilesResult {
386+
const originalCurrentDirectory = currentDirectory;
383387
const options: ts.CompilerOptions & HarnessOptions = compilerOptions ? ts.cloneCompilerOptions(compilerOptions) : { noResolve: false };
384388
options.newLine = options.newLine || ts.NewLineKind.CarriageReturnLineFeed;
385389
options.noErrorTruncation = true;
@@ -428,7 +432,8 @@ export namespace Compiler {
428432
const host = new fakes.CompilerHost(fs, options);
429433
const result = compiler.compileFiles(host, programFileNames, options, typeScriptVersion);
430434
result.symlinks = symlinks;
431-
return result;
435+
(result as CompileFilesResult).repeat = newOptions => compileFiles(inputFiles, otherFiles, { ...harnessSettings, ...newOptions }, compilerOptions, originalCurrentDirectory, symlinks);
436+
return result as CompileFilesResult;
432437
}
433438

434439
export interface DeclarationCompilationContext {
@@ -944,7 +949,7 @@ export namespace Compiler {
944949
return "\n//// https://sokra.github.io/source-map-visualization" + hash + "\n";
945950
}
946951

947-
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: compiler.CompilationResult, tsConfigFiles: readonly TestFile[], toBeCompiled: readonly TestFile[], otherFiles: readonly TestFile[], harnessSettings: TestCaseParser.CompilerSettings) {
952+
export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompileFilesResult, tsConfigFiles: readonly TestFile[], toBeCompiled: readonly TestFile[], otherFiles: readonly TestFile[], harnessSettings: TestCaseParser.CompilerSettings) {
948953
if (!options.noEmit && !options.emitDeclarationOnly && result.js.size === 0 && result.diagnostics.length === 0) {
949954
throw new Error("Expected at least one js file to be emitted or at least one error to be created.");
950955
}
@@ -996,9 +1001,33 @@ export namespace Compiler {
9961001
jsCode += "\r\n\r\n";
9971002
jsCode += getErrorBaseline(tsConfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.diagnostics);
9981003
}
1004+
else if (!options.noCheck && !options.noEmit && (options.composite || options.declaration || options.emitDeclarationOnly)) {
1005+
const withoutChecking = result.repeat({ noCheck: "true", emitDeclarationOnly: "true" });
1006+
compareResultFileSets(withoutChecking.dts, result.dts);
1007+
}
9991008

10001009
// eslint-disable-next-line no-restricted-syntax
10011010
Baseline.runBaseline(baselinePath.replace(/\.tsx?/, ts.Extension.Js), jsCode.length > 0 ? tsCode + "\r\n\r\n" + jsCode : null);
1011+
1012+
function compareResultFileSets(a: ReadonlyMap<string, documents.TextDocument>, b: ReadonlyMap<string, documents.TextDocument>) {
1013+
a.forEach((doc, key) => {
1014+
const original = b.get(key);
1015+
if (!original) {
1016+
jsCode += `\r\n\r\n!!!! File ${Utils.removeTestPathPrefixes(doc.file)} missing from original emit, but present in noCheck emit\r\n`;
1017+
jsCode += fileOutput(doc, harnessSettings);
1018+
}
1019+
else if (original.text !== doc.text) {
1020+
jsCode += `\r\n\r\n!!!! File ${Utils.removeTestPathPrefixes(doc.file)} differs from original emit in noCheck emit\r\n`;
1021+
const Diff = require("diff");
1022+
const expected = original.text;
1023+
const actual = doc.text;
1024+
const patch = Diff.createTwoFilesPatch("Expected", "Actual", expected, actual, "The full check baseline", "with noCheck set");
1025+
const fileName = harnessSettings.fullEmitPaths ? Utils.removeTestPathPrefixes(doc.file) : ts.getBaseFileName(doc.file);
1026+
jsCode += "//// [" + fileName + "]\r\n";
1027+
jsCode += patch;
1028+
}
1029+
});
1030+
}
10021031
}
10031032

10041033
function fileOutput(file: documents.TextDocument, harnessSettings: TestCaseParser.CompilerSettings): string {

src/services/transpile.ts

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export function transpileModule(input: string, transpileOptions: TranspileOption
7575
* - noResolve = true
7676
* - declaration = true
7777
* - emitDeclarationOnly = true
78+
* - noCheck = true
7879
* Note that this declaration file may differ from one produced by a full program typecheck,
7980
* in that only types in the single input file are available to be used in the generated declarations.
8081
*/
@@ -141,6 +142,7 @@ function transpileWorker(input: string, transpileOptions: TranspileOptions, decl
141142
options.declaration = true;
142143
options.emitDeclarationOnly = true;
143144
options.isolatedDeclarations = true;
145+
options.noCheck = true;
144146
}
145147
else {
146148
options.declaration = false;

src/testRunner/compilerRunner.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as compiler from "./_namespaces/compiler";
21
import {
32
Baseline,
43
Compiler,
@@ -171,7 +170,7 @@ class CompilerTest {
171170
private configuredName: string;
172171
private harnessSettings: TestCaseParser.CompilerSettings;
173172
private hasNonDtsFiles: boolean;
174-
private result: compiler.CompilationResult;
173+
private result: Compiler.CompileFilesResult;
175174
private options: ts.CompilerOptions;
176175
private tsConfigFiles: Compiler.TestFile[];
177176
// equivalent to the files that will be passed on the command line

src/testRunner/tests.ts

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import "./unittests/tsbuild/lateBoundSymbol";
8686
import "./unittests/tsbuild/libraryResolution";
8787
import "./unittests/tsbuild/moduleResolution";
8888
import "./unittests/tsbuild/moduleSpecifiers";
89+
import "./unittests/tsbuild/noCheck";
8990
import "./unittests/tsbuild/noEmit";
9091
import "./unittests/tsbuild/noEmitOnError";
9192
import "./unittests/tsbuild/outFile";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
CommandLineOption,
3+
optionDeclarations,
4+
} from "../../_namespaces/ts";
5+
import { jsonToReadableText } from "../helpers";
6+
import {
7+
noChangeRun,
8+
verifyTsc,
9+
} from "../helpers/tsc";
10+
import { loadProjectFromFiles } from "../helpers/vfs";
11+
12+
function verifyNoCheckFlag(variant: string) {
13+
function verifyNoCheckWorker(subScenario: string, declAText: string, commandLineArgs: readonly string[]) {
14+
verifyTsc({
15+
scenario: variant,
16+
subScenario,
17+
fs: () =>
18+
loadProjectFromFiles({
19+
"/src/a.ts": getATsContent(declAText),
20+
"/src/tsconfig.json": jsonToReadableText({
21+
compilerOptions: { noCheck: true, emitDeclarationOnly: true, declaration: true },
22+
}),
23+
}),
24+
commandLineArgs,
25+
edits: [
26+
noChangeRun,
27+
{
28+
caption: "Fix `a` error",
29+
edit: fs => fs.writeFileSync("/src/a.ts", getATsContent(`const a = "hello"`)),
30+
},
31+
noChangeRun,
32+
{
33+
caption: "Disable noCheck",
34+
edit: fs =>
35+
fs.writeFileSync(
36+
"/src/tsconfig.json",
37+
jsonToReadableText({
38+
compilerOptions: { emitDeclarationOnly: true, declaration: true },
39+
}),
40+
),
41+
},
42+
noChangeRun,
43+
],
44+
baselinePrograms: true,
45+
});
46+
47+
function getATsContent(declAText: string) {
48+
return `const err: number = "error";
49+
${declAText}`;
50+
}
51+
}
52+
53+
function verifyNoCheck(subScenario: string, aTsContent: string) {
54+
verifyNoCheckWorker(subScenario, aTsContent, ["--b", "/src/tsconfig.json", "-v"]);
55+
verifyNoCheckWorker(`${subScenario} with incremental`, aTsContent, ["--b", "/src/tsconfig.json", "-v", "--incremental"]);
56+
}
57+
58+
verifyNoCheck("syntax errors", `const a = "hello`);
59+
verifyNoCheck("semantic errors", `const a: number = "hello"`);
60+
}
61+
62+
describe("unittests:: tsbuild:: noCheck", () => {
63+
// Enable the `noCheck` option on the CLI for testing purposes, to ensure it works with incremental/build
64+
let validate: CommandLineOption["extraValidation"];
65+
before(() => {
66+
for (const opt of optionDeclarations) {
67+
if (opt.name === "noCheck") {
68+
validate = opt.extraValidation;
69+
opt.extraValidation = () => undefined;
70+
}
71+
}
72+
});
73+
after(() => {
74+
for (const opt of optionDeclarations) {
75+
if (opt.name === "noCheck") {
76+
opt.extraValidation = validate;
77+
}
78+
}
79+
});
80+
81+
verifyNoCheckFlag("noCheck");
82+
});
83+
84+
describe("unittests:: tsbuild:: noCheck:: errors", () => {
85+
verifyNoCheckFlag("noCheck-errors");
86+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"compilerOptions": {}
3+
}

tests/baselines/reference/deferredLookupTypeResolution.js

+17
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,20 @@ declare function f3(x: 'a' | 'b'): {
6262
b: any;
6363
x: any;
6464
};
65+
66+
67+
!!!! File deferredLookupTypeResolution.d.ts differs from original emit in noCheck emit
68+
//// [deferredLookupTypeResolution.d.ts]
69+
===================================================================
70+
--- Expected The full check baseline
71+
+++ Actual with noCheck set
72+
@@ -15,8 +15,8 @@
73+
[P in A | B]: any;
74+
};
75+
declare function f2<A extends string>(a: A): { [P in A | "x"]: any; };
76+
declare function f3(x: 'a' | 'b'): {
77+
+ x: any;
78+
a: any;
79+
b: any;
80+
- x: any;
81+
};

tests/baselines/reference/indexSignatures1.js

+18
Original file line numberDiff line numberDiff line change
@@ -680,3 +680,21 @@ type Rec1 = {
680680
type Rec2 = Record<Id, number>;
681681
type K1 = keyof Rec1;
682682
type K2 = keyof Rec2;
683+
684+
685+
!!!! File indexSignatures1.d.ts differs from original emit in noCheck emit
686+
//// [indexSignatures1.d.ts]
687+
===================================================================
688+
--- Expected The full check baseline
689+
+++ Actual with noCheck set
690+
@@ -118,9 +118,9 @@
691+
[x: symbol]: 4 | 5;
692+
[sym]: 4;
693+
};
694+
declare const obj13: {
695+
- [x: string]: 0 | 2 | 1 | 3;
696+
+ [x: string]: 0 | 1 | 2 | 3;
697+
[x: number]: 2 | 3;
698+
[x: symbol]: 4 | 5;
699+
x: 0;
700+
1: 2;

tests/baselines/reference/jsDeclarationsCrossfileMerge.js

+6
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ module.exports.memberName = "thing";
2727
declare const _exports: typeof m.default;
2828
export = _exports;
2929
import m = require("./exporter");
30+
31+
32+
!!!! File out/exporter.d.ts missing from original emit, but present in noCheck emit
33+
//// [exporter.d.ts]
34+
export default validate;
35+
declare function validate(): void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////
2+
3+
//// [noCheckDoesNotReportError.ts]
4+
export const a: number = "not ok";
5+
6+
7+
8+
9+
//// [noCheckDoesNotReportError.d.ts]
10+
export declare const a: number;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////
2+
3+
=== noCheckDoesNotReportError.ts ===
4+
export const a: number = "not ok";
5+
>a : Symbol(a, Decl(noCheckDoesNotReportError.ts, 0, 12))
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [tests/cases/compiler/noCheckDoesNotReportError.ts] ////
2+
3+
=== noCheckDoesNotReportError.ts ===
4+
export const a: number = "not ok";
5+
>a : number
6+
> : ^^^^^^
7+
>"not ok" : "not ok"
8+
> : ^^^^^^^^
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error TS5053: Option 'emitDeclarationOnly' cannot be specified with option 'noEmit'.
2+
error TS5053: Option 'noCheck' cannot be specified with option 'noEmit'.
3+
4+
5+
!!! error TS5053: Option 'emitDeclarationOnly' cannot be specified with option 'noEmit'.
6+
!!! error TS5053: Option 'noCheck' cannot be specified with option 'noEmit'.
7+
==== noCheckNoEmit.ts (0 errors) ====
8+
export const a: number = "not ok";
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//// [tests/cases/compiler/noCheckNoEmit.ts] ////
2+
3+
=== noCheckNoEmit.ts ===
4+
export const a: number = "not ok";
5+
>a : Symbol(a, Decl(noCheckNoEmit.ts, 0, 12))
6+

0 commit comments

Comments
 (0)