Skip to content
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

Pick correct compilerOptions when checking if we can share emitSignatures #50910

Merged
merged 3 commits into from Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/compiler/builder.ts
Expand Up @@ -173,10 +173,16 @@ namespace ts {
const oldCompilerOptions = useOldState ? oldState!.compilerOptions : undefined;
const canCopySemanticDiagnostics = useOldState && oldState!.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile &&
!compilerOptionsAffectSemanticDiagnostics(compilerOptions, oldCompilerOptions!);
// We can only reuse emit signatures (i.e. .d.ts signatures) if the .d.ts file is unchanged,
// which will eg be depedent on change in options like declarationDir and outDir options are unchanged.
// We need to look in oldState.compilerOptions, rather than oldCompilerOptions (i.e.we need to disregard useOldState) because
// oldCompilerOptions can be undefined if there was change in say module from None to some other option
// which would make useOldState as false since we can now use reference maps that are needed to track what to emit, what to check etc
// but that option change does not affect d.ts file name so emitSignatures should still be reused.
const canCopyEmitSignatures = compilerOptions.composite &&
oldState?.emitSignatures &&
!outFilePath &&
!compilerOptionsAffectDeclarationPath(compilerOptions, oldCompilerOptions!);
!compilerOptionsAffectDeclarationPath(compilerOptions, oldState.compilerOptions);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a comment would help to explain why this one spot doesn't use oldCompilerOptions. (To be honest, I couldn't figure it out from your PR description.) It's especially disconcerting that we might be dotting into oldState when useOldState is false, so please consider explaining that as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if its more clear now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like maybe useOldState should be renamed? Maybe something like useOldStateFor...?

I'll attempt to paraphrase - please check my understanding:
We can only reuse emit signatures (i.e. .d.ts signatures) if the .d.ts file is unchanged, which will only be the case if the declarationDir and outDir options are unchanged.
We need to look in oldState.compilerOptions, rather than oldCompilerOptions (i.e. we need to disregard useOldState) because oldCompilerOptions will be undefined if the file references map indicates that the files to be emitted have changed, which isn't relevant for checking signature reuses.

Was I close?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to look in oldState.compilerOptions, rather than oldCompilerOptions (i.e. we need to disregard useOldState)

Since useOldState is determined by whether we create reference map to check for files to emit or not. So if say module option changes from Module.None to some other Module kind then oldCompilerOptions will be undefined but that shouldn't be taken into account when considering whether to reuse d.ts emit signature

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe something like useOldStateFor...?

not ideal since useOldStateFor since its everything - like semantic diagnostics, files to emit, changed files and so on except d.ts emit signature

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's much clearer - thanks! For my own edification: every use case other than signature emit should still use oldCompilerOptions, correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

if (useOldState) {
// Copy old state's changed files set
oldState!.changedFilesSet?.forEach(value => state.changedFilesSet.add(value));
Expand Down
21 changes: 21 additions & 0 deletions src/testRunner/unittests/tsc/composite.ts
Expand Up @@ -81,5 +81,26 @@ namespace ts {
}),
commandLineArgs: ["--composite", "false", "--p", "src/project", "--tsBuildInfoFile", "null"],
});

verifyTscWithEdits({
scenario: "composite",
subScenario: "converting to modules",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "const x = 10;",
"/src/project/tsconfig.json": JSON.stringify({
compilerOptions: {
module: "none",
composite: true,
},
}),
}),
commandLineArgs: ["-p", "/src/project"],
edits: [
{
subScenario: "convert to modules",
modifyFs: fs => replaceText(fs, "/src/project/tsconfig.json", "none", "es2015"),
}
]
});
});
}
126 changes: 126 additions & 0 deletions tests/baselines/reference/tsc/composite/converting-to-modules.js
@@ -0,0 +1,126 @@
Input::
//// [/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
interface ReadonlyArray<T> {}
declare const console: { log(msg: any): void; };

//// [/src/project/src/main.ts]
const x = 10;

//// [/src/project/tsconfig.json]
{"compilerOptions":{"module":"none","composite":true}}



Output::
/lib/tsc -p /src/project
exitCode:: ExitStatus.Success


//// [/src/project/src/main.d.ts]
declare const x = 10;


//// [/src/project/src/main.js]
var x = 10;


//// [/src/project/tsconfig.tsbuildinfo]
{"program":{"fileNames":["../../lib/lib.d.ts","./src/main.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"5029505981-const x = 10;","signature":"-3198459068-declare const x = 10;\r\n","affectsGlobalScope":true}],"options":{"composite":true,"module":0},"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/main.d.ts"},"version":"FakeTSVersion"}

//// [/src/project/tsconfig.tsbuildinfo.readable.baseline.txt]
{
"program": {
"fileNames": [
"../../lib/lib.d.ts",
"./src/main.ts"
],
"fileInfos": {
"../../lib/lib.d.ts": {
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"affectsGlobalScope": true
},
"./src/main.ts": {
"version": "5029505981-const x = 10;",
"signature": "-3198459068-declare const x = 10;\r\n",
"affectsGlobalScope": true
}
},
"options": {
"composite": true,
"module": 0
},
"semanticDiagnosticsPerFile": [
"../../lib/lib.d.ts",
"./src/main.ts"
],
"latestChangedDtsFile": "./src/main.d.ts"
},
"version": "FakeTSVersion",
"size": 816
}



Change:: convert to modules
Input::
//// [/src/project/tsconfig.json]
{"compilerOptions":{"module":"es2015","composite":true}}



Output::
/lib/tsc -p /src/project
exitCode:: ExitStatus.Success


//// [/src/project/src/main.js] file written with same contents
//// [/src/project/tsconfig.tsbuildinfo]
{"program":{"fileNames":["../../lib/lib.d.ts","./src/main.ts"],"fileInfos":[{"version":"3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };","affectsGlobalScope":true},{"version":"5029505981-const x = 10;","signature":"-3198459068-declare const x = 10;\r\n","affectsGlobalScope":true}],"options":{"composite":true,"module":5},"referencedMap":[],"exportedModulesMap":[],"semanticDiagnosticsPerFile":[1,2],"latestChangedDtsFile":"./src/main.d.ts"},"version":"FakeTSVersion"}

//// [/src/project/tsconfig.tsbuildinfo.readable.baseline.txt]
{
"program": {
"fileNames": [
"../../lib/lib.d.ts",
"./src/main.ts"
],
"fileInfos": {
"../../lib/lib.d.ts": {
"version": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"signature": "3858781397-/// <reference no-default-lib=\"true\"/>\ninterface Boolean {}\ninterface Function {}\ninterface CallableFunction {}\ninterface NewableFunction {}\ninterface IArguments {}\ninterface Number { toExponential: any; }\ninterface Object {}\ninterface RegExp {}\ninterface String { charAt: any; }\ninterface Array<T> { length: number; [n: number]: T; }\ninterface ReadonlyArray<T> {}\ndeclare const console: { log(msg: any): void; };",
"affectsGlobalScope": true
},
"./src/main.ts": {
"version": "5029505981-const x = 10;",
"signature": "-3198459068-declare const x = 10;\r\n",
"affectsGlobalScope": true
}
},
"options": {
"composite": true,
"module": 5
},
"referencedMap": {},
"exportedModulesMap": {},
"semanticDiagnosticsPerFile": [
"../../lib/lib.d.ts",
"./src/main.ts"
],
"latestChangedDtsFile": "./src/main.d.ts"
},
"version": "FakeTSVersion",
"size": 859
}