Crash in incremental due to inconsistent casing
Tracking the crash mentioned in #1535.
Stack trace
panic: ManyToManySet.Set: key already exists: c:/users/gabrielaa/code/.../removable.d.ts
goroutine 1 [running]:
github.com/microsoft/typescript-go/internal/collections.(*ManyToManySet[...]).Set(0xa65e80, {0xc002fc0700, 0x5d}, 0xc000c07800)
C:/Users/gabrielaa/code/typescript-go/internal/collections/manytomanyset.go:31 +0x33a
github.com/microsoft/typescript-go/internal/incremental.newSnapshotForProgram(0xc0000036c0, 0x0, 0x0)
C:/Users/gabrielaa/code/typescript-go/internal/incremental/snapshot.go:373 +0x572
github.com/microsoft/typescript-go/internal/incremental.NewProgram(0xc0000036c0, 0x0, 0x0)
C:/Users/gabrielaa/code/typescript-go/internal/incremental/program.go:36 +0x25
github.com/microsoft/typescript-go/internal/execute.performIncrementalCompilation({0xa54370, 0xc0000ceba0}, 0xc0000e61e0, 0xc0001f4e00, 0xc000037d10, 0x36d6ac, 0x0)
C:/Users/gabrielaa/code/typescript-go/internal/execute/tsc.go:247 +0x377
github.com/microsoft/typescript-go/internal/execute.tscCompilation({0xa54370, 0xc0000ceba0}, 0xc0000e60f0, 0x0)
C:/Users/gabrielaa/code/typescript-go/internal/execute/tsc.go:194 +0xb53
github.com/microsoft/typescript-go/internal/execute.CommandLine({0xa54370, 0xc0000ceba0}, {0xc00008a150, 0x2, 0x3}, 0x0)
C:/Users/gabrielaa/code/typescript-go/internal/execute/tsc.go:63 +0x17e
main.runMain()
C:/Users/gabrielaa/code/typescript-go/cmd/tsgo/main.go:23 +0x109
main.main()
C:/Users/gabrielaa/code/typescript-go/cmd/tsgo/main.go:10 +0x13
Steps to reproduce
- From #1535, you can repro it with the following test in
tsc_test.go:
{
subScenario: "Compile incremental with case insensitive file names",
commandLineArgs: []string{"-p", "."},
files: FileMap{
"/home/project/tsconfig.json": stringtestutil.Dedent(`
{
"compilerOptions": {
"incremental": true
},
}`),
"/home/project/src/index.ts": stringtestutil.Dedent(`
import type { Foo1 } from 'lib1';
import type { Foo2 } from 'lib2';
export const foo1: Foo1 = { foo: "a" };
export const foo2: Foo2 = { foo: "b" };`),
"/home/node_modules/lib1/index.d.ts": stringtestutil.Dedent(`
import type { Foo } from 'someLib';
export type { Foo as Foo1 };`),
"/home/node_modules/lib1/package.json": stringtestutil.Dedent(`
{
"name": "lib1"
}`),
"/home/node_modules/lib2/index.d.ts": stringtestutil.Dedent(`
import type { Foo } from 'somelib';
export type { Foo as Foo2 };
export declare const foo2: Foo;`),
"/home/node_modules/lib2/package.json": stringtestutil.Dedent(`
{
"name": "lib2"
}
`),
"/home/node_modules/someLib/index.d.ts": stringtestutil.Dedent(`
import type { Str } from 'otherLib';
export type Foo = { foo: Str; };`),
"/home/node_modules/someLib/package.json": stringtestutil.Dedent(`
{
"name": "somelib"
}`),
"/home/node_modules/otherLib/index.d.ts": stringtestutil.Dedent(`
export type Str = string;`),
"/home/node_modules/otherLib/package.json": stringtestutil.Dedent(`
{
"name": "otherlib"
}`),
},
cwd: "/home/project",
ignoreCase: true,
},
See #1535 for discussion, but the crash happens because right now it's possible for the same file to be included more than once in a program, when the file system is case insensitive and the file is included each time with a name that differs in casing from the others.
Having the same file multiple times in the program then breaks the assumption of the snapshot code that each file (i.e. each Path) will only show up once in the program.
Note that you can get into this situation even without the compiler producing an inconsistent casing error. In the original scenario, I caused the crash by invoking tsgo with a path to the tsconfig which used a different casing than the realpath. This resulted in files added to the program by using the config path having one casing, and files added via module resolution (which invokes realpath) having a different casing.